flutter-routing-and-navigation

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

flutter-navigation-routing

flutter-navigation-routing

Goal

目标

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.
在Flutter应用中实现健壮的导航与路由功能。评估应用需求以选择合适的路由策略(命令式
Navigator
、声明式
Router
或嵌套导航),处理深层链接,管理路由间的数据传递,同时遵循Flutter最佳实践。

Instructions

使用指南

1. Determine Routing Strategy (Decision Logic)

1. 确定路由策略(决策逻辑)

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
      Router
      API (typically via a routing package like
      go_router
      ).
  • 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
      .
  • Condition C: Is it a simple application with basic screen-to-screen transitions and no complex deep linking?
    • Action: Use the imperative
      Navigator
      API (
      Navigator.push
      and
      Navigator.pop
      ) with
      MaterialPageRoute
      or
      CupertinoPageRoute
      .
  • Condition D: Are Named Routes requested?
    • Action: Use
      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?"
通过以下决策树评估应用的导航需求:
  • 条件A: 应用是否需要复杂的深层链接、Web URL同步或高级路由逻辑?
    • 操作: 使用声明式
      Router
      API(通常通过
      go_router
      这类路由包实现)。
  • 条件B: 应用是否需要独立的子流程(例如多步骤设置向导或持久化底部导航栏)?
    • 操作: 实现嵌套
      Navigator
  • 条件C: 是否是仅具备基础屏幕跳转、无复杂深层链接需求的简单应用?
    • 操作: 搭配
      MaterialPageRoute
      CupertinoPageRoute
      使用命令式
      Navigator
      API(
      Navigator.push
      Navigator.pop
      )。
  • 条件D: 是否要求使用命名路由?
    • 操作: 使用
      MaterialApp.routes
      onGenerateRoute
      ,但请注意其在深层链接自定义、Web前进按钮支持方面的局限性。
请停止操作并询问用户: "基于你的应用需求,我们应该实现简单的命令式导航(
Navigator.push
)、声明式路由(用于深层链接/Web端的
Router
/
go_router
)还是嵌套导航流程?"

2. Implement Basic Imperative Navigation

2. 实现基础命令式导航

If simple navigation is selected, use the
Navigator
widget to push and pop
Route
objects.
Pushing a new route:
dart
Navigator.of(context).push(
  MaterialPageRoute<void>(
    builder: (context) => const SecondScreen(),
  ),
);
Popping a route:
dart
Navigator.of(context).pop();
如果选择了简单导航,使用
Navigator
组件来推入和弹出
Route
对象。
推入新路由:
dart
Navigator.of(context).push(
  MaterialPageRoute<void>(
    builder: (context) => const SecondScreen(),
  ),
);
弹出路由:
dart
Navigator.of(context).pop();

3. Implement Data Passing Between Screens

3. 实现屏幕间的数据传递

Pass data to new screens using constructor arguments (preferred for imperative navigation) or
RouteSettings
(for named routes).
Passing 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;
通过构造函数参数(命令式导航的首选方案)或
RouteSettings
(适用于命名路由)向新屏幕传递数据。
通过构造函数传递:
dart
// 导航并传递数据
Navigator.push(
  context,
  MaterialPageRoute<void>(
    builder: (context) => DetailScreen(todo: currentTodo),
  ),
);

// 接收数据
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),
    );
  }
}
通过RouteSettings传递(命名路由):
dart
// 导航并传递数据
Navigator.pushNamed(
  context,
  '/details',
  arguments: currentTodo,
);

// 在目标组件中提取数据
final todo = ModalRoute.of(context)!.settings.arguments as Todo;

4. Implement Named Routes (If Required)

4. 实现命名路由(如有需要)

If named routes are explicitly required, configure
MaterialApp
with
initialRoute
and
routes
or
onGenerateRoute
.
dart
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;
  },
)
如果明确要求使用命名路由,为
MaterialApp
配置
initialRoute
routes
onGenerateRoute
dart
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;
  },
)

5. Implement Nested Navigation

5. 实现嵌套导航

For sub-flows, instantiate a new
Navigator
widget within the widget tree. You MUST assign a
GlobalKey<NavigatorState>
to manage the nested stack.
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);
  }
}
针对子流程,在组件树中实例化一个新的
Navigator
组件。你必须为其分配
GlobalKey<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

6. 验证与修复

Review the implemented routing logic to ensure stability:
  • Verify that
    Navigator.pop()
    does not inadvertently close the application if the stack is empty (use
    Navigator.canPop(context)
    if necessary).
  • If using
    initialRoute
    , verify that the
    home
    property is NOT defined in
    MaterialApp
    .
  • If extracting arguments via
    ModalRoute
    , verify that null checks or type casts are safely handled.
检查已实现的路由逻辑确保稳定性:
  • 验证栈为空时
    Navigator.pop()
    不会意外关闭应用(必要时使用
    Navigator.canPop(context)
    )。
  • 如果使用了
    initialRoute
    ,确认
    MaterialApp
    中未定义
    home
    属性。
  • 如果通过
    ModalRoute
    提取参数,确认空值检查和类型转换的处理是安全的。

Constraints

约束条件

  • Do NOT use named routes (
    MaterialApp.routes
    ) for applications requiring complex deep linking or web support; use the
    Router
    API instead.
  • Do NOT define a
    home
    property in
    MaterialApp
    if an
    initialRoute
    is provided.
  • You MUST use a
    GlobalKey<NavigatorState>
    when implementing a nested
    Navigator
    to ensure the correct navigation stack is targeted.
  • Do NOT include external URLs or links in the generated code or comments.
  • Always cast
    ModalRoute.of(context)!.settings.arguments
    to the specific expected type and handle potential nulls if the route can be accessed without arguments.
  • 对于需要复杂深层链接或Web支持的应用,禁止使用命名路由(
    MaterialApp.routes
    ),请改用
    Router
    API。
  • 如果提供了
    initialRoute
    ,禁止在
    MaterialApp
    中定义
    home
    属性。
  • 实现嵌套
    Navigator
    时必须使用
    GlobalKey<NavigatorState>
    ,确保定位到正确的导航栈。
  • 生成的代码或注释中禁止包含外部URL或链接。
  • 始终将
    ModalRoute.of(context)!.settings.arguments
    转换为预期的具体类型,如果路由可以在无参数的情况下访问,请处理可能的空值。