Skip to content

Commit 981bf05

Browse files
committed
Add Flutter widget preview documentation for get_it
- Create comprehensive flutter_previews.md guide - Add 4 compilable code sample files demonstrating both approaches - Update navigation to include Flutter Previews page - Document real vs mock services, async initialization, and best practices
1 parent 80d804f commit 981bf05

6 files changed

Lines changed: 579 additions & 0 deletions

File tree

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter/widget_previews.dart';
3+
import 'package:get_it/get_it.dart';
4+
5+
final getIt = GetIt.instance;
6+
7+
// Mock services with mutable state for different scenarios
8+
class MockAuthService {
9+
bool isAuthenticated = false;
10+
}
11+
12+
class MockUserService {
13+
String currentUser = '';
14+
}
15+
16+
class MockApiClient {
17+
bool shouldFail = false;
18+
19+
Future<String> fetchData() async {
20+
if (shouldFail) throw Exception('API Error');
21+
return 'Mock data';
22+
}
23+
}
24+
25+
// GetItPreviewWrapper (normally in a separate file)
26+
class GetItPreviewWrapper extends StatefulWidget {
27+
const GetItPreviewWrapper({
28+
super.key,
29+
required this.init,
30+
required this.child,
31+
});
32+
33+
final Widget child;
34+
final void Function(GetIt getIt) init;
35+
36+
@override
37+
State<GetItPreviewWrapper> createState() => _GetItPreviewWrapperState();
38+
}
39+
40+
class _GetItPreviewWrapperState extends State<GetItPreviewWrapper> {
41+
@override
42+
void initState() {
43+
super.initState();
44+
widget.init(GetIt.instance);
45+
}
46+
47+
@override
48+
void dispose() {
49+
GetIt.instance.reset();
50+
super.dispose();
51+
}
52+
53+
@override
54+
Widget build(BuildContext context) {
55+
return widget.child;
56+
}
57+
}
58+
59+
// #region example
60+
// Create different wrappers for different scenarios
61+
62+
// Logged in user scenario
63+
Widget loggedInWrapper(Widget child) {
64+
return GetItPreviewWrapper(
65+
init: (getIt) {
66+
getIt.registerSingleton<MockAuthService>(
67+
MockAuthService()..isAuthenticated = true,
68+
);
69+
getIt.registerSingleton<MockUserService>(
70+
MockUserService()..currentUser = 'John Doe',
71+
);
72+
},
73+
child: child,
74+
);
75+
}
76+
77+
// Logged out user scenario
78+
Widget loggedOutWrapper(Widget child) {
79+
return GetItPreviewWrapper(
80+
init: (getIt) {
81+
getIt.registerSingleton<MockAuthService>(
82+
MockAuthService()..isAuthenticated = false,
83+
);
84+
},
85+
child: child,
86+
);
87+
}
88+
89+
// Error state scenario
90+
Widget errorStateWrapper(Widget child) {
91+
return GetItPreviewWrapper(
92+
init: (getIt) {
93+
getIt.registerSingleton<MockApiClient>(
94+
MockApiClient()..shouldFail = true,
95+
);
96+
},
97+
child: child,
98+
);
99+
}
100+
101+
// Use different wrappers to preview different states
102+
@Preview(name: 'Login Button - Logged In', wrapper: loggedInWrapper)
103+
Widget loginButtonLoggedIn() => const LoginButtonWidget();
104+
105+
@Preview(name: 'Login Button - Logged Out', wrapper: loggedOutWrapper)
106+
Widget loginButtonLoggedOut() => const LoginButtonWidget();
107+
108+
@Preview(name: 'Dashboard - Error State', wrapper: errorStateWrapper)
109+
Widget dashboardError() => const DashboardWidget();
110+
// #endregion example
111+
112+
class LoginButtonWidget extends StatelessWidget {
113+
const LoginButtonWidget({super.key});
114+
115+
@override
116+
Widget build(BuildContext context) {
117+
final auth = getIt<MockAuthService>();
118+
return ElevatedButton(
119+
onPressed: () {},
120+
child: Text(auth.isAuthenticated ? 'Logout' : 'Login'),
121+
);
122+
}
123+
}
124+
125+
class DashboardWidget extends StatelessWidget {
126+
const DashboardWidget({super.key});
127+
128+
@override
129+
Widget build(BuildContext context) {
130+
return const Center(child: Text('Dashboard'));
131+
}
132+
}
133+
134+
void main() {
135+
// Test code would go here
136+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter/widget_previews.dart';
3+
import 'package:get_it/get_it.dart';
4+
5+
final getIt = GetIt.instance;
6+
7+
// Simple mock services for preview
8+
class MockUserService {
9+
String get currentUser => 'John Doe';
10+
}
11+
12+
class MockApiClient {
13+
Future<String> fetchData() async => 'Mock data';
14+
}
15+
16+
// #region example
17+
@Preview()
18+
Widget userProfilePreview() {
19+
// The preview function may be called multiple times during hot reload,
20+
// so guard against double registration by checking the last service
21+
if (!getIt.isRegistered<MockApiClient>()) {
22+
getIt.registerSingleton<MockUserService>(MockUserService());
23+
getIt.registerLazySingleton<MockApiClient>(() => MockApiClient());
24+
}
25+
26+
return const MaterialApp(
27+
home: Scaffold(
28+
body: UserProfileWidget(),
29+
),
30+
);
31+
}
32+
// #endregion example
33+
34+
class UserProfileWidget extends StatelessWidget {
35+
const UserProfileWidget({super.key});
36+
37+
@override
38+
Widget build(BuildContext context) {
39+
final userService = getIt<MockUserService>();
40+
return Center(
41+
child: Text('User: ${userService.currentUser}'),
42+
);
43+
}
44+
}
45+
46+
void main() {
47+
// Test code would go here
48+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:get_it/get_it.dart';
3+
4+
// #region example
5+
/// A wrapper widget for Flutter widget previews that initializes GetIt.
6+
///
7+
/// This widget handles GetIt setup in initState and cleanup via reset()
8+
/// in dispose, making it perfect for preview scenarios where widgets are
9+
/// rendered in isolation.
10+
class GetItPreviewWrapper extends StatefulWidget {
11+
const GetItPreviewWrapper({
12+
super.key,
13+
required this.init,
14+
required this.child,
15+
});
16+
17+
/// The child widget to render after GetIt is initialized
18+
final Widget child;
19+
20+
/// Initialization function that registers dependencies in GetIt
21+
final void Function(GetIt getIt) init;
22+
23+
@override
24+
State<GetItPreviewWrapper> createState() => _GetItPreviewWrapperState();
25+
}
26+
27+
class _GetItPreviewWrapperState extends State<GetItPreviewWrapper> {
28+
@override
29+
void initState() {
30+
super.initState();
31+
// Initialize GetIt with preview dependencies
32+
widget.init(GetIt.instance);
33+
}
34+
35+
@override
36+
void dispose() {
37+
// Clean up all GetIt registrations when preview is disposed
38+
GetIt.instance.reset();
39+
super.dispose();
40+
}
41+
42+
@override
43+
Widget build(BuildContext context) {
44+
return widget.child;
45+
}
46+
}
47+
// #endregion example
48+
49+
void main() {
50+
// This file is for documentation only
51+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter/widget_previews.dart';
3+
import 'package:get_it/get_it.dart';
4+
5+
final getIt = GetIt.instance;
6+
7+
// Simple mock services for preview
8+
class MockUserService {
9+
String get currentUser => 'John Doe';
10+
}
11+
12+
class MockApiClient {
13+
Future<String> fetchData() async => 'Mock data';
14+
}
15+
16+
class MockAuthService {
17+
bool get isAuthenticated => true;
18+
}
19+
20+
// GetItPreviewWrapper definition (normally in a separate file)
21+
class GetItPreviewWrapper extends StatefulWidget {
22+
const GetItPreviewWrapper({
23+
super.key,
24+
required this.init,
25+
required this.child,
26+
});
27+
28+
final Widget child;
29+
final void Function(GetIt getIt) init;
30+
31+
@override
32+
State<GetItPreviewWrapper> createState() => _GetItPreviewWrapperState();
33+
}
34+
35+
class _GetItPreviewWrapperState extends State<GetItPreviewWrapper> {
36+
@override
37+
void initState() {
38+
super.initState();
39+
widget.init(GetIt.instance);
40+
}
41+
42+
@override
43+
void dispose() {
44+
GetIt.instance.reset();
45+
super.dispose();
46+
}
47+
48+
@override
49+
Widget build(BuildContext context) {
50+
return widget.child;
51+
}
52+
}
53+
54+
// #region example
55+
// Top-level wrapper function for @Preview annotation
56+
Widget myPreviewWrapper(Widget child) {
57+
return GetItPreviewWrapper(
58+
init: (getIt) {
59+
// Register all preview dependencies here
60+
getIt.registerLazySingleton<MockApiClient>(() => MockApiClient());
61+
getIt.registerSingleton<MockUserService>(MockUserService());
62+
getIt.registerFactory<MockAuthService>(() => MockAuthService());
63+
},
64+
child: child,
65+
);
66+
}
67+
68+
// Use the wrapper in your preview
69+
@Preview(name: 'Dashboard Widget', wrapper: myPreviewWrapper)
70+
Widget dashboardPreview() => const MaterialApp(
71+
home: Scaffold(
72+
body: DashboardWidget(),
73+
),
74+
);
75+
// #endregion example
76+
77+
class DashboardWidget extends StatelessWidget {
78+
const DashboardWidget({super.key});
79+
80+
@override
81+
Widget build(BuildContext context) {
82+
final userService = getIt<MockUserService>();
83+
return Center(
84+
child: Text('Dashboard for ${userService.currentUser}'),
85+
);
86+
}
87+
}
88+
89+
void main() {
90+
// Test code would go here
91+
}

‎docs/.vitepress/config.mts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export default defineConfig({
5757
{ text: 'Multiple registrations', link: '/documentation/get_it/multiple_registrations.md' },
5858
{ text: 'Advanced', link: '/documentation/get_it/advanced.md' },
5959
{ text: 'Testing', link: '/documentation/get_it/testing.md' },
60+
{ text: 'Flutter Previews', link: '/documentation/get_it/flutter_previews.md' },
6061
{ text: 'FAQ', link: '/documentation/get_it/faq.md' }
6162
]
6263
},

0 commit comments

Comments
 (0)