Skip to content

Commit 8b0e54a

Browse files
jacobaraujo7claude
andcommitted
docs(di): cross-module resolution + async bootstrap pattern
Document that a feature's binds resolve root-owned/core deps (7.1.0), the local-shadows-core precedence, and the "await once in a Future<Module> builder" idiom for Hive/SharedPreferences so main stays thin and features take no parameters. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 22b846e commit 8b0e54a

1 file changed

Lines changed: 86 additions & 0 deletions

File tree

doc/docs/flutter_modular/dependency-injection.md

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,92 @@ with a single page, prefer page‑scoped [`provide`](./state-management.md) over
100100
bind.
101101
:::
102102

103+
## Sharing dependencies across modules
104+
105+
Put a dependency that many features need — a config, an HTTP client, a session — in
106+
a **root‑owned** (path‑less) "core" module, and depend on it **by type** anywhere.
107+
You never thread it by hand: a feature's own binds, and its page‑scoped
108+
[`provide`](./state-management.md) binds, both resolve it from the graph.
109+
110+
```dart
111+
// CORE (root-owned): the shared dependency, registered once
112+
final coreModule = createModule(
113+
register: (c) => c.addInstance<ApiConfig>(ApiConfig('https://api.example')),
114+
);
115+
116+
// FEATURE: its OWN bind depends on the core ApiConfig — supplied automatically
117+
final ordersModule = createModule(
118+
path: '/orders',
119+
register: (c) => c
120+
..add<OrdersGateway>(OrdersGateway.new) // OrdersGateway(ApiConfig) ← from core
121+
..route('/', child: (ctx, state) => const OrdersPage()),
122+
);
123+
124+
final appModule = createModule(
125+
register: (c) => c
126+
..module(coreModule) // included once at the root → visible to every feature
127+
..module(ordersModule), // takes NO parameters; resolves ApiConfig by type
128+
);
129+
```
130+
131+
You include the shared module **once at the root** — there is no need to re‑import it
132+
in each feature (simpler than Angular's per‑feature `SharedModule`). Because the binds
133+
are root‑owned, every feature sees them.
134+
135+
:::info Precedence — local shadows the core
136+
If a feature registers its **own** bind of the same type, that local bind wins; the
137+
core bind is the fallback. So a feature can override a shared default for itself
138+
without affecting the rest of the app.
139+
:::
140+
141+
:::note Requires 7.1.0+
142+
Resolving a core dependency from a feature's **module‑level** bind needs
143+
flutter_modular **7.1.0** (`auto_injector >= 2.2.0`). Page‑scoped `provide` binds
144+
resolved the core in earlier versions too.
145+
:::
146+
147+
## Async bootstrap (Hive, SharedPreferences, a DB connection)
148+
149+
`register` is **synchronous**, but some shared dependencies need an `await` to come up
150+
— opening a Hive box, reading `SharedPreferences`, connecting a database. The idiom is
151+
to do that `await` **once**, in a builder that *returns the module*, and capture the
152+
ready instances in its closure. `main` stays thin and features take no parameters:
153+
154+
```dart
155+
Future<Module> buildCoreModule() async {
156+
await Hive.initFlutter();
157+
final box = await Hive.openBox<dynamic>('app'); // awaited once, here
158+
return createModule(
159+
register: (c) => c
160+
// the raw box stays private in the closure — expose the CONTRACT
161+
..addInstance<SettingsRepository>(HiveSettingsRepository(box)),
162+
);
163+
}
164+
165+
Future<void> main() async {
166+
WidgetsFlutterBinding.ensureInitialized();
167+
final core = await buildCoreModule(); // one await, one instance
168+
runApp(ModularApp(module: buildAppModule(core), child: const AppRoot()));
169+
}
170+
171+
Module buildAppModule(Module core) => createModule(
172+
register: (c) => c
173+
..module(core) // shared, root-owned — features resolve it by type
174+
..module(ordersModule), // no parameters threaded in
175+
);
176+
```
177+
178+
Blocking on the bootstrap once, then registering the *ready* instances with
179+
`addInstance`, keeps the rest of the app synchronous — no loading states sprinkled
180+
through your widgets. Combined with cross‑module resolution above, `ordersModule`'s
181+
binds resolve `SettingsRepository` from the core with **zero** parameter threading.
182+
183+
:::warning Build the async module once
184+
`buildCoreModule()` returns a **new** module each call, and composition dedups by
185+
identity — so call it **once** and reference that single instance. Calling it twice
186+
would open the Hive box twice and register two distinct modules.
187+
:::
188+
103189
## Next
104190

105191
- Bind state to a page's lifecycle → [State management](./state-management.md)

0 commit comments

Comments
 (0)