Skip to main content

Overview

The Modular class provides a clean, static API for accessing dependency injection services and router configuration throughout your Flutter application. It offers a similar API to Flutter Modular but is built on Nest’s powerful DI container.
All Modular static methods require ModularApp to be initialized first. Calling these methods before initialization will throw a FlutterError.

Service Resolution Methods

get<T>()

Retrieve a service instance from the global container.
static T get<T extends Object>({String? instanceName})
T
Type
required
The type of service to retrieve. Must be registered in a module.
instanceName
String
Optional named instance identifier. Use when you have multiple instances of the same type registered with different names.
returns
T
The resolved service instance.

Usage Example

// Get a service
final userService = Modular.get<UserService>();
final user = await userService.getCurrentUser();

// Get a named instance
final primaryDb = Modular.get<Database>(instanceName: 'primary');
final cacheDb = Modular.get<Database>(instanceName: 'cache');

Usage in Widgets

class UserProfileScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final authService = Modular.get<AuthService>();
    final userRepo = Modular.get<UserRepository>();
    
    return FutureBuilder(
      future: userRepo.getUserProfile(authService.currentUserId),
      builder: (context, snapshot) {
        // Build UI
      },
    );
  }
}

getWithParams<T>()

Retrieve a service instance that requires parameters for construction.
static T getWithParams<T extends Object>(dynamic param1, [dynamic param2])
T
Type
required
The type of service to retrieve.
param1
dynamic
required
First parameter to pass to the factory function.
param2
dynamic
Optional second parameter to pass to the factory function.
returns
T
The resolved service instance created with the provided parameters.

Usage Example

// In your module
class AppModule extends Module {
  @override
  List<Provider> get providers => [
    Provider.factory<UserRepository>(
      (userId, tenantId) => UserRepository(userId, tenantId),
    ),
  ];
}

// Using getWithParams
final userRepo = Modular.getWithParams<UserRepository>(
  'user123',
  'tenant456',
);

getAsync<T>()

Retrieve an asynchronously initialized service instance.
static Future<T> getAsync<T extends Object>({String? instanceName})
T
Type
required
The type of service to retrieve.
instanceName
String
Optional named instance identifier.
returns
Future<T>
A future that completes with the resolved service instance.

Usage Example

// In your module - register async service
class AppModule extends Module {
  @override
  List<Provider> get providers => [
    Provider.asyncSingleton<Database>(
      () async => await Database.initialize(),
    ),
  ];
}

// Using getAsync
final database = await Modular.getAsync<Database>();
final users = await database.query('users');

isRegistered<T>()

Check if a service type is registered in the container.
static bool isRegistered<T extends Object>({String? instanceName})
T
Type
required
The type of service to check.
instanceName
String
Optional named instance identifier.
returns
bool
true if the service is registered, false otherwise.

Usage Example

if (Modular.isRegistered<AuthService>()) {
  final auth = Modular.get<AuthService>();
  // Use auth service
} else {
  // Handle missing service
  print('AuthService not registered');
}

// Check named instances
if (Modular.isRegistered<Database>(instanceName: 'cache')) {
  final cacheDb = Modular.get<Database>(instanceName: 'cache');
}

getAvailableServices()

Get a set of all registered service types.
static Set<Type> getAvailableServices()
returns
Set<Type>
A set containing all registered service types.

Usage Example

final services = Modular.getAvailableServices();
for (final serviceType in services) {
  print('Available: $serviceType');
}

// Check if specific service exists
if (services.contains(UserService)) {
  final userService = Modular.get<UserService>();
}

Context-Based Methods

of()

Get the ApplicationContainerNotifier from a BuildContext. This provides reactive access to the container.
static ApplicationContainerNotifier of(BuildContext context)
context
BuildContext
required
The build context to search for the container provider.
returns
ApplicationContainerNotifier
The container notifier from the widget tree.
Throws FlutterError if no ApplicationContainerProvider is found in the widget tree.

Usage Example

class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final containerNotifier = Modular.of(context);
    
    // Access services
    final userService = containerNotifier.get<UserService>();
    
    return ListenableBuilder(
      listenable: containerNotifier,
      builder: (context, child) {
        // Rebuild when container changes
        return Text('Services: ${containerNotifier.getAvailableServices().length}');
      },
    );
  }
}

containerOf()

Get the raw ApplicationContainer from a BuildContext.
static ApplicationContainer containerOf(BuildContext context)
context
BuildContext
required
The build context to search for the container.
returns
ApplicationContainer
The raw container instance.

Usage Example

class DebugPanel extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final container = Modular.containerOf(context);
    final modules = container.modules;
    
    return Column(
      children: [
        Text('Registered Modules: ${modules.length}'),
        for (final module in modules)
          Text('- ${module.runtimeType}'),
      ],
    );
  }
}

Router Configuration

router()

Create and configure a GoRouter instance from the root module’s routes.
static GoRouter router(
  GoRouter Function(GoRouter router) configurator, {
  Module? rootModule,
  bool forceRecreate = false,
})
configurator
Function
required
A callback function that receives the base router and returns a configured router. Use this to customize router settings.
Modular.router((router) {
  return GoRouter(
    routes: router.routes,
    initialLocation: '/home',
    debugLogDiagnostics: true,
    redirect: (context, state) {
      // Custom redirect logic
    },
  );
})
rootModule
Module
Optional root module. If not provided, uses the module from ModularApp.
forceRecreate
bool
default:"false"
Force recreation of the router even if a cached version exists. Useful when routes change dynamically.
returns
GoRouter
The configured GoRouter instance.
The router is cached by default to prevent recreation during hot reloads. Use forceRecreate: true or clearRouterCache() to regenerate routes.

Usage Example

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      title: 'My App',
      routerConfig: Modular.router(
        (router) => GoRouter(
          routes: router.routes,
          initialLocation: '/splash',
          debugLogDiagnostics: kDebugMode,
          redirect: (context, state) {
            final authService = Modular.get<AuthService>();
            final isLoggedIn = authService.isAuthenticated;
            
            if (!isLoggedIn && state.location != '/login') {
              return '/login';
            }
            return null;
          },
          errorBuilder: (context, state) => ErrorScreen(state.error),
        ),
      ),
    );
  }
}

clearRouterCache()

Clear the cached router instance, forcing recreation on next router() call.
static void clearRouterCache()

Usage Example

// After dynamically updating routes
Modular.clearRouterCache();

// Next router() call will recreate the router with new routes
final newRouter = Modular.router((router) => router);

cachedRouter

Get the currently cached router instance (for debugging).
static GoRouter? get cachedRouter
returns
GoRouter?
The cached router instance, or null if no router is cached.

isRouterCached

Check if a router is currently cached.
static bool get isRouterCached
returns
bool
true if a router is cached, false otherwise.

rootModule

Get the current root module.
static Module? get rootModule
returns
Module?
The root module instance, or null if not initialized.

Error Handling

Initialization Errors

try {
  final service = Modular.get<MyService>();
} catch (e) {
  if (e is FlutterError) {
    // Handle: ModularApp not initialized
    print('Make sure ModularApp is initialized');
  }
}

Service Not Found

try {
  final service = Modular.get<UnregisteredService>();
} catch (e) {
  // Handle: Service not registered in any module
  print('Service not found: $e');
}

Best Practices

Use Modular.of(context) for reactive updates: When you need to rebuild widgets based on container changes, use of() instead of static get().
Cache router in production: The default router caching prevents unnecessary recreations during hot reloads, improving development experience.
Don’t call get&lt;T&gt;() in widget constructors: Widget constructors can be called before ModularApp is initialized. Instead, call it in build() or initState().

See Also