Documentation Index
Fetch the complete documentation index at: https://mintlify.com/khode-io/nest-dart/llms.txt
Use this file to discover all available pages before exploring further.
Advanced Routing Patterns
Nest Flutter provides powerful routing capabilities through the RouteMixin, enabling modular route organization with GoRouter integration.
Route Collection Mechanism
The collectAllRoutes() method recursively collects routes from a module and all its imported modules, preventing duplicate processing:
class TodoModule extends Module {
@override
List<Module> get imports => [CoreModule()];
@override
List<RouteBase> get routes => [
GoRoute(
path: '/',
builder: (context, state) => const TodoListView(),
),
GoRoute(
path: '/:id',
builder: (context, state) => TodoDetailView(
todoId: int.tryParse(state.pathParameters['id'] ?? '0') ?? 0,
),
),
];
}
The route collection mechanism uses a Set<Type> to track processed module types, preventing infinite loops in circular dependencies.
Route Prefixing
The routePrefix property allows you to namespace all routes within a module:
class TodoModule extends Module {
@override
String? get routePrefix => '/todos';
@override
List<RouteBase> get routes => [
GoRoute(path: '/', builder: (context, state) => const TodoListView()),
// Becomes /todos/
GoRoute(path: '/:id', builder: (context, state) => TodoDetailView()),
// Becomes /todos/:id
GoRoute(path: '/second', builder: (context, state) => Placeholder()),
// Becomes /todos/second
];
}
How Route Prefix Works
From packages/nest_flutter/lib/src/module.dart:48-62:
RouteBase _applyRoutePrefix(RouteBase route, String prefix) {
if (route is GoRoute) {
// Ensure prefix starts with / and doesn't end with /
final cleanPrefix = prefix.startsWith('/') ? prefix : '/$prefix';
final finalPrefix = cleanPrefix.endsWith('/')
? cleanPrefix.substring(0, cleanPrefix.length - 1)
: cleanPrefix;
// Ensure route path starts with /
final routePath = route.path.startsWith('/')
? route.path
: '/${route.path}';
// Combine prefix and path, avoiding double slashes
final newPath = routePath == '/' ? finalPrefix : '$finalPrefix$routePath';
return GoRoute(
path: newPath,
name: route.name,
builder: route.builder,
pageBuilder: route.pageBuilder,
redirect: route.redirect,
routes: route.routes,
);
}
return route;
}
The prefix normalization ensures clean paths regardless of whether you include leading/trailing slashes.
Nested Routes
Organize related routes hierarchically within a single module:
class ModuleB extends Module {
@override
String? get routePrefix => '/module-b';
@override
List<RouteBase> get routes => [
GoRoute(
path: '/',
builder: (context, state) => const MainView(),
routes: [
GoRoute(
path: 'second',
builder: (context, state) => const SecondView(),
),
GoRoute(
path: 'third/:id',
builder: (context, state) => DetailView(
id: state.pathParameters['id']!,
),
),
],
),
];
}
This creates the following route structure:
/module-b/ - MainView
/module-b/second - SecondView
/module-b/third/:id - DetailView
Multi-Module Route Organization
Routes are collected from imported modules in dependency-first order:
class AppModule extends Module {
@override
List<Module> get imports => [
AuthModule(), // Routes collected first
TodoModule(), // Then these routes
ProfileModule(), // Finally these routes
];
}
class AuthModule extends Module {
@override
String? get routePrefix => '/auth';
@override
List<RouteBase> get routes => [
GoRoute(path: '/login', builder: (context, state) => LoginView()),
GoRoute(path: '/register', builder: (context, state) => RegisterView()),
];
}
Collection Order
From packages/nest_flutter/lib/src/module.dart:27-42:
// First, collect routes from imported modules
for (final importedModule in imports) {
if (importedModule is Module) {
allRoutes.addAll(importedModule.collectAllRoutes(processedModuleTypes));
}
}
// Then add this module's routes, applying prefix if specified
for (final route in routes) {
final processedRoute = (routePrefix != null && routePrefix!.isNotEmpty)
? _applyRoutePrefix(route, routePrefix!)
: route;
allRoutes.add(processedRoute);
}
Imported modules’ routes are added before the current module’s routes. Order matters if you have overlapping path patterns.
Complete Router Setup
Integrate module routes with GoRouter:
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:nest_flutter/nest_flutter.dart';
class AppModule extends Module {
@override
List<Module> get imports => [
TodoModule(),
ProfileModule(),
];
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
final appModule = AppModule();
await Modular.initialize(appModule);
// Collect all routes from modules
final routes = appModule.collectAllRoutes();
final router = GoRouter(
routes: [
GoRoute(
path: '/',
builder: (context, state) => const HomeView(),
),
...routes, // Add all module routes
],
);
runApp(MyApp(router: router));
}
Best Practices
1. Use Clear Prefixes
// Good: Clear, semantic prefixes
class UserModule extends Module {
@override
String? get routePrefix => '/users';
}
// Avoid: Generic or unclear prefixes
class UserModule extends Module {
@override
String? get routePrefix => '/u';
}
class DashboardModule extends Module {
@override
String? get routePrefix => '/dashboard';
@override
List<RouteBase> get routes => [
GoRoute(
path: '/',
builder: (context, state) => const DashboardView(),
routes: [
GoRoute(path: 'analytics', builder: (context, state) => AnalyticsView()),
GoRoute(path: 'settings', builder: (context, state) => SettingsView()),
GoRoute(path: 'reports', builder: (context, state) => ReportsView()),
],
),
];
}
3. Avoid Route Conflicts
// Ensure module prefixes don't conflict
class AdminModule extends Module {
@override
String? get routePrefix => '/admin';
}
class UserModule extends Module {
@override
String? get routePrefix => '/users'; // Different prefix, no conflict
}
Use route prefixes to create clear boundaries between feature modules and prevent path conflicts.
Route Guards and Redirects
Combine route prefixes with GoRouter redirects for authentication:
class ProtectedModule extends Module {
@override
String? get routePrefix => '/app';
@override
List<RouteBase> get routes => [
GoRoute(
path: '/dashboard',
redirect: (context, state) {
final authService = Modular.get<AuthService>();
if (!authService.isAuthenticated) {
return '/login';
}
return null;
},
builder: (context, state) => const DashboardView(),
),
];
}