Overview
The Module class extends nest_core.Module with routing capabilities through the RouteMixin. It provides a way to organize both dependency injection providers and route definitions in a cohesive module structure.
Class Definition
abstract class Module extends nest_core.Module with RouteMixin {}
The Flutter Module class combines:
- Dependency Injection: From
nest_core.Module (providers, imports, exports)
- Routing Support: From
RouteMixin (routes, route prefixes, route collection)
Inheritance
Object
└─ nest_core.Module
└─ Module (with RouteMixin)
Creating a Module
Basic Module
import 'package:nest_flutter/nest_flutter.dart';
import 'package:go_router/go_router.dart';
class HomeModule extends Module {
@override
List<Provider> get providers => [
Provider.singleton<HomeService>(() => HomeService()),
Provider.factory<HomeRepository>(() => HomeRepository()),
];
@override
List<RouteBase> get routes => [
GoRoute(
path: '/home',
builder: (context, state) => HomeScreen(),
),
];
}
Module with Imports
class AppModule extends Module {
@override
List<Module> get imports => [
AuthModule(),
UserModule(),
HomeModule(),
];
@override
List<Provider> get providers => [
Provider.singleton<AppService>(() => AppService()),
];
@override
List<RouteBase> get routes => [
GoRoute(
path: '/',
redirect: (context, state) => '/home',
),
];
}
RouteMixin API
The RouteMixin adds routing capabilities to modules.
routes
Define the routes provided by this module.
List<RouteBase> get routes => []
A list of GoRouter routes. These routes will be automatically collected and registered with the router.
Usage Example
class UserModule extends Module {
@override
List<RouteBase> get routes => [
GoRoute(
path: '/users',
builder: (context, state) => UserListScreen(),
routes: [
GoRoute(
path: ':id',
builder: (context, state) {
final userId = state.pathParameters['id']!;
return UserDetailScreen(userId: userId);
},
),
],
),
GoRoute(
path: '/profile',
builder: (context, state) => ProfileScreen(),
),
];
}
routePrefix
Optional prefix to prepend to all routes in this module.
String? get routePrefix => null
The route prefix, or null for no prefix. Useful for organizing routes under a common path segment.
Route prefixes are automatically normalized to start with / and not end with /.
Usage Example
class AdminModule extends Module {
@override
String get routePrefix => '/admin';
@override
List<RouteBase> get routes => [
GoRoute(
path: '/dashboard', // Becomes: /admin/dashboard
builder: (context, state) => AdminDashboard(),
),
GoRoute(
path: '/users', // Becomes: /admin/users
builder: (context, state) => AdminUsersScreen(),
),
GoRoute(
path: '/settings', // Becomes: /admin/settings
builder: (context, state) => AdminSettings(),
),
];
}
Prefix Normalization
// Input variations that all produce the same result:
routePrefix => '/admin'; // OK
routePrefix => 'admin'; // Normalized to: /admin
routePrefix => '/admin/'; // Normalized to: /admin
routePrefix => 'admin/'; // Normalized to: /admin
// Route path variations:
path: '/dashboard'; // OK
path: 'dashboard'; // Normalized to: /dashboard
// Final result: /admin/dashboard
collectAllRoutes()
Collect all routes from this module and its imports recursively.
List<RouteBase> collectAllRoutes([Set<Type>? processedModuleTypes])
Internal parameter used to track processed module types and prevent duplicates. You typically don’t need to provide this.
A flat list of all routes from this module and all imported modules, with prefixes applied.
This method prevents duplicate processing of the same module type. If a module type appears multiple times in the import tree, it’s only processed once.
Collection Order
- Routes from imported modules (depth-first)
- Routes from the current module
- Route prefixes are applied to each module’s routes
- Duplicate module types are skipped
Usage Example
// Typically called internally by Modular.router()
// But you can call it manually if needed:
final appModule = AppModule();
final allRoutes = appModule.collectAllRoutes();
print('Total routes: ${allRoutes.length}');
for (final route in allRoutes) {
if (route is GoRoute) {
print('Route: ${route.path}');
}
}
Inherited from nest_core.Module
The Flutter Module class inherits all properties from nest_core.Module:
providers
List of dependency injection providers.
List<Provider> get providers => []
Usage Example
class UserModule extends Module {
@override
List<Provider> get providers => [
// Singleton - single instance shared across app
Provider.singleton<UserRepository>(
() => UserRepository(Modular.get<Database>()),
),
// Factory - new instance each time
Provider.factory<UserService>(
() => UserService(Modular.get<UserRepository>()),
),
// Lazy singleton - created on first access
Provider.lazySingleton<UserCache>(
() => UserCache(),
),
// Async singleton - asynchronous initialization
Provider.asyncSingleton<UserDatabase>(
() async => await UserDatabase.initialize(),
),
];
}
imports
List of modules to import.
List<Module> get imports => []
Imported modules make their exported providers available to this module and collect their routes.
Usage Example
class AppModule extends Module {
@override
List<Module> get imports => [
CoreModule(),
AuthModule(),
UserModule(),
ProductModule(),
];
}
exports
List of provider types to export to importing modules.
List<Type> get exports => []
Usage Example
class CoreModule extends Module {
@override
List<Provider> get providers => [
Provider.singleton<Database>(() => Database()),
Provider.singleton<Logger>(() => Logger()),
Provider.singleton<Config>(() => Config()),
];
@override
List<Type> get exports => [
Database, // Available to importing modules
Logger, // Available to importing modules
// Config is NOT exported - private to CoreModule
];
}
class UserModule extends Module {
@override
List<Module> get imports => [CoreModule()];
@override
List<Provider> get providers => [
Provider.singleton<UserRepository>(
// Can access Database and Logger because they're exported
() => UserRepository(
Modular.get<Database>(),
Modular.get<Logger>(),
),
),
];
}
Complete Module Example
Feature Module
import 'package:nest_flutter/nest_flutter.dart';
import 'package:go_router/go_router.dart';
class ProductModule extends Module {
@override
String get routePrefix => '/products';
@override
List<Provider> get providers => [
Provider.singleton<ProductRepository>(
() => ProductRepository(Modular.get<Database>()),
),
Provider.factory<ProductService>(
() => ProductService(Modular.get<ProductRepository>()),
),
Provider.lazySingleton<ProductCache>(
() => ProductCache(),
),
];
@override
List<RouteBase> get routes => [
GoRoute(
path: '/', // Becomes: /products
builder: (context, state) => ProductListScreen(),
routes: [
GoRoute(
path: ':id', // Becomes: /products/:id
builder: (context, state) {
final productId = state.pathParameters['id']!;
return ProductDetailScreen(productId: productId);
},
),
],
),
GoRoute(
path: '/create', // Becomes: /products/create
builder: (context, state) => CreateProductScreen(),
),
];
}
Root Application Module
class AppModule extends Module {
@override
List<Module> get imports => [
CoreModule(),
AuthModule(),
UserModule(),
ProductModule(),
OrderModule(),
];
@override
List<Provider> get providers => [
Provider.singleton<AppService>(
() => AppService(
Modular.get<AuthService>(),
Modular.get<Logger>(),
),
),
];
@override
List<RouteBase> get routes => [
GoRoute(
path: '/',
redirect: (context, state) => '/home',
),
GoRoute(
path: '/home',
builder: (context, state) => HomeScreen(),
),
];
}
Module with Shared Services
class CoreModule extends Module {
@override
List<Provider> get providers => [
Provider.singleton<Database>(
() => Database('app.db'),
),
Provider.singleton<Logger>(
() => Logger(),
),
Provider.singleton<ApiClient>(
() => ApiClient(
baseUrl: 'https://api.example.com',
logger: Modular.get<Logger>(),
),
),
];
@override
List<Type> get exports => [
Database,
Logger,
ApiClient,
];
// Core module typically has no routes
@override
List<RouteBase> get routes => [];
}
Duplicate Module Prevention
If the same module type appears multiple times in the import tree, only the first occurrence is processed. This prevents duplicate route registration and circular dependencies.
class SharedModule extends Module {
@override
List<RouteBase> get routes => [
GoRoute(path: '/shared', builder: (context, state) => SharedScreen()),
];
}
class ModuleA extends Module {
@override
List<Module> get imports => [SharedModule()];
}
class ModuleB extends Module {
@override
List<Module> get imports => [SharedModule()];
}
class AppModule extends Module {
@override
List<Module> get imports => [
ModuleA(),
ModuleB(),
];
}
// Result: SharedModule is only processed once
// The /shared route is only registered once
Best Practices
Organize by feature: Create one module per feature area (auth, user, product, etc.) with its own services and routes.
Use route prefixes for organization: Group related routes under a common prefix to create clear URL hierarchies.
Export shared services: Use a CoreModule or SharedModule to provide common services to other modules.
Avoid circular imports: Don’t create circular dependencies between modules. Use a shared module for common dependencies.
See Also