向新螢幕傳送資料
如何向新路由傳遞資料。
通常,你不僅需要導航到新螢幕,還需要將資料傳遞給該螢幕。例如,你可能需要傳遞關於被點選條目的資訊。
請記住:螢幕只是 Widget。在此示例中,建立一個待辦事項列表。當點選某個待辦事項時,導航到一個顯示該待辦事項資訊的新螢幕(Widget)。本方案包含以下步驟:
- 定義一個待辦事項類。
- 顯示待辦事項列表。
- 建立一個能夠顯示待辦事項資訊的詳情螢幕。
- 導航並向詳情螢幕傳遞資料。
1. 定義一個待辦事項(todo)類
#首先,你需要一種簡單的方式來表示待辦事項。對於此示例,建立一個包含兩部分資料的類:標題和描述。
class Todo {
final String title;
final String description;
const Todo(this.title, this.description);
}
2. 建立待辦事項列表
#其次,顯示待辦事項列表。在此示例中,生成 20 個待辦事項並使用 ListView 顯示它們。有關處理列表的更多資訊,請參閱使用列表方案。
生成待辦事項列表
#final todos = List.generate(
20,
(i) => Todo(
'Todo $i',
'A description of what needs to be done for Todo $i',
),
);
使用 ListView 顯示待辦事項列表
#ListView.builder(
itemCount: todos.length,
itemBuilder: (context, index) {
return ListTile(title: Text(todos[index].title));
},
)
到目前為止,一切順利。這生成了 20 個待辦事項並將它們顯示在 ListView 中。
3. 建立一個用於顯示列表的待辦事項螢幕
#為此,我們建立一個 StatelessWidget,將其命名為 TodosScreen。由於此頁面的內容在執行時不會改變,我們需要在這個 Widget 的作用域內要求傳入待辦事項列表。
我們將 ListView.builder 作為我們返回給 build() 方法的 Widget 的 body 傳入。這會將列表渲染到螢幕上,讓你開始工作!
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));
},
),
);
}
}
有了 Flutter 的預設樣式,你無需為以後想做的事情而操心,直接使用即可!
4. 建立一個用於顯示待辦事項詳細資訊的詳情螢幕
#現在,建立第二個螢幕。螢幕標題包含待辦事項的標題,螢幕主體顯示其描述。
由於詳情螢幕是一個普通的 StatelessWidget,要求使用者在 UI 中傳入一個 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),
),
);
}
}
5. 導航並向詳情螢幕傳遞資料
#有了 DetailScreen,你就可以執行導航了。在此示例中,當用戶點選列表中的待辦事項時,導航到 DetailScreen。將該待辦事項傳遞給 DetailScreen。
為了在 TodosScreen 中捕獲使用者的點選,為 ListTile Widget 編寫一個 onTap() 回撥。在 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<void>(
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<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),
),
);
}
}
或者,使用 RouteSettings 傳遞引數
#重複前兩個步驟。
建立詳情螢幕以提取引數
#接下來,建立一個從 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),
),
);
}
}
導航並將引數傳遞給詳情螢幕
#最後,當用戶點選 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<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]),
),
);
},
);
},
)
完整示例
#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),
),
);
}
}