在 flutter 中,路由(Route)与导航器(Navigator)搭配在一起,但能够实现页面的跳转。
导航器(Navigator)
路由(Route)对应着一个一个页面,页面的之间的切换便是由导航器(Navigator)控件管理。导航器管理着一个页面堆栈,在 MaterialApp 应用中, home 对应的页面则为最底层。 导航器(Navigator)通过提供的 push 和 pop 等方法来实现页面的切换。
1 2 3 4 5 6 7 8 9
| Future push( BuildContext context, Route route, )
bool pop( BuildContext context, [result], )
|
在页面中通过使用 Nabigator.of(context) 来调用导航器。
返回值
当路由被推送,用于获取用户的输入时,可以通过 pop 方法的 result 参数返回用户的输入值。推送路由的方法会返回 Future 类型, Future 会在该路由被弹出式解析,其值位 pop 方法返回的 result 参数。若未传参则值为 null 。
1 2 3 4 5 6 7 8 9 10
| bool value = await Navigator.of(context).push(MaterialPageRoute<bool>( builder: (BuildContext context) { return Center( child: GestureDetector( child: Text("确定"), onTap: () { Navigator.of(context).pop(true); }, ), ); } ));
|
弹出路由
路由不一定需要掩盖整个屏幕,PopupRoutes 覆盖屏幕,屏幕颜色只能部分不透明,以允许当前屏幕显示。弹出路由是模态的,因为它们阻止了下面的控件的输入。
框架有创建和显示弹出路由的功能,例如:showDialog、showMenu 和 showModalBottomSheet。这些功能如上所述返回其推送的路由的 Future ,调用时可以等待返回的值在路由弹出时采取行动,或者发现路由的值。
还有一些创建弹出路由的控件,如PopupMenuButton和DropdownButton。这些控件创建PopupRoute的内部子类,并使用路由的push和pop方法来显示和关闭它们。
1 2 3 4 5 6 7 8 9 10 11 12
| Future<Null> fun() async { bool value = await showDialog( context: 'context', barrierDismissible: true, child: new Center( child: new GestureDetector( child: new Text("sure"), onTap: () { Navigator.of(context).pop(true); }, ), ) ); }
|
路由(Route)
路由(Route)分为两种,一种是静态路由,另一种是动态路由。
静态路由
所谓静态路由就是在 flutter 初始化时, flutter 就知道该应用存在那些路由。这些路由定义在 MaterialApp 控件的 routers 属性中。
routers 需要 Map 类型,第一个参数是路由名称,第二个参数是对应的页面。
下面是以简单的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| void main() => runApp(MyApp());
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: 'Welcome to Flutter', home: Page1(), routes: <String, WidgetBuilder>{'/page2': (_) => Page2('from page1')}, ); } }
class Page1 extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Page1"), ), body: Center( child: RaisedButton( onPressed: () { Navigator.of(context).pushNamed("/page2"); }, child: Text("点我跳转"), ), ), ); } }
class Page2 extends StatelessWidget { Page2(this.content);
final String content;
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Page2"), ), body: Center( child: Text(content), ), ); } }
|
从示例中可以看到我们在定义路由的时候是可以参入参数的,但是这种静态路由的参数是固定的,也就是说我们需要在定义的时候就把参数传递进去,若是参数需要动态进行获取那么这种静态路由可能就不太适合了。
在定义路由的时候,我们可以直接把路由写在 routers 参数后面,如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( ... routes: <String, WidgetBuilder> { '/page1': Page1(), '/page2': Page2(), '/page3': Page3(), }, ... ); } }
|
也可采用官方的路由统一管理的方式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| class Page2 extends StatelessWidget { static const String routeName = '/page2';
Page2(this.content);
final String content;
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text("Page2"), ), body: Center( child: Text(content), ), ); } }
List<RouteData> routerList = <RouteData>[ RouteData( routeName: Page1.routeName, buildRoute: (BuildContext context) => Page1(), ), RouteData( routeName: Page2.routeName, buildRoute: (BuildContext context) => Page2('from somewhere'), ), ];
class RouteData { const RouteData({@required this.routeName, @required this.buildRoute});
final String routeName; final WidgetBuilder buildRoute;
@override String toString() { return '$runtimeType($routeName $routeName)'; } }
class MyApp extends StatelessWidget { Map<String, WidgetBuilder> _createRouter() { return Map<String, WidgetBuilder>.fromIterable( routerList, key: (dynamic route) => '${route.routeName}', value: (dynamic route) => route.buildRoute, ); }
...
@override Widget build(BuildContext context) { return MaterialApp( ... routes: _createRouter(), ... ); } }
|
动态路由
在导航器(Navigator)的 push 方法需要传入一个 Route 对象,我们可是使用 PageRouteBuilder 方法来创建这个对象。
1 2 3 4 5 6 7 8 9 10 11 12
| Navigator.push( context, PageRouteBuilder( pageBuilder: ( BuildContext context, _, __, ) { return Page(); }, ), );
|
这样的路由我们就可以进行动态传参,根据不同的状态传递不同的参数到路由中。
这种路由方式我们可以自己定义路由动画。在 PageRouteBuilder 中有一个 transitionsBuilder 属性,可以定义动画。
1 2 3 4 5 6 7 8
| ... transitionsBuilder: (_, Animation<double> animation, __, Widget child) { return FadeTransition( opacity: animation, child: child, ); }, ...
|