傳遞資料到新頁面
在開發的過程中,我們經常需要在跳轉到新頁面的時候,能同時傳遞一些資料。比如,傳遞使用者點選的元素訊息。
還記得麼,全屏的介面也只是 widget。在這個例子中,我們會建立一個待辦事項清單,當某個事項被點選的時候,會跳轉到新的一屏 (widget),在新的一屏顯示待辦事項的詳細訊息。
-
定義一個描述待辦事項的資料類
-
顯示待辦事項
-
建立一個顯示待辦事項詳細訊息的介面
-
傳遞資料並跳轉到待辦事項詳細訊息介面
1. 定義一個描述待辦事項的資料類
首先,我們需要一個簡單的方式來描述待辦事項。我們建立一個類叫做 Todo
,包含 title
和 description
兩個成員變數。
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. 建立一個待辦頁面顯示待辦事件清單
為了實現這個,我們要建立一個無狀態的 widget (StatelessWidget
),我們叫它 TodosScreen
。因為這個頁面在執行時內容並不會變動,在這個 widget 的 scope 裡,我們會把這個待辦事項的陣列設定為必須 (加入 @require 限定符)。
我們把 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),
);
},
),
);
}
}
使用 Flutter 自帶的樣式,未來會變得很輕鬆。
4. 建立一個顯示待辦事項詳細訊息的介面
現在,我們來建立第二個全屏的介面,介面的標題是待辦事項的標題,介面下面顯示待辦事項的描述訊息。
這個介面是一個 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),
),
);
}
}
5. 傳遞資料並跳轉到待辦事項詳細訊息介面
上面寫完了 DetailScreen
,現在該執行介面跳轉啦!我們想讓使用者在點選清單中的某個待辦事項時跳轉到 DetailScreen
介面,同時能傳遞點選的這條代辦事項物件(Todo
物件)。
想要獲取到使用者在 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),
),
);
}
}
或者使用 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(
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),
),
);
}
}