flutter-routing-and-navigation by flutter/skills
npx skills add https://github.com/flutter/skills --skill flutter-routing-and-navigation在 Flutter 应用程序中实现稳健的导航和路由。评估应用程序需求以选择适当的路由策略(命令式 Navigator、声明式 Router 或嵌套导航),处理深度链接,并管理路由之间的数据传递,同时遵循 Flutter 最佳实践。
使用以下决策树评估应用程序的导航需求:
Router API(通常通过像 go_router 这样的路由包)。Navigator。Navigator API( 和 )配合 或 。广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
Navigator.pushNavigator.popMaterialPageRouteCupertinoPageRouteMaterialApp.routes 或 onGenerateRoute,但请注意其在深度链接自定义和网页前进按钮支持方面的限制。暂停并询问用户: "根据您的应用程序需求,我们应该实现简单的命令式导航(Navigator.push)、声明式路由(用于深度链接/网页的 Router/go_router),还是嵌套导航流程?"
如果选择简单导航,使用 Navigator 小部件来推送和弹出 Route 对象。
推送新路由:
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (context) => const SecondScreen(),
),
);
弹出一个路由:
Navigator.of(context).pop();
使用构造函数参数(对于命令式导航是首选)或 RouteSettings(对于命名路由)将数据传递给新屏幕。
通过构造函数传递:
// 导航并传递数据
Navigator.push(
context,
MaterialPageRoute<void>(
builder: (context) => DetailScreen(todo: currentTodo),
),
);
// 接收数据
class DetailScreen extends StatelessWidget {
const DetailScreen({super.key, required this.todo});
final Todo todo;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(todo.title)),
body: Text(todo.description),
);
}
}
通过 RouteSettings 传递(命名路由):
// 导航并传递数据
Navigator.pushNamed(
context,
'/details',
arguments: currentTodo,
);
// 在目标小部件中提取数据
final todo = ModalRoute.of(context)!.settings.arguments as Todo;
如果明确需要命名路由,请使用 initialRoute 和 routes 或 onGenerateRoute 配置 MaterialApp。
MaterialApp(
title: 'Named Routes App',
initialRoute: '/',
routes: {
'/': (context) => const FirstScreen(),
'/second': (context) => const SecondScreen(),
},
// 或者使用 onGenerateRoute 进行动态参数提取
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;
},
)
对于子流程,在小部件树中实例化一个新的 Navigator 小部件。您必须分配一个 GlobalKey<NavigatorState> 来管理嵌套堆栈。
class SetupFlowState extends State<SetupFlow> {
final _navigatorKey = GlobalKey<NavigatorState>();
void _onDiscoveryComplete() {
_navigatorKey.currentState!.pushNamed('/select_device');
}
@override
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);
}
}
审查已实现的路由逻辑以确保稳定性:
Navigator.pop() 不会意外关闭应用程序(必要时使用 Navigator.canPop(context))。initialRoute,请验证 MaterialApp 中是否未定义 home 属性。ModalRoute 提取参数,请验证是否安全处理了空值检查或类型转换。MaterialApp.routes);请改用 Router API。initialRoute,则不要在 MaterialApp 中定义 home 属性。Navigator 时,必须使用 GlobalKey<NavigatorState> 以确保定位到正确的导航堆栈。ModalRoute.of(context)!.settings.arguments 转换为特定的预期类型,并且如果路由可以在没有参数的情况下访问,请处理可能的空值。每周安装量
1.1K
代码仓库
GitHub 星标数
784
首次出现
2026年3月4日
安全审计
安装于
codex1.0K
cursor1.0K
opencode1.0K
gemini-cli1.0K
github-copilot1.0K
kimi-cli1.0K
Implements robust navigation and routing in Flutter applications. Evaluates application requirements to select the appropriate routing strategy (imperative Navigator, declarative Router, or nested navigation), handles deep linking, and manages data passing between routes while adhering to Flutter best practices.
Evaluate the application's navigation requirements using the following decision tree:
Router API (typically via a routing package like go_router).Navigator.Navigator API (Navigator.push and Navigator.pop) with MaterialPageRoute or CupertinoPageRoute.MaterialApp.routes or onGenerateRoute, but note the limitations regarding deep link customization and web forward-button support.STOP AND ASK THE USER: "Based on your app's requirements, should we implement simple imperative navigation (Navigator.push), declarative routing (Router/go_router for deep links/web), or a nested navigation flow?"
If simple navigation is selected, use the Navigator widget to push and pop Route objects.
Pushing a new route:
Navigator.of(context).push(
MaterialPageRoute<void>(
builder: (context) => const SecondScreen(),
),
);
Popping a route:
Navigator.of(context).pop();
Pass data to new screens using constructor arguments (preferred for imperative navigation) or RouteSettings (for named routes).
Passing via Constructor:
// 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;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(todo.title)),
body: Text(todo.description),
);
}
}
Passing via RouteSettings (Named Routes):
// 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;
If named routes are explicitly required, configure MaterialApp with initialRoute and routes or onGenerateRoute.
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;
},
)
For sub-flows, instantiate a new Navigator widget within the widget tree. You MUST assign a GlobalKey<NavigatorState> to manage the nested stack.
class SetupFlowState extends State<SetupFlow> {
final _navigatorKey = GlobalKey<NavigatorState>();
void _onDiscoveryComplete() {
_navigatorKey.currentState!.pushNamed('/select_device');
}
@override
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);
}
}
Review the implemented routing logic to ensure stability:
Navigator.pop() does not inadvertently close the application if the stack is empty (use Navigator.canPop(context) if necessary).initialRoute, verify that the home property is NOT defined in MaterialApp.ModalRoute, verify that null checks or type casts are safely handled.MaterialApp.routes) for applications requiring complex deep linking or web support; use the Router API instead.home property in MaterialApp if an initialRoute is provided.GlobalKey<NavigatorState> when implementing a nested Navigator to ensure the correct navigation stack is targeted.ModalRoute.of(context)!.settings.arguments to the specific expected type and handle potential nulls if the route can be accessed without arguments.Weekly Installs
1.1K
Repository
GitHub Stars
784
First Seen
Mar 4, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex1.0K
cursor1.0K
opencode1.0K
gemini-cli1.0K
github-copilot1.0K
kimi-cli1.0K
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
102,200 周安装