Flutter路由官方文档学习笔记

在Flutter中,screen和pages都叫做路由(routes)。一个route就是一个widget,使用Navigator导航到新的一个route。
1.导航到新的页面并返回 两个route之间的导航由以下步骤:

  • 创建两个route
  • 使用Navigator.push()导航到第二个route
  • 使用Navigator.pop()返回到第一个route
1.1 创建两个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()导航到第二个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进行导航 上面通过使用创建route并将其推送到导航器来导航到新的页面。
如果你的应用中需要由很多地方导航到同一个页面,如果在使用以上的方法那么代码就会重复。解决办法就是定义命名路由,通过命名路由进行导航。
使用命名路由步骤:
  • 创建两个页面
  • 定义routes
  • 使用Navigator.pushNamed()导航到第二个页面
  • 使用Navigator.pop()返回到第一个页面
2.1 创建两个页面 代码同上1.1
2.2 定义routes 通过MaterialApp构造函数提供的附加属性来定义routes:initialRoutes和routes它本身。
initialRoutes的属性定义应用程序从哪个route开始。
routes属性定义可用的命名路由以及导航到这些路由时要构建的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()导航到第二个页面
// 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.将参数传递给命名路由 Navigator提供了使用公共标识符从应用程序的任何地方传递给命名路由。
使用Navigator.pushNamed()方法的arguments参数完成参数传递。
以下步骤将参数传递给命名路由并使用ModalRoute.of()和onGenerateRoute()读取参数:
  • 定义需要传递的参数。
  • 创建一个提取参数的widget。
  • 在路由表中注册widget。
  • 导航到widget。
3.1 定义需要传递的参数
// 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 创建一个提取参数的widget 创建一个widget从ScreenArguments获取并显示title和message。要访问ScreenArguments,请使用ModalRoute.of()方法。此方法返回带有参数的当前路由。
// 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 导航到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提取参数 也可以在onGenerateRoute()函数中提取参数并将它们传递给widget,而不是直接在窗口小部件中提取参数。
onGenerateRoute()函数根据给定的RouteSettings创建正确的路由。
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. 带数据从页面返回 有些时候我们需要从打开页面带数据返回到导航页面。
使用Navigator.pop()方法带回数据的步骤:
  • 定义主屏幕
  • 添加一个启动选择屏幕的按钮
  • 使用两个按钮显示选择屏幕
  • 点击按钮时,关闭选择屏幕
  • 在主屏幕上显示选择的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 添加一个启动选择屏幕的按钮 创建SelectionButton按钮,它将做以下两件事:
  • 点击时打开选中页面
  • 等待选中页面返回结果
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 使用两个按钮显示选择屏幕 现在,构建一个包含两个按钮的选择屏幕。当用户点击按钮时,该应用关闭选择屏幕并让主屏幕知道点击了哪个按钮。
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: [ 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 点击按钮时,关闭选择屏幕 现在,更新两个按钮的onPressed()回调。要将数据返回到第一个屏幕,请使用Navigator.pop()方法,该方法接受名为result的可选第二个参数。任何结果都会在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 现在您正在启动选择屏幕并等待结果,您将需要对返回的信息执行某些操作。
_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. 将数据发送到新页面 通常,您不仅要导航到新屏幕,还要将数据传递到屏幕。例如,您可能希望传递有关已点击的项目的信息
请记住:屏幕只是小部件。在此示例中,创建待办事项列表。轻触待办事项时,导航到显示有关待办事项信息的新屏幕(widget)。此配方使用以下步骤:
  • 定义待办事项类
  • 显示待办事项列表
  • 创建一个可以显示待办事项信息的详细信息屏幕
  • 导航并将数据传递到详细信息屏幕
5.1 定义待办事项类 首先,您需要一种简单的方式来表示待办事项。对于此示例,请创建一个包含两个数据的类:标题和说明。
class Todo { final String title; final String description; Todo(this.title, this.description); }

5.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), ); }, );

5.3 创建一个可以显示待办事项信息的详细信息屏幕 现在,创建第二个屏幕。屏幕标题包含待办事项的标题,屏幕正文显示说明。
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 导航并将数据传递到详细信息屏幕 有了DetailScreen,您就可以执行导航了。在此示例中,当用户点击列表中的待办事项时,导航到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]), ), ); }, ); }, );

【Flutter路由官方文档学习笔记】参考
flutter.dev Navigation
flutter_route sample

    推荐阅读