๐ฅ Hot Reload โ
Modularity preserves singleton state during Flutter hot reload. Factories and lazy singletons get updated, but already-created instances survive.
What happens on hot reload โ
- Existing singleton and
registerSingletoninstances are preserved. registerFactoryandregisterLazySingletondelegates are replaced.ModuleOverrideScopeoverrides and themodule.hotReload()hook are re-applied automatically.
No need to recreate controllers or restart the app.
RegistrationStrategy โ
modularity_contracts defines RegistrationStrategy and RegistrationAwareBinder:
Strategy overview
| Strategy | Behaviour |
|---|---|
replace | Re-registration overwrites the existing entry (default) |
preserveExisting | Singletons are kept; only the factory delegate is updated |
SimpleBinder and GetItBinder (from modularity_get_it) both implement RegistrationAwareBinder. ModuleController.hotReload() automatically wraps rebinds in runWithStrategy(RegistrationStrategy.preserveExisting, ...), so manual strategy switching is not needed in normal usage.
WARNING
modularity_injectable's GetItBinder does not implement RegistrationAwareBinder. If you use the injectable integration, hot-reload strategy switching is not available through the binder directly.
TIP
If you implement a custom binder, implement RegistrationAwareBinder and respect registrationStrategy in your register* methods.
How hotReload() works โ
ModuleController.hotReload() executes the following steps:
- Checks the module is in
loadedstatus (no-op otherwise). - For
ExportableBinder: callsresetPublicScope()to allow re-registration. - Switches binder to
preserveExistingstrategy. - Runs in order:
module.binds(binder)- override re-application (if
ModuleOverrideScopewas provided) module.exports(binder)sealPublicScope()
- Invokes
module.hotReload(binder)-- your custom hook.
The hotReload hook โ
Override hotReload() in your module to run custom logic after rebinding:
class AuthModule extends Module {
@override
void binds(Binder i) {
i.registerLazySingleton<AuthRepo>(() => AuthRepoImpl());
}
@override
void hotReload(Binder i) {
// Runs after binds/exports have been refreshed.
// Use for cache invalidation, re-subscription, etc.
}
}Overrides and hot reload โ
ModuleOverrideScope describes an override tree: selfOverrides for the current module, children for imported modules.
final overrides = ModuleOverrideScope(children: {
AuthModule: ModuleOverrideScope(
selfOverrides: (binder) =>
binder.registerLazySingleton<AuthApi>(() => FakeAuthApi()),
),
});- Flutter:
ModuleScope(overrideScope: overrides, ...) - Tests:
testModule(frommodularity_test)(MyModule(), body, overrideScope: overrides)
TIP
Overrides run after binds() but before exports(), and are automatically re-applied on every hotReload() call. This means your test fakes and debug stubs survive hot reload without any extra wiring.
Manual strategy switching โ
Any RegistrationAwareBinder exposes runWithStrategy for targeted factory updates:
final binder = ModuleProvider.of(context);
if (binder is RegistrationAwareBinder) {
binder.runWithStrategy(
RegistrationStrategy.preserveExisting,
() {
binder.registerFactory<Foo>(() => Foo());
binder.registerLazySingleton<Bar>(() => Bar());
},
);
}WARNING
Use manual strategy switching only when you explicitly need rebind behaviour that preserves existing instances outside of the normal hot reload flow.
FAQ โ
Do I need to manually reset singletons? No. To restart a module from scratch, call controller.dispose() and create a new ModuleController.
Why does re-registering a public binding throw an error? The public scope is protected by sealPublicScope(). During hotReload(), resetPublicScope() is called first, so re-registration works. Manual re-export outside that context is intentionally blocked.
How do I test hot reload behaviour?dart test packages/core/test/ includes HotReloadModule tests that verify singleton preservation and factory refresh. For GetIt-specific behaviour, see packages/adapters/modularity_get_it/test/.