flutter_modular_lc 6.5.1
flutter_modular_lc: ^6.5.1 copied to clipboard
Flutter Modular with proper DI lifecycle management. Fork of flutter_modular that fixes singleton disposal issues on app restart, hot reload, and module rebuild.
Flutter Modular LC (Lifecycle) #
A fork of flutter_modular with proper Dependency Injection lifecycle management.
This package fixes critical singleton/binding disposal issues that occur during app restart, hot reload, module rebuild, and widget disposal.
🐛 The Problem #
The original flutter_modular has a DI disposal bug where old dependency bindings and singletons are NOT properly disposed when:
- The app is restarted (hot restart)
- A module is rebuilt
ModularAppwidget is disposed and recreated- Navigating away and back to a modular SDK/feature
Symptoms #
- Stale State: Old singleton instances persist, causing unexpected behavior
- Memory Leaks: Disposed widgets still hold references to old injector instances
- State Corruption: New modules reuse old instances instead of fresh ones
- SDK Integration Issues: Multiple SDKs with their own
ModularAppinstances conflict
Root Cause #
In the original flutter_modular, the injectors are declared as top-level final variables:
// ❌ PROBLEMATIC: These persist forever, even after dispose()
final _innerInjector = AutoInjector(tag: 'ModularApp', ...);
final injector = AutoInjector(tag: 'ModularCore', ...);
This means:
- Injectors are created once when the library loads
- They are never recreated even after
ModularAppis disposed disposeRecursive()clears bindings but the injector shell persists- New
ModularAppinstances reuse the same injector with potentially stale registrations
✅ The Fix #
This package introduces ModularInjectors - a lifecycle-aware injector management system:
// ✅ FIXED: Injectors are created fresh and disposed properly
class ModularInjectors {
static AutoInjector? _innerInjector;
static AutoInjector? _injector;
/// Initialize fresh injectors - called in ModularApp.initState
static void initialize() {
dispose(); // Clean up any existing injectors first
_innerInjector = AutoInjector(tag: 'ModularApp', ...);
_injector = AutoInjector(tag: 'ModularCore', ...);
}
/// Dispose all injectors - called in ModularApp.dispose
static void dispose() {
_innerInjector?.disposeRecursive();
_injector?.disposeRecursive();
_innerInjector = null;
_injector = null;
}
}
How It Works #
| Lifecycle Event | Before (Broken) | After (Fixed) |
|---|---|---|
ModularApp created |
Reuses existing global injector | Creates fresh injectors via ModularInjectors.initialize() |
ModularApp disposed |
Only clears some bindings | Fully disposes injectors and nulls references via ModularInjectors.dispose() |
| Hot restart | Old singletons persist | Fresh injectors, fresh singletons |
| Multiple SDKs | Injector conflicts | Each SDK lifecycle is isolated |
📦 Installation #
dependencies:
flutter_modular_lc: ^6.5.0
Then run:
flutter pub get
🔄 Migration from flutter_modular #
Simply replace your import:
// Before
import 'package:flutter_modular/flutter_modular.dart';
// After
import 'package:flutter_modular_lc/flutter_modular.dart';
No other code changes required! The API is 100% compatible.
🚀 Usage #
Basic Usage (Same as flutter_modular) #
import 'package:flutter_modular_lc/flutter_modular.dart';
void main() {
runApp(ModularApp(
module: AppModule(),
child: MaterialApp.router(
routerConfig: Modular.routerConfig,
),
));
}
class AppModule extends Module {
@override
void binds(Injector i) {
i.addSingleton<MyService>(MyService.new);
}
@override
void routes(RouteManager r) {
r.child('/', child: (context) => HomePage());
}
}
SDK/Feature Module Pattern #
This package is especially useful when building SDKs or feature modules that have their own ModularApp:
class MySdkWrapper extends StatefulWidget {
@override
State<MySdkWrapper> createState() => _MySdkWrapperState();
}
class _MySdkWrapperState extends State<MySdkWrapper> {
@override
void initState() {
super.initState();
// Optional: Pre-initialize if needed before build
ModularInjectors.initialize();
}
@override
Widget build(BuildContext context) {
return ModularApp(
module: MySdkModule(),
child: MaterialApp.router(
routerConfig: Modular.routerConfig,
),
);
}
}
Manual Lifecycle Control #
For advanced use cases, you can manually control injector lifecycle:
// Initialize fresh injectors
ModularInjectors.initialize();
// Check if initialized
if (ModularInjectors.isInitialized) {
// Access injector
final service = injector.get<MyService>();
}
// Dispose when done
ModularInjectors.dispose();
🔍 Verifying the Fix #
To verify singletons are properly disposed:
class MyService implements Disposable {
MyService() {
print('MyService CREATED: ${identityHashCode(this)}');
}
@override
void dispose() {
print('MyService DISPOSED: ${identityHashCode(this)}');
}
}
With this package, you'll see:
// First launch
MyService CREATED: 123456789
// After hot restart or ModularApp rebuild
MyService DISPOSED: 123456789 // ✅ Old instance disposed
MyService CREATED: 987654321 // ✅ Fresh instance created
📋 API Reference #
ModularInjectors #
| Method | Description |
|---|---|
ModularInjectors.initialize() |
Creates fresh injectors. Automatically disposes existing ones first. |
ModularInjectors.dispose() |
Disposes all injectors and clears references. |
ModularInjectors.isInitialized |
Returns true if injectors are ready. |
ModularInjectors.injector |
Access the main injector (throws if not initialized). |
ModularInjectors.innerInjector |
Access the inner injector for module bindings. |
Backward Compatibility #
The global injector getter still works:
// This still works (delegates to ModularInjectors.injector)
final service = injector.get<MyService>();
🤝 Contributing #
Contributions are welcome! Please feel free to submit issues and pull requests.
📄 License #
This project is licensed under the MIT License - see the LICENSE file for details.
🙏 Acknowledgments #
This is a fork of flutter_modular by Flutterando. All credit for the original architecture goes to them.
This fork specifically addresses the DI lifecycle management issue to provide proper singleton disposal.