在Flutter中,screen和pages都叫做路由(routes)。一個route就是一個widget,使用Navigator導(dǎo)航到新的一個route。
1.導(dǎo)航到新的頁面并返回
兩個route之間的導(dǎo)航由以下步驟:
- 創(chuàng)建兩個route
- 使用Navigator.push()導(dǎo)航到第二個route
- 使用Navigator.pop()返回到第一個route
1.1 創(chuàng)建兩個routes
class FirstRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('First Route'),
),
body: Center(
child: RaisedButton(
child: Text('Open route'),
onPressed: () {
// Navigate to second route when tapped.
},
),
),
);
}
}
class SecondRoute extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second Route"),
),
body: Center(
child: RaisedButton(
onPressed: () {
// Navigate back to first route when tapped.
},
child: Text('Go back!'),
),
),
);
}
}
1.2 使用Navigator.push()導(dǎo)航到第二個route
// Within the `FirstRoute` widget
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => SecondRoute()),
);
}
1.3 使用Navigator.pop()返回到第一個route
// Within the SecondRoute widget
onPressed: () {
Navigator.pop(context);
}
2. 使用命名routes進(jìn)行導(dǎo)航
上面通過使用創(chuàng)建route并將其推送到導(dǎo)航器來導(dǎo)航到新的頁面。
如果你的應(yīng)用中需要由很多地方導(dǎo)航到同一個頁面,如果在使用以上的方法那么代碼就會重復(fù)。解決辦法就是定義命名路由,通過命名路由進(jìn)行導(dǎo)航。
使用命名路由步驟:
- 創(chuàng)建兩個頁面
- 定義routes
- 使用Navigator.pushNamed()導(dǎo)航到第二個頁面
- 使用Navigator.pop()返回到第一個頁面
2.1 創(chuàng)建兩個頁面
代碼同上1.1
2.2 定義routes
通過MaterialApp構(gòu)造函數(shù)提供的附加屬性來定義routes:initialRoutes和routes它本身。
initialRoutes的屬性定義應(yīng)用程序從哪個route開始。
routes屬性定義可用的命名路由以及導(dǎo)航到這些路由時要構(gòu)建的widget。
MaterialApp(
// Start the app with the "/" named route. In this case, the app starts
// on the FirstScreen widget.
initialRoute: '/',
routes: {
// When navigating to the "/" route, build the FirstScreen widget.
'/': (context) => FirstScreen(),
// When navigating to the "/second" route, build the SecondScreen widget.
'/second': (context) => SecondScreen(),
},
);
2.3 使用Navigator.pushNamed()導(dǎo)航到第二個頁面
// Within the `FirstScreen` widget
onPressed: () {
// Navigate to the second screen using a named route.
Navigator.pushNamed(context, '/second');
}
2.4 使用Navigator.pop()返回到第一個頁面
代碼同上1.3
3.將參數(shù)傳遞給命名路由
Navigator提供了使用公共標(biāo)識符從應(yīng)用程序的任何地方傳遞給命名路由。
使用Navigator.pushNamed()方法的arguments參數(shù)完成參數(shù)傳遞。
以下步驟將參數(shù)傳遞給命名路由并使用ModalRoute.of()和onGenerateRoute()讀取參數(shù):
- 定義需要傳遞的參數(shù)。
- 創(chuàng)建一個提取參數(shù)的widget。
- 在路由表中注冊widget。
- 導(dǎo)航到widget。
3.1 定義需要傳遞的參數(shù)
// You can pass any object to the arguments parameter.
// In this example, create a class that contains a customizable
// title and message.
class ScreenArguments {
final String title;
final String message;
ScreenArguments(this.title, this.message);
}
3.2 創(chuàng)建一個提取參數(shù)的widget
創(chuàng)建一個widget從ScreenArguments獲取并顯示title和message。要訪問ScreenArguments,請使用ModalRoute.of()方法。此方法返回帶有參數(shù)的當(dāng)前路由。
// A widget that extracts the necessary arguments from the ModalRoute.
class ExtractArgumentsScreen extends StatelessWidget {
static const routeName = '/extractArguments';
@override
Widget build(BuildContext context) {
// Extract the arguments from the current ModalRoute settings and cast
// them as ScreenArguments.
final ScreenArguments args = ModalRoute.of(context).settings.arguments;
return Scaffold(
appBar: AppBar(
title: Text(args.title),
),
body: Center(
child: Text(args.message),
),
);
}
}
3.3 在路由表中注冊widget
MaterialApp(
routes: {
ExtractArgumentsScreen.routeName: (context) => ExtractArgumentsScreen(),
},
);
3.4 導(dǎo)航到widget
// A button that navigates to a named route. The named route
// extracts the arguments by itself.
RaisedButton(
child: Text("Navigate to screen that extracts arguments"),
onPressed: () {
// When the user taps the button, navigate to the specific route
// and provide the arguments as part of the RouteSettings.
Navigator.pushNamed(
context,
ExtractArgumentsScreen.routeName,
arguments: ScreenArguments(
'Extract Arguments Screen',
'This message is extracted in the build method.',
),
);
},
);
使用onGenerateRoute提取參數(shù)
也可以在onGenerateRoute()函數(shù)中提取參數(shù)并將它們傳遞給widget,而不是直接在窗口小部件中提取參數(shù)。
onGenerateRoute()函數(shù)根據(jù)給定的RouteSettings創(chuàng)建正確的路由。
MaterialApp(
// Provide a function to handle named routes. Use this function to
// identify the named route being pushed, and create the correct
// screen.
onGenerateRoute: (settings) {
// If you push the PassArguments route
if (settings.name == PassArgumentsScreen.routeName) {
// Cast the arguments to the correct type: ScreenArguments.
final ScreenArguments args = settings.arguments;
// Then, extract the required data from the arguments and
// pass the data to the correct screen.
return MaterialPageRoute(
builder: (context) {
return PassArgumentsScreen(
title: args.title,
message: args.message,
);
},
);
}
},
);
4. 帶數(shù)據(jù)從頁面返回
有些時候我們需要從打開頁面帶數(shù)據(jù)返回到導(dǎo)航頁面。
使用Navigator.pop()方法帶回數(shù)據(jù)的步驟:
- 定義主屏幕
- 添加一個啟動選擇屏幕的按鈕
- 使用兩個按鈕顯示選擇屏幕
- 點(diǎn)擊按鈕時,關(guān)閉選擇屏幕
- 在主屏幕上顯示選擇的snackbar
4.1 定義主屏幕
class HomeScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Returning Data Demo'),
),
// Create the SelectionButton widget in the next step.
body: Center(child: SelectionButton()),
);
}
}
4.2 添加一個啟動選擇屏幕的按鈕
創(chuàng)建SelectionButton按鈕,它將做以下兩件事:
- 點(diǎn)擊時打開選中頁面
- 等待選中頁面返回結(jié)果
class SelectionButton extends StatelessWidget {
@override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: () {
_navigateAndDisplaySelection(context);
},
child: Text('Pick an option, any option!'),
);
}
// A method that launches the SelectionScreen and awaits the
// result from Navigator.pop.
_navigateAndDisplaySelection(BuildContext context) async {
// Navigator.push returns a Future that completes after calling
// Navigator.pop on the Selection Screen.
final result = await Navigator.push(
context,
// Create the SelectionScreen in the next step.
MaterialPageRoute(builder: (context) => SelectionScreen()),
);
}
}
4.3 使用兩個按鈕顯示選擇屏幕
現(xiàn)在,構(gòu)建一個包含兩個按鈕的選擇屏幕。當(dāng)用戶點(diǎn)擊按鈕時,該應(yīng)用關(guān)閉選擇屏幕并讓主屏幕知道點(diǎn)擊了哪個按鈕。
class SelectionScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Pick an option'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
onPressed: () {
// Pop here with "Yep"...
},
child: Text('Yep!'),
),
),
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
onPressed: () {
// Pop here with "Nope"
},
child: Text('Nope.'),
),
)
],
),
),
);
}
}
4.4 點(diǎn)擊按鈕時,關(guān)閉選擇屏幕
現(xiàn)在,更新兩個按鈕的onPressed()回調(diào)。要將數(shù)據(jù)返回到第一個屏幕,請使用Navigator.pop()方法,該方法接受名為result的可選第二個參數(shù)。任何結(jié)果都會在SelectionButton中返回到Future。
Yep button
RaisedButton(
onPressed: () {
// The Yep button returns "Yep!" as the result.
Navigator.pop(context, 'Yep!');
},
child: Text('Yep!'),
);
Nope button
RaisedButton(
onPressed: () {
// The Nope button returns "Nope!" as the result.
Navigator.pop(context, 'Nope!');
},
child: Text('Nope!'),
);
4.5 在主屏幕上顯示選擇的snackbar
現(xiàn)在您正在啟動選擇屏幕并等待結(jié)果,您將需要對返回的信息執(zhí)行某些操作。
_navigateAndDisplaySelection(BuildContext context) async {
final result = await Navigator.push(
context,
MaterialPageRoute(builder: (context) => SelectionScreen()),
);
// After the Selection Screen returns a result, hide any previous snackbars
// and show the new result.
Scaffold.of(context)
..removeCurrentSnackBar()
..showSnackBar(SnackBar(content: Text("$result")));
}
5. 將數(shù)據(jù)發(fā)送到新頁面
通常,您不僅要導(dǎo)航到新屏幕,還要將數(shù)據(jù)傳遞到屏幕。例如,您可能希望傳遞有關(guān)已點(diǎn)擊的項目的信息
請記?。浩聊恢皇切〔考?。在此示例中,創(chuàng)建待辦事項列表。輕觸待辦事項時,導(dǎo)航到顯示有關(guān)待辦事項信息的新屏幕(widget)。此配方使用以下步驟:
- 定義待辦事項類
- 顯示待辦事項列表
- 創(chuàng)建一個可以顯示待辦事項信息的詳細(xì)信息屏幕
- 導(dǎo)航并將數(shù)據(jù)傳遞到詳細(xì)信息屏幕
5.1 定義待辦事項類
首先,您需要一種簡單的方式來表示待辦事項。對于此示例,請創(chuàng)建一個包含兩個數(shù)據(jù)的類:標(biāo)題和說明。
class Todo {
final String title;
final String description;
Todo(this.title, this.description);
}
5.2 顯示待辦事項列表
其次,顯示待辦事項列表。在此示例中,生成20個待辦事項并使用ListView顯示它們。
生成待辦事項列表
final todos = List<Todo>.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),
);
},
);
5.3 創(chuàng)建一個可以顯示待辦事項信息的詳細(xì)信息屏幕
現(xiàn)在,創(chuàng)建第二個屏幕。屏幕標(biāo)題包含待辦事項的標(biāo)題,屏幕正文顯示說明。
class DetailScreen extends StatelessWidget {
// Declare a field that holds the Todo.
final Todo todo;
// In the constructor, require a Todo.
DetailScreen({Key key, @required this.todo}) : super(key: key);
@override
Widget build(BuildContext context) {
// Use the Todo to create the UI.
return Scaffold(
appBar: AppBar(
title: Text(todo.title),
),
body: Padding(
padding: EdgeInsets.all(16.0),
child: Text(todo.description),
),
);
}
}
5.4 導(dǎo)航并將數(shù)據(jù)傳遞到詳細(xì)信息屏幕
有了DetailScreen,您就可以執(zhí)行導(dǎo)航了。在此示例中,當(dāng)用戶點(diǎn)擊列表中的待辦事項時,導(dǎo)航到DetailScreen。將待辦事項傳遞給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 to it.
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => DetailScreen(todo: todos[index]),
),
);
},
);
},
);