# 传递数据到新页面

> 如何向新路由传递数据。



<?code-excerpt path-base="cookbook/navigation/passing_data"?>

Often, you not only want to navigate to a new screen,
but also pass data to the screen as well.
For example, you might want to pass information about
the item that's been tapped.

在开发的过程中，我们经常需要在跳转到新页面的时候，
能同时传递一些数据。比如，传递用户点击的元素信息。

Remember: Screens are just widgets.
In this example, create a list of todos.
When a todo is tapped, navigate to a new screen (widget) that
displays information about the todo.
This recipe uses the following steps:

还记得么，全屏的界面也只是 widget。在这个例子中，我们会创建一个待办事项列表，
当某个事项被点击的时候，会跳转到新的一屏 (widget)，
在新的一屏显示待办事项的详细信息。

  1. Define a todo class.

     定义一个描述待办事项的数据类

  2. Display a list of todos.

     显示待办事项

  3. Create a detail screen that can display information about a todo.

     创建一个显示待办事项详细信息的界面

  4. Navigate and pass data to the detail screen.

     传递数据并跳转到待办事项详细信息界面

## 1. Define a todo class

## 1. 定义一个描述待办事项的数据类

First, you need a simple way to represent todos. For this example,
create a class that contains two pieces of data: the title and description.

首先，我们需要一个简单的方式来描述待办事项。
我们创建一个类叫做 `Todo`，包含 `title` 和 `description` 两个成员变量。

<?code-excerpt "lib/main.dart (Todo)"?>
```dart
class Todo {
  final String title;
  final String description;

  const Todo(this.title, this.description);
}
```

## 2. Create a list of todos

## 2. 创建待办事项列表

Second, display a list of todos. In this example, generate
20 todos and show them using a ListView.
For more information on working with lists,
see the [Use lists][] recipe.

第二步，我们需要显示一个待办事项列表，
生成 20 条待办事项并用 `ListView` 显示。
如果你想了解更多关于列表显示的内容，
请阅读文档 [`基础列表`][Use lists]。

### Generate the list of todos

### 生成待办事项列表

<?code-excerpt "lib/main.dart (Generate)" replace="/^todos:/final todos =/g/^\),$/);/g"?>
```dart
final todos = List.generate(
  20,
  (i) => Todo(
    'Todo $i',
    'A description of what needs to be done for Todo $i',
  ),
);
```

### Display the list of todos using a ListView

### 用 `ListView` 显示待办事项列表

<?code-excerpt "lib/main_todoscreen.dart (ListViewBuilder)" replace="/^body: //g;/^\),$/)/g"?>
```dart
ListView.builder(
  itemCount: todos.length,
  itemBuilder: (context, index) {
    return ListTile(title: Text(todos[index].title));
  },
)
```

So far, so good.
This generates 20 todos and displays them in a ListView.

到目前为止，我们生成了 20 条待办事项，
并用 `ListView` 把它显示出来了。

## 3. Create a Todo screen to display the list

## 3. 创建一个待办页面显示待办事件列表

For this, we create a `StatelessWidget`. We call it `TodosScreen`.
Since the contents of this page won't change during runtime,
we'll have to require the list
of todos within the scope of this widget.

为了实现这个，我们要创建一个无状态的 widget (`StatelessWidget`)，
我们叫它 `TodosScreen`。
因为这个页面在运行时内容并不会变动，
在这个 widget 的 scope 里，
我们会把这个待办事项的数组设置为必须 (加入 @require 限定符）。

We pass in our `ListView.builder` as body of the widget we're returning to `build()`.
This'll render the list on to the screen for you to get going!

我们把 `ListView.builder` 作为 body 的参数返回给 `build()` 方法，
这将会把列表渲染到屏幕上供你继续下一步。

<?code-excerpt "lib/main_todoscreen.dart (TodosScreen)"?>
```dart
class TodosScreen extends StatelessWidget {
  // Requiring the list of todos.
  const TodosScreen({super.key, required this.todos});

  final List<Todo> todos;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Todos')),
      //passing in the ListView.builder
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, index) {
          return ListTile(title: Text(todos[index].title));
        },
      ),
    );
  }
}
```

With Flutter's default styling, you're good to go without sweating about
things that you'd like to do later on!

使用 Flutter 自带的样式，未来会变得很轻松。

## 4. Create a detail screen to display information about a todo

## 4. 创建一个显示待办事项详细信息的界面

Now, create the second screen. The title of the screen contains the
title of the todo, and the body of the screen shows the description.

现在，我们来创建第二个全屏的界面，
界面的标题是待办事项的标题，
界面下面显示待办事项的描述信息。

Since the detail screen is a normal `StatelessWidget`,
require the user to enter a `Todo` in the UI.
Then, build the UI using the given todo.

这个界面是一个 `StatelessWidget`，创建的时需要传递 `Todo` 对象给它，
它就可以使用传给他的 `Todo` 对象来构建 UI 。

<?code-excerpt "lib/main.dart (detail)"?>
```dart
class DetailScreen extends StatelessWidget {
  // In the constructor, require a Todo.
  const DetailScreen({super.key, required this.todo});

  // Declare a field that holds the Todo.
  final Todo todo;

  @override
  Widget build(BuildContext context) {
    // Use the Todo to create the UI.
    return Scaffold(
      appBar: AppBar(title: Text(todo.title)),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Text(todo.description),
      ),
    );
  }
}
```

## 5. Navigate and pass data to the detail screen

## 5. 传递数据并跳转到待办事项详细信息界面

With a `DetailScreen` in place,
you're ready to perform the Navigation.
In this example, navigate to the `DetailScreen` when a user
taps a todo in the list. Pass the todo to the `DetailScreen`.

上面写完了 `DetailScreen` ，现在该执行界面跳转啦！
我们想让用户在点击列表中的某个待办事项时跳转到 `DetailScreen` 界面，
同时能传递点击的这条代办事项对象（`Todo` 对象） 。

To capture the user's tap in the `TodosScreen`, write an [`onTap()`][]
callback for the `ListTile` widget. Within the `onTap()` callback,
use the [`Navigator.push()`][] method.

想要获取到用户在 `TodosScreen` 的点击事件，
我们来编写 `ListTile` widget 的 `onTap()` 回调函数，
继续使用 [`Navigator.push()`][] 方法。

<?code-excerpt "lib/main.dart (builder)"?>
```dart
body: ListView.builder(
  itemCount: todos.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(todos[index].title),
      // When a user taps the ListTile, navigate to the DetailScreen.
      // Notice that you're not only creating a DetailScreen, you're
      // also passing the current todo through to it.
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute<void>(
            builder: (context) => DetailScreen(todo: todos[index]),
          ),
        );
      },
    );
  },
),
```

### Interactive example

### 交互式样例

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

class Todo {
  final String title;
  final String description;

  const Todo(this.title, this.description);
}

void main() {
  runApp(
    MaterialApp(
      title: 'Passing Data',
      home: TodosScreen(
        todos: List.generate(
          20,
          (i) => Todo(
            'Todo $i',
            'A description of what needs to be done for Todo $i',
          ),
        ),
      ),
    ),
  );
}

class TodosScreen extends StatelessWidget {
  const TodosScreen({super.key, required this.todos});

  final List<Todo> todos;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Todos')),
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(todos[index].title),
            // When a user taps the ListTile, navigate to the DetailScreen.
            // Notice that you're not only creating a DetailScreen, you're
            // also passing the current todo through to it.
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute<void>(
                  builder: (context) => DetailScreen(todo: todos[index]),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

class DetailScreen extends StatelessWidget {
  // In the constructor, require a Todo.
  const DetailScreen({super.key, required this.todo});

  // Declare a field that holds the Todo.
  final Todo todo;

  @override
  Widget build(BuildContext context) {
    // Use the Todo to create the UI.
    return Scaffold(
      appBar: AppBar(title: Text(todo.title)),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Text(todo.description),
      ),
    );
  }
}
```

## Alternatively, pass the arguments using RouteSettings

## 或者使用 RouteSettings 传递参数

Repeat the first two steps.

重复前面两个步骤。

### Create a detail screen to extract the arguments

### 创建一个详情页以提取参数

Next, create a detail screen that extracts and displays the title and description from the `Todo`. To access the `Todo`, use the [`ModalRoute.of()`][] method. This method returns the current route with the arguments.

接下来，创建一个详情页用于提取并显示来自 `Todo` 页面的标题和描述信息。
为了访问 `Todo` 页面，请使用 `ModalRoute.of()` 方法。
它将会返回带有参数的当前路由。

<?code-excerpt "lib/main_routesettings.dart (DetailScreen)"?>
```dart
class DetailScreen extends StatelessWidget {
  const DetailScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final todo = ModalRoute.of(context)!.settings.arguments as Todo;

    // Use the Todo to create the UI.
    return Scaffold(
      appBar: AppBar(title: Text(todo.title)),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Text(todo.description),
      ),
    );
  }
}
```

### Navigate and pass the arguments to the detail screen

### 导航并向详情页传递参数

Finally, navigate to the `DetailScreen` when a user taps
a `ListTile` widget using `Navigator.push()`.
Pass the arguments as part of the [`RouteSettings`][].
The `DetailScreen` extracts these arguments.

最后，当用户点击 `ListTile` widget 时，
使用 `Navigator.push()` 导航到 `DetailScreen`。
将参数作为 [`RouteSettings`][] 的一部分进行传递，
`DetailScreen` 将会提取这些参数。

<?code-excerpt "lib/main_routesettings.dart (builder)" replace="/^body: //g;/^\),$/)/g"?>
```dart
ListView.builder(
  itemCount: todos.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(todos[index].title),
      // When a user taps the ListTile, navigate to the DetailScreen.
      // Notice that you're not only creating a DetailScreen, you're
      // also passing the current todo through to it.
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute<void>(
            builder: (context) => const DetailScreen(),
            // Pass the arguments as part of the RouteSettings. The
            // DetailScreen reads the arguments from these settings.
            settings: RouteSettings(arguments: todos[index]),
          ),
        );
      },
    );
  },
)
```

### Complete example

### 完整样例

<?code-excerpt "lib/main_routesettings.dart"?>
```dart
import 'package:flutter/material.dart';

class Todo {
  final String title;
  final String description;

  const Todo(this.title, this.description);
}

void main() {
  runApp(
    MaterialApp(
      title: 'Passing Data',
      home: TodosScreen(
        todos: List.generate(
          20,
          (i) => Todo(
            'Todo $i',
            'A description of what needs to be done for Todo $i',
          ),
        ),
      ),
    ),
  );
}

class TodosScreen extends StatelessWidget {
  const TodosScreen({super.key, required this.todos});

  final List<Todo> todos;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Todos')),
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(todos[index].title),
            // When a user taps the ListTile, navigate to the DetailScreen.
            // Notice that you're not only creating a DetailScreen, you're
            // also passing the current todo through to it.
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute<void>(
                  builder: (context) => const DetailScreen(),
                  // Pass the arguments as part of the RouteSettings. The
                  // DetailScreen reads the arguments from these settings.
                  settings: RouteSettings(arguments: todos[index]),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

class DetailScreen extends StatelessWidget {
  const DetailScreen({super.key});

  @override
  Widget build(BuildContext context) {
    final todo = ModalRoute.of(context)!.settings.arguments as Todo;

    // Use the Todo to create the UI.
    return Scaffold(
      appBar: AppBar(title: Text(todo.title)),
      body: Padding(
        padding: const EdgeInsets.all(16),
        child: Text(todo.description),
      ),
    );
  }
}
```

<noscript>
  <img src="/assets/images/docs/cookbook/passing-data.webp" alt="Passing Data Demo" class="site-mobile-screenshot" />
</noscript>


[`ModalRoute.of()`]: https://api.flutter-io.cn/flutter/widgets/ModalRoute/of.html
[`Navigator.push()`]: https://api.flutter-io.cn/flutter/widgets/Navigator/push.html
[`onTap()`]: https://api.flutter-io.cn/flutter/material/ListTile/onTap.html
[`RouteSettings`]: https://api.flutter-io.cn/flutter/widgets/RouteSettings-class.html
[Use lists]: /cookbook/lists/basic-list

