💉 Dependency Injection
GoRouter Modular provides a powerful dependency injection system with automatic disposal to prevent memory leaks.
🔧 Basic Usage
Module Dependencies
class HomeModule extends Module {
FutureOr<List<Bind<Object>>> binds() => [
Bind.singleton<HomeController>((i) => HomeController()),
Bind.factory<UserRepository>((i) => UserRepository()),
Bind.lazySingleton<ApiService>((i) => ApiService()),
];
List<ModularRoute> get routes => [
ChildRoute('/', child: (context, state) => HomePage()),
];
}
Dependency Types
Singleton - One instance for the entire module lifecycle
Bind.singleton<HomeController>((i) => HomeController());
Factory - New instance every time
Bind.factory<UserRepository>((i) => UserRepository());
Lazy Singleton - Created only when first accessed
Bind.lazySingleton<ApiService>((i) => ApiService());
🔍 Retrieving Dependencies
Using Context
class HomePage extends StatelessWidget {
Widget build(BuildContext context) {
final controller = context.read<HomeController>();
final repository = context.read<UserRepository>();
return Scaffold(
body: Text('Hello ${controller.userName}'),
);
}
}
Using Modular
final controller = Modular.get<HomeController>();
final repository = Modular.get<UserRepository>();
Using Bind
final controller = Bind.get<HomeController>();
final repository = Bind.get<UserRepository>();
🔄 Module Lifecycle
Initialization
class AuthModule extends Module {
void initState(Injector i) {
// Initialize resources when module loads
final authService = i.get<AuthService>();
authService.initialize();
}
FutureOr<List<Bind<Object>>> binds() => [
Bind.singleton<AuthService>((i) => AuthService()),
];
}
Disposal
class AuthModule extends Module {
void dispose() {
// The dispose method is used when you want to execute an action when the module is disposed,
// for example, stop listening to a stream or disconnect from a websocket.
}
}
⚡️ Asynchronous Binds
You can use asynchronous binds to initialize dependencies that require async operations, such as fetching remote configuration or initializing plugins.
Example: Remote Config (e.g., Firebase Remote Config)
class RemoteConfigModule extends Module {
FutureOr<List<Bind<Object>>> binds() async {
// Fetch remote config asynchronously
final remoteConfig = await RemoteConfig.instance.fetchAndActivate();
return [
Bind.singleton<Dio>(
(i) => Dio(BaseOptions(baseUrl: remoteConfig.baseUrl)),
),
];
}
}
Warning Asynchronous binds are strictly forbidden in the
AppModule
. Only use async binds in feature modules. The rootAppModule
must always use synchronous binds to ensure proper app initialization and avoid unpredictable behavior.
🎯 Advanced Patterns
Dependency with Parameters
class UserModule extends Module {
FutureOr<List<Bind<Object>>> binds() => [
Bind.singleton<UserService>((i) => UserService(
apiKey: 'your-api-key',
baseUrl: 'https://api.example.com',
)),
];
}
Dependent Dependencies
class AppModule extends Module {
FutureOr<List<Bind<Object>>> binds() => [
Bind.singleton<ApiService>((i) => ApiService()),
Bind.singleton<UserRepository>((i) => UserRepository(
apiService: i.get<ApiService>(), // Inject dependency
)),
];
}
Conditional Dependencies
class AppModule extends Module {
FutureOr<List<Bind<Object>>> binds() => [
if (kDebugMode)
Bind.singleton<Logger>((i) => DebugLogger())
else
Bind.singleton<Logger>((i) => ProductionLogger()),
];
}
🛡️ Memory Management
Automatic Disposal
- Dependencies are automatically disposed when modules are unloaded
- Prevents memory leaks
- No manual cleanup required
📚 Related Topics
- 🏗️ Project Structure - Organize your modules
- 🛣️ Routes - Define module routes
- 🎭 Event System - Module communication