Skip to content

Commit 38c68a6

Browse files
committed
Add Flutter widget preview support
- Add GetItPreviewWrapper helper widget in example - Update README with Flutter previews section - Add comprehensive preview examples showing both direct and wrapper approaches - Document how to use get_it with Flutter's widget previewer
1 parent f3f3176 commit 38c68a6

File tree

3 files changed

+194
-1
lines changed

3 files changed

+194
-1
lines changed

README.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,29 @@ tearDown(() async {
141141

142142
[Read testing guide →](https://flutter-it.dev/documentation/get_it/testing)
143143

144+
### Flutter Widget Previews
145+
146+
`get_it` works seamlessly with Flutter's widget previewer. Since previews run in isolation, you need to initialize `get_it` within the preview itself:
147+
148+
```dart
149+
@Preview()
150+
Widget myPreview() {
151+
if (!getIt.isRegistered<MyService>()) {
152+
getIt.registerSingleton<MyService>(MockService());
153+
}
154+
return const MyWidget();
155+
}
156+
```
157+
158+
Or use a reusable wrapper for automatic cleanup:
159+
160+
```dart
161+
@Preview(name: 'My Widget', wrapper: myWrapper)
162+
Widget myPreview() => const MyWidget();
163+
```
164+
165+
[Read Flutter previews guide →](https://flutter-it.dev/documentation/get_it/flutter_previews)
166+
144167
## Ecosystem Integration
145168

146169
**get_it works independently** — use it standalone for dependency injection in any Dart or Flutter project.

example/lib/main.dart

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,24 @@
11
import 'package:flutter/material.dart';
2+
import 'package:flutter/widget_previews.dart';
23
import 'package:get_it/get_it.dart';
34
import 'package:get_it_example/app_model.dart';
5+
import 'package:get_it_example/preview_wrapper.dart';
46

57
// This is our global ServiceLocator
68
GetIt getIt = GetIt.instance;
79

8-
void main() {
10+
void setupLocator() {
11+
// Here you can register other dependencies if needed
912
/// I use signalReady here only to show how to use it. In 99% of the cases
1013
/// you don't need it. Just use registerSingletonAsync
1114
getIt.registerSingleton<AppModel>(
1215
AppModelImplementation(),
1316
signalsReady: true,
1417
);
18+
}
1519

20+
void main() {
21+
setupLocator();
1622
runApp(const MyApp());
1723
}
1824

@@ -105,3 +111,62 @@ class _MyHomePageState extends State<MyHomePage> {
105111
);
106112
}
107113
}
114+
115+
// ==============================================================================
116+
// Flutter Widget Preview Examples
117+
// ==============================================================================
118+
//
119+
// Flutter's widget previewer renders widgets in isolation, without running
120+
// main() or your normal app initialization. This means `get_it` won't be
121+
// initialized automatically. Below are two approaches to handle this:
122+
123+
// ------------------------------------------------------------------------------
124+
// Approach 1: Direct Registration with isRegistered() Check
125+
// ------------------------------------------------------------------------------
126+
//
127+
// Use this for simple, one-off previews where you want maximum control.
128+
// The previewer may call this function multiple times, so we check if
129+
// the service is already registered before registering it again.
130+
131+
@Preview()
132+
Widget preview() {
133+
// Guard against double registration since preview functions
134+
// can be called multiple times during hot reload
135+
if (!getIt.isRegistered<AppModel>()) {
136+
getIt.registerSingleton<AppModel>(
137+
AppModelImplementation(),
138+
signalsReady: true,
139+
);
140+
}
141+
return const MyApp();
142+
}
143+
144+
// ------------------------------------------------------------------------------
145+
// Approach 2: Wrapper Widget with Automatic Cleanup
146+
// ------------------------------------------------------------------------------
147+
//
148+
// Use this when you want automatic cleanup or need to reuse the same setup
149+
// across multiple previews. The wrapper handles initialization in initState
150+
// and cleanup via reset() in dispose.
151+
//
152+
// Uncomment the @Preview annotation to enable this preview:
153+
154+
// @Preview(name: 'With GetIt Wrapper', wrapper: wrapper)
155+
Widget previewWithWrapper() => const MyApp();
156+
157+
// Wrapper function (must be top-level or static for @Preview)
158+
Widget wrapper(Widget child) {
159+
return GetItPreviewWrapper(
160+
init: (getIt) {
161+
// Register all preview dependencies here
162+
getIt.registerSingleton<AppModel>(
163+
AppModelImplementation(),
164+
signalsReady: true,
165+
);
166+
},
167+
child: child,
168+
);
169+
}
170+
171+
// Note: GetItPreviewWrapper is defined in preview_wrapper.dart
172+
// See that file for full documentation on how it works.

example/lib/preview_wrapper.dart

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:get_it/get_it.dart';
3+
4+
/// A wrapper widget for Flutter widget previews that initializes `get_it`.
5+
///
6+
/// Flutter's widget previewer renders widgets in isolation, separate from your
7+
/// app's normal initialization flow. This means `get_it` won't be initialized
8+
/// automatically. This wrapper handles `get_it` setup and cleanup for previews.
9+
///
10+
/// ## How to Use
11+
///
12+
/// ```dart
13+
/// Widget myWrapper(Widget child) {
14+
/// return GetItPreviewWrapper(
15+
/// init: (getIt) {
16+
/// // Register your preview dependencies
17+
/// getIt.registerLazySingleton<ApiService>(() => MockApiService());
18+
/// getIt.registerLazySingleton<UserRepo>(() => MockUserRepo());
19+
/// },
20+
/// child: child,
21+
/// );
22+
/// }
23+
///
24+
/// @Preview(name: 'My Widget', wrapper: myWrapper)
25+
/// Widget myWidgetPreview() => const MyWidget();
26+
/// ```
27+
///
28+
/// ## When to Use This vs Direct Registration
29+
///
30+
/// **Use this wrapper when:**
31+
/// - You want automatic cleanup (calls `reset()` on dispose)
32+
/// - You have complex setup logic
33+
/// - You want to reuse the same setup across multiple previews
34+
///
35+
/// **Use direct registration when:**
36+
/// - You have simple, one-off previews
37+
/// - You want maximum control over initialization
38+
///
39+
/// Example of direct registration:
40+
/// ```dart
41+
/// @Preview()
42+
/// Widget preview() {
43+
/// if (!getIt.isRegistered<MyService>()) {
44+
/// getIt.registerSingleton<MyService>(MockService());
45+
/// }
46+
/// return const MyWidget();
47+
/// }
48+
/// ```
49+
///
50+
/// ## How It Works
51+
///
52+
/// 1. **initState**: Calls your `init` function to register dependencies
53+
/// 2. **build**: Returns your child widget with GetIt ready to use
54+
/// 3. **dispose**: Calls `getIt.reset()` to clean up all registrations
55+
///
56+
/// The preview environment may call your preview function multiple times,
57+
/// so the wrapper ensures proper cleanup between renders.
58+
class GetItPreviewWrapper extends StatefulWidget {
59+
const GetItPreviewWrapper({
60+
super.key,
61+
required this.init,
62+
required this.child,
63+
});
64+
65+
/// The child widget to render after `get_it` is initialized
66+
final Widget child;
67+
68+
/// Initialization function that registers dependencies in `get_it`
69+
///
70+
/// This is called once in `initState` before the widget is built.
71+
/// Register all your preview dependencies here.
72+
///
73+
/// Example:
74+
/// ```dart
75+
/// init: (getIt) {
76+
/// getIt.registerLazySingleton<ApiService>(() => MockApiService());
77+
/// getIt.registerSingleton<Config>(TestConfig());
78+
/// }
79+
/// ```
80+
final void Function(GetIt getIt) init;
81+
82+
@override
83+
State<GetItPreviewWrapper> createState() => _GetItPreviewWrapperState();
84+
}
85+
86+
class _GetItPreviewWrapperState extends State<GetItPreviewWrapper> {
87+
@override
88+
void initState() {
89+
super.initState();
90+
// Initialize get_it with preview dependencies
91+
widget.init(GetIt.instance);
92+
}
93+
94+
@override
95+
void dispose() {
96+
// Clean up all get_it registrations when preview is disposed
97+
GetIt.instance.reset();
98+
super.dispose();
99+
}
100+
101+
@override
102+
Widget build(BuildContext context) {
103+
return widget.child;
104+
}
105+
}

0 commit comments

Comments
 (0)