Skip to content

Commit

Permalink
[完成翻译] src/content/cookbook/effects/nested-nav.md (#1486)
Browse files Browse the repository at this point in the history
Co-authored-by: Amos <amoshuke@qq.com>
  • Loading branch information
Pleasurecruise and AmosHuKe authored Aug 26, 2024
1 parent 4efba99 commit 0a4f60a
Showing 1 changed file with 134 additions and 0 deletions.
134 changes: 134 additions & 0 deletions src/content/cookbook/effects/nested-nav.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ top-level `Navigator` widget. The list would be very long,
and many of these routes would
be better handled nested within another widget.

随着应用程序的发展,它会累积几十甚至上百条路由。
虽然,有些路由可以作为顶层(全局)路由。
例如,“/”、“profile”、“contact”、“social_feed”
这些都是应用中可能存在的顶层路由。
但是,如果你在顶层 `Navigator` widget 中定义了所有可能的路由,
那么路由列表将会非常庞大,
实际上,许多路由更适合嵌套在其他 widget 中处理。

Consider an Internet of Things (IoT) setup flow for a wireless
light bulb that you control with your app.
This setup flow consists of 4 pages:
Expand All @@ -32,21 +40,43 @@ in the setup flow. This delegation of navigation facilitates
greater local control, which is
generally preferable when developing software.

设想一个用于无线灯泡的物联网 (IoT) 设置流程,
你可以通过应用程序来控制这个灯泡。
该设置流程包括 4 个页面:
查找附近的灯泡、选择你要添加的灯泡、添加灯泡、最后完成设置。
你可以在顶层 `Navigator` widget 中协调这些操作。
然而,更合理的做法是,
在你的 `SetupFlow` widget 中定义一个嵌套的 `Navigator` widget,
并让这个嵌套的 `Navigator` 负责管理设置流程中的这 4 个页面。
这种导航委托方式有助于加强局部控制,
这在软件开发中通常是更可取的。

The following animation shows the app's behavior:

下面的动画展示了应用程序的行为:

![Gif showing the nested "setup" flow](/assets/images/docs/cookbook/effects/NestedNavigator.gif){:.site-mobile-screenshot}

In this recipe, you implement a four-page IoT setup
flow that maintains its own navigation nested beneath
the top-level `Navigator` widget.

在这个教程中,你将实现一个包含四个页面的物联网 (IoT) 设置流程,
该流程在顶层 `Navigator` widget 下嵌套了单独管理的导航。

## Prepare for navigation

## 导航准备阶段

This IoT app has two top-level screens,
along with the setup flow. Define these
route names as constants so that they can
be referenced within code.

这个物联网 (IoT) 应用程序包含两个顶层页面,
以及一个设置流程。
将这些路由名称定义为常量,以便在代码中引用它们。

<?code-excerpt "lib/main.dart (routes)"?>
```dart
const routeHome = '/';
Expand All @@ -68,6 +98,13 @@ that a route name is intended for the setup flow without
recognizing all the individual pages associated with
the setup flow.

主页和设置页的路由是使用静态名称引用的。
然而,设置流程中的页面是通过两个路径组合来生成它们的路由名称的:
首先是一个 `/setup/` 前缀,然后是具体页面的名称。
通过将这两个路径组合在一起,
你的 `Navigator` 可以判断出某个路由名称是否属于设置流程,
而无需识别所有与设置流程相关的具体页面。

The top-level `Navigator` isn't responsible for identifying
individual setup flow pages. Therefore, your top-level
`Navigator` needs to parse the incoming route name to
Expand All @@ -76,9 +113,19 @@ means that you can't use the `routes` property of your top-level
`Navigator`. Instead, you must provide a function for the
`onGenerateRoute` property.

顶层 `Navigator` 不负责识别具体的设置流程页面。
因此,顶层 `Navigator` 需要解析传入的路由名称,
以识别设置流程的前缀。
由于需要解析路由名称,
不能使用顶层 `Navigator``routes` 属性。
相反,你必须为 `onGenerateRoute` 属性提供一个函数。

Implement `onGenerateRoute` to return the appropriate widget
for each of the three top-level paths.

实现 `onGenerateRoute` 函数,
以便为三个顶层路径分别返回相应的 widget。

<?code-excerpt "lib/main.dart (OnGenerateRoute)"?>
```dart
onGenerateRoute: (settings) {
Expand Down Expand Up @@ -115,9 +162,18 @@ This splitting of the route name is what allows the top-level
`Navigator` to be agnostic toward the various subroutes
within the setup flow.

请注意,主页和设置页的路由是与精确的路由名称相匹配的。
然而,设置流程的路由条件只检查前缀。
如果路由名称包含设置流程的前缀,那么路由名称的其余部分将被忽略,
并将其余部分传递给 `SetupFlow` widget 进行处理。
这种对路由名称的拆分方式,使顶层 `Navigator` 可以不关注设置流程中的各个子路由。

Create a stateful widget called `SetupFlow` that
accepts a route name.

创建一个名为 `SetupFlow` 的 stateful widget,
该 widget 接收一个路由名称作为参数。

<?code-excerpt "lib/setupflow.dart (SetupFlow)" replace="/@override\n*.*\n\s*return const SizedBox\(\);\n\s*}/\/\/.../g"?>
```dart
class SetupFlow extends StatefulWidget {
Expand All @@ -139,13 +195,20 @@ class SetupFlowState extends State<SetupFlow> {

## Display an app bar for the setup flow

## 为设置流程显示一个 AppBar

The setup flow displays a persistent app bar
that appears across all pages.

设置流程显示一个始终可见的 AppBar,贯穿设置流程中的所有页面。

Return a `Scaffold` widget from your `SetupFlow`
widget's `build()` method,
and include the desired `AppBar` widget.

在你的 `SetupFlow` widget 的 `build()` 方法中返回一个 `Scaffold` widget,
并包含所需的 `AppBar` widget。

<?code-excerpt "lib/setupflow2.dart (SetupFlow2)"?>
```dart
@override
Expand All @@ -169,10 +232,18 @@ exiting the flow causes the user to lose all progress.
Therefore, the user is prompted to confirm whether they
want to exit the setup flow.

AppBar 显示一个返回箭头,
当返回箭头被按下时,会退出设置流程。
然而,退出流程会导致用户丢失所有进度。
因此,系统会提示用户确认是否真的想要退出设置流程。

Prompt the user to confirm exiting the setup flow,
and ensure that the prompt appears when the user
presses the hardware back button on Android.

提示用户确认是否退出设置流程,
并确保在用户按下 Android 设备上的实体返回按钮时也会出现该提示。

<?code-excerpt "lib/prompt_user.dart (PromptUser)"?>
```dart
Future<void> _onExitPressed() async {
Expand Down Expand Up @@ -251,20 +322,37 @@ If the user presses **Leave**, then the setup flow pops itself
from the top-level navigation stack.
If the user presses **Stay**, then the action is ignored.

当用户点击 AppBar 中的返回箭头或按下 Android 设备上的实体返回按钮时,
会弹出一个警告对话框,确认用户是否要离开设置流程。
如果用户点击 **Leave**,则设置流程会从顶层导航堆栈中移除。
如果用户点击 **Stay**,则忽略该操作。

You might notice that the `Navigator.pop()`
is invoked by both the **Leave** and
**Stay** buttons. To be clear,
this `pop()` action pops the alert dialog off
the navigation stack, not the setup flow.

你可能会注意到,
**Leave****Stay** 按钮都会调用 `Navigator.pop()`
需要明确的是,这个 `pop()` 操作是将警告对话框从导航堆栈中移除,
而不是移除设置流程。

## Generate nested routes

## 创建嵌套路由

The setup flow's job is to display the appropriate
page within the flow.

设置流程的任务是显示流程中相应的页面。

Add a `Navigator` widget to `SetupFlow`,
and implement the `onGenerateRoute` property.

`SetupFlow` 中添加一个 `Navigator` widget,
并实现 `onGenerateRoute` 属性。

<?code-excerpt "lib/add_navigator.dart (AddNavigator)"?>
```dart
final _navigatorKey = GlobalKey<NavigatorState>();
Expand Down Expand Up @@ -338,6 +426,10 @@ which includes the route's `name`.
Based on that route name,
one of four flow pages is returned.

`_onGenerateRoute` 函数的工作方式与顶层 `Navigator` 相同。
该函数接收一个`RouteSettings` 对象,其中包含了路由名称 `name`
根据路由名称,将返回四个流程页面之一。

The first page, called `find_devices`,
waits a few seconds to simulate network scanning.
After the wait period, the page invokes its callback.
Expand All @@ -348,6 +440,13 @@ Therefore, in `_onDiscoveryComplete`, the `_navigatorKey`
instructs the nested `Navigator` to navigate to the
`select_device` page.

第一页名为 `find_devices`,它会等待几秒钟来模拟网络扫描。
在等待时间结束后,页面会调用其回调函数。
在这个教程中,回调函数是 `_onDiscoveryComplete`
设置流程识别到设备发现完成后,应该显示设备选择页面。
因此,在 `_onDiscoveryComplete` 中,
`_navigatorKey` 指示嵌套的 `Navigator` 导航到 `select_device` 页面。

The `select_device` page asks the user to select a
device from a list of available devices. In this recipe,
only one device is presented to the user.
Expand All @@ -358,6 +457,13 @@ should be shown. Therefore, in `_onDeviceSelected`,
the `_navigatorKey` instructs the nested `Navigator`
to navigate to the `"connecting"` page.

`select_device` 页面要求用户从可用设备列表中选择一个设备。
在这个教程中,只向用户展示了一个设备。
当用户点击设备时,`onDeviceSelected` 回调被调用。
设置流程识别到设备选择后,应该显示连接页面。
因此,在 `_onDeviceSelected` 中,
`_navigatorKey` 指示嵌套的 `Navigator` 导航到 `"connecting"` 页面。

The `connecting` page works the same way as the
`find_devices` page. The `connecting` page waits
for a few seconds and then invokes its callback.
Expand All @@ -368,27 +474,55 @@ in `_onConnectionEstablished`, the `_navigatorKey`
instructs the nested `Navigator` to navigate to the
`finished` page.

`connecting` 页面与 `find_devices` 页面工作方式相同。
`connecting` 页面等待几秒钟,然后调用其回调函数。
在这个教程中,回调函数是 `_onConnectionEstablished`
设置流程识别到连接建立后,应该显示最终页面。
因此,在 `_onConnectionEstablished` 中,
`_navigatorKey` 指示嵌套的 `Navigator` 导航到 `finished` 页面。

The `finished` page provides the user with a **Finish**
button. When the user taps **Finish**,
the `_exitSetup` callback is invoked, which pops the entire
setup flow off the top-level `Navigator` stack,
taking the user back to the home screen.

`finished` 页面提供了一个 **Finish** 按钮。
当用户点击 **Finish** 时,`_exitSetup` 回调被调用,
这会将整个设置流程从顶层 `Navigator` 堆栈中移除,使用户回到主页。

Congratulations!
You implemented nested navigation with four subroutes.

恭喜你!你实现了具有四个子路由的嵌套导航。

## Interactive example

## 交互示例

Run the app:

运行应用程序:

* On the **Add your first bulb** screen,
click the FAB, shown with a plus sign, **+**.
This brings you to the **Select a nearby device**
screen. A single bulb is listed.

**Add your first bulb** 页面上,
点击带有加号 **+** 的悬浮操作按钮。
这会将你带到 **Select a nearby device** 页面。
页面上列出了一个灯泡设备。

* Click the listed bulb. A **Finished!** screen appears.

点击列出的灯泡设备。页面上出现 **Finish** 按钮。

* Click the **Finished** button to return to the
first screen.

按下 **Finish** 按钮返回第一页。

<?code-excerpt "lib/main.dart"?>
```dartpad title="Flutter nested navigation hands-on example in DartPad" run="true"
import 'package:flutter/material.dart';
Expand Down

0 comments on commit 0a4f60a

Please sign in to comment.