๐ Migration Guide
Migrate your existing GoRouter Modular app from version 2.x to 4.x with this comprehensive guide.
โ ๏ธ Breaking Changesโ
Important Changes in 4.x:โ
- Root routes now required
binds()
method replaces getter- New lifecycle methods
ModularApp.router
replacesMaterialApp.router
๐ Migration Stepsโ
1. Update Routesโ
Before (2.x):
class HomeModule extends Module {
List<ModularRoute> get routes => [
ChildRoute('/user', builder: (context, state) => UserPage()),
ChildRoute('/profile', builder: (context, state) => ProfilePage()),
];
}
After (4.x):
class HomeModule extends Module {
List<ModularRoute> get routes => [
ChildRoute('/', child: (context, state) => UserPage()), // Root required
ChildRoute('/profile', child: (context, state) => ProfilePage()),
];
}
2. Convert Bindsโ
Before (2.x):
class HomeModule extends Module {
List<Bind<Object>> get binds => [
Bind.singleton<UserService>((i) => UserService()),
Bind.factory<UserRepository>((i) => UserRepository()),
];
}
After (4.x):
class HomeModule extends Module {
FutureOr<List<Bind<Object>>> binds() => [
Bind.singleton<UserService>((i) => UserService()),
Bind.factory<UserRepository>((i) => UserRepository()),
];
}
3. Update App Widgetโ
Before (2.x):
class AppWidget extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: Modular.routerConfig,
title: 'My App',
theme: ThemeData(primarySwatch: Colors.blue),
);
}
}
After (4.x):
class AppWidget extends StatelessWidget {
Widget build(BuildContext context) {
return ModularApp.router(
title: 'My App',
theme: ThemeData(primarySwatch: Colors.blue),
);
}
}
4. Update Route Parametersโ
Before (2.x):
ChildRoute('/user/:id', builder: (context, state) =>
UserPage(id: state.params['id'])),
After (4.x):
ChildRoute('/user/:id', child: (context, state) =>
UserPage(id: state.pathParameters['id']!)),
5. Update Navigationโ
Before (2.x):
Modular.to.navigate('/user/123');
Modular.to.pushNamed('/modal');
After (4.x):
context.go('/user/123');
context.push('/modal');
๐ฏ Detailed Migration Examplesโ
Complete Module Migrationโ
Before (2.x):
class AuthModule extends Module {
List<Bind<Object>> get binds => [
Bind.singleton<AuthService>((i) => AuthService()),
Bind.singleton<AuthController>((i) => AuthController()),
];
List<ModularRoute> get routes => [
ChildRoute('/login', builder: (context, state) => LoginPage()),
ChildRoute('/register', builder: (context, state) => RegisterPage()),
ChildRoute('/user/:id', builder: (context, state) =>
UserPage(id: state.params['id'])),
];
}
After (4.x):
class AuthModule extends Module {
FutureOr<List<Bind<Object>>> binds() => [
Bind.singleton<AuthService>((i) => AuthService()),
Bind.singleton<AuthController>((i) => AuthController()),
];
List<ModularRoute> get routes => [
ChildRoute('/', child: (context, state) => LoginPage()), // Root route
ChildRoute('/register', child: (context, state) => RegisterPage()),
ChildRoute('/user/:id', child: (context, state) =>
UserPage(id: state.pathParameters['id']!)),
];
}
App Module Migrationโ
Before (2.x):
class AppModule extends Module {
List<Bind<Object>> get binds => [
Bind.singleton<AppController>((i) => AppController()),
];
List<ModularRoute> get routes => [
ModuleRoute("/", module: HomeModule()),
ModuleRoute("/auth", module: AuthModule()),
];
}
After (4.x):
class AppModule extends Module {
FutureOr<List<Bind<Object>>> binds() => [
Bind.singleton<AppController>((i) => AppController()),
];
List<ModularRoute> get routes => [
ModuleRoute("/", module: HomeModule()),
ModuleRoute("/auth", module: AuthModule()),
];
}
Main Function Migrationโ
Before (2.x):
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Modular.configure(Modular.configure(appModule: AppModule(), initialRoute: "/");
runApp(AppWidget());
}
class AppWidget extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp.router(
routerConfig: Modular.routerConfig,
title: 'My App',
theme: ThemeData(primarySwatch: Colors.blue),
);
}
}
After (4.x):
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Modular.configure(Modular.configure(appModule: AppModule(), initialRoute: "/");
runApp(AppWidget());
}
class AppWidget extends StatelessWidget {
Widget build(BuildContext context) {
return ModularApp.router(
title: 'My App',
theme: ThemeData(primarySwatch: Colors.blue),
);
}
}
๐ง Advanced Migrationโ
Event Module Migrationโ
Before (2.x):
class NotificationModule extends Module {
void initState() {
modularEvent.on<ShowNotificationEvent>().listen((event) {
// Handle event
});
}
}
After (4.x):
class NotificationModule extends EventModule {
void listen() {
on<ShowNotificationEvent>((event, context) {
// Handle event with context
});
}
}
Dependency Access Migrationโ
Before (2.x):
// In widgets
final controller = Modular.get<UserController>();
// In services
final service = Modular.get<UserService>();
After (4.x):
// In widgets (recommended)
final controller = context.read<UserController>();
// In services (still works)
final service = Modular.get<UserService>();
๐จ Common Issuesโ
1. Missing Root Routeโ
// โ Error - Missing root route
List<ModularRoute> get routes => [
ChildRoute('/profile', child: (context, state) => ProfilePage()),
];
// โ
Fix - Add root route
List<ModularRoute> get routes => [
ChildRoute('/', child: (context, state) => HomePage()),
ChildRoute('/profile', child: (context, state) => ProfilePage()),
];
2. Wrong Parameter Accessโ
// โ Error - Old parameter access
final id = state.params['id'];
// โ
Fix - New parameter access
final id = state.pathParameters['id']!;
3. Wrong Navigation Methodโ
// โ Error - Old navigation
Modular.to.navigate('/user/123');
// โ
Fix - New navigation
context.go('/user/123');
๐งช Testing Migrationโ
Update Test Filesโ
// Before (2.x)
void main() {
setUp(() {
Modular.configure(appModule: TestModule());
});
}
// After (4.x)
void main() {
setUp(() {
Modular.configure(appModule: TestModule(), initialRoute: "/");
});
}
๐ Related Topicsโ
- ๐ Quick Start - Get started with 4.x
- ๐ Dependency Injection - New DI patterns
- ๐ฃ๏ธ Routes - Updated routing system