title | description | tags | keywords | js | |||||
---|---|---|---|---|---|---|---|---|---|
传递数据到新页面 |
如何向新路由传递数据。 |
cookbook, 实用教程, 路由 |
传参,待办事项应用 |
|
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), 在新的一屏显示待办事项的详细信息。
-
Define a todo class.
定义一个描述待办事项的数据类
-
Display a list of todos.
显示待办事项
-
Create a detail screen that can display information about a todo.
创建一个显示待办事项详细信息的界面
-
Navigate and pass data to the detail screen.
传递数据并跳转到待办事项详细信息界面
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
两个成员变量。
class Todo {
final String title;
final String description;
const Todo(this.title, this.description);
}
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
显示。
如果你想了解更多关于列表显示的内容,
请阅读文档 基础列表
。
final todos = List.generate(
20,
(i) => Todo(
'Todo $i',
'A description of what needs to be done for Todo $i',
),
);
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
把它显示出来了。
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()
方法,
这将会把列表渲染到屏幕上供你继续下一步。
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 自带的样式,未来会变得很轻松。
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 。
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),
),
);
}
}
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()
方法。
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(
builder: (context) => DetailScreen(todo: todos[index]),
),
);
},
);
},
),
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(
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),
),
);
}
}
Repeat the first two steps.
重复前面两个步骤。
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()
方法。
它将会返回带有参数的当前路由。
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),
),
);
}
}
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
将会提取这些参数。
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(
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]),
),
);
},
);
},
)
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(
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),
),
);
}
}