flutter-routing-and-navigation
Original:🇺🇸 English
Translated
Move between or deep link to different screens or routes within a Flutter application
93installs
Sourceflutter/skills
Added on
NPX Install
npx skill4agent add flutter/skills flutter-routing-and-navigationTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →flutter-navigation-routing
Goal
Implements robust navigation and routing in Flutter applications. Evaluates application requirements to select the appropriate routing strategy (imperative , declarative , or nested navigation), handles deep linking, and manages data passing between routes while adhering to Flutter best practices.
NavigatorRouterInstructions
1. Determine Routing Strategy (Decision Logic)
Evaluate the application's navigation requirements using the following decision tree:
- Condition A: Does the app require complex deep linking, web URL synchronization, or advanced routing logic?
- Action: Use the declarative API (typically via a routing package like
Router).go_router
- Action: Use the declarative
- Condition B: Does the app require independent sub-flows (e.g., a multi-step setup wizard or persistent bottom navigation bars)?
- Action: Implement a Nested .
Navigator
- Action: Implement a Nested
- Condition C: Is it a simple application with basic screen-to-screen transitions and no complex deep linking?
- Action: Use the imperative API (
NavigatorandNavigator.push) withNavigator.poporMaterialPageRoute.CupertinoPageRoute
- Action: Use the imperative
- Condition D: Are Named Routes requested?
- Action: Use or
MaterialApp.routes, but note the limitations regarding deep link customization and web forward-button support.onGenerateRoute
- Action: Use
STOP AND ASK THE USER: "Based on your app's requirements, should we implement simple imperative navigation (), declarative routing (/ for deep links/web), or a nested navigation flow?"
Navigator.pushRoutergo_router2. Implement Basic Imperative Navigation
If simple navigation is selected, use the widget to push and pop objects.
NavigatorRoutePushing a new route:
dart
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (context) => const SecondScreen(),
),
);Popping a route:
dart
Navigator.of(context).pop();3. Implement Data Passing Between Screens
Pass data to new screens using constructor arguments (preferred for imperative navigation) or (for named routes).
RouteSettingsPassing via Constructor:
dart
// Navigating and passing data
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => DetailScreen(todo: currentTodo),
),
);
// Receiving data
class DetailScreen extends StatelessWidget {
const DetailScreen({super.key, required this.todo});
final Todo todo;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(todo.title)),
body: Text(todo.description),
);
}
}Passing via RouteSettings (Named Routes):
dart
// Navigating and passing data
Navigator.pushNamed(
context,
'/details',
arguments: currentTodo,
);
// Extracting data in the destination widget
final todo = ModalRoute.of(context)!.settings.arguments as Todo;4. Implement Named Routes (If Required)
If named routes are explicitly required, configure with and or .
MaterialAppinitialRouteroutesonGenerateRoutedart
MaterialApp(
title: 'Named Routes App',
initialRoute: '/',
routes: {
'/': (context) => const FirstScreen(),
'/second': (context) => const SecondScreen(),
},
// OR use onGenerateRoute for dynamic argument extraction
onGenerateRoute: (settings) {
if (settings.name == '/details') {
final args = settings.arguments as Todo;
return MaterialPageRoute(
builder: (context) => DetailScreen(todo: args),
);
}
assert(false, 'Need to implement ${settings.name}');
return null;
},
)5. Implement Nested Navigation
For sub-flows, instantiate a new widget within the widget tree. You MUST assign a to manage the nested stack.
NavigatorGlobalKey<NavigatorState>dart
class SetupFlowState extends State<SetupFlow> {
final _navigatorKey = GlobalKey<NavigatorState>();
void _onDiscoveryComplete() {
_navigatorKey.currentState!.pushNamed('/select_device');
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Setup Flow')),
body: Navigator(
key: _navigatorKey,
initialRoute: '/find_devices',
onGenerateRoute: _onGenerateRoute,
),
);
}
Route<Widget> _onGenerateRoute(RouteSettings settings) {
Widget page;
switch (settings.name) {
case '/find_devices':
page = WaitingPage(onWaitComplete: _onDiscoveryComplete);
break;
case '/select_device':
page = const SelectDevicePage();
break;
default:
throw StateError('Unexpected route name: ${settings.name}!');
}
return MaterialPageRoute(builder: (context) => page, settings: settings);
}
}6. Validate and Fix
Review the implemented routing logic to ensure stability:
- Verify that does not inadvertently close the application if the stack is empty (use
Navigator.pop()if necessary).Navigator.canPop(context) - If using , verify that the
initialRouteproperty is NOT defined inhome.MaterialApp - If extracting arguments via , verify that null checks or type casts are safely handled.
ModalRoute
Constraints
- Do NOT use named routes () for applications requiring complex deep linking or web support; use the
MaterialApp.routesAPI instead.Router - Do NOT define a property in
homeif anMaterialAppis provided.initialRoute - You MUST use a when implementing a nested
GlobalKey<NavigatorState>to ensure the correct navigation stack is targeted.Navigator - Do NOT include external URLs or links in the generated code or comments.
- Always cast to the specific expected type and handle potential nulls if the route can be accessed without arguments.
ModalRoute.of(context)!.settings.arguments