💉 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 {
@override
FutureOr<List<Bind<Object>>> binds() => [
Bind.singleton<HomeController>((i) => HomeController()),
Bind.factory<UserRepository>((i) => UserRepository()),
Bind.lazySingleton<ApiService>((i) => ApiService()),
];
@override
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 {
@override
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 {
@override
void initState(Injector i) {
// Initialize resources when module loads
final authService = i.get<AuthService>();
authService.initialize();
}
@override
FutureOr<List<Bind<Object>>> binds() => [
Bind.singleton<AuthService>((i) => AuthService()),
];
}
Disposal
class AuthModule extends Module {
@override
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 {
@override
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)),
),
];
}
}
class SharedPreferencesModule extends Module {
@override
FutureOr<List<Bind<Object>>> binds() async {
// Initialize SharedPreferences asynchronously
final prefs = await SharedPreferences.getInstance();
return [
Bind.singleton<SharedPreferences>((i) => prefs),
Bind.singleton<LocalStorageService>((i) => LocalStorageService(prefs)),
];
}
}
:::warning ⚠️ Important Note
In async binds()
methods, avoid using Modular.get<T>()
because the bind might not have been injected yet. Always use the Injector
parameter or create dependencies directly.
:::
:::warning Asynchronous binds are strictly forbidden in the AppModule
. Only use async binds in feature modules. The root AppModule
must always use synchronous binds to ensure proper app initialization and avoid unpredictable behavior.
:::
🎯 Advanced Patterns
Dependency with Parameters
class UserModule extends Module {
@override
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 {
@override
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 {
@override
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