前言
管理多个页面时有两个核心概念和类:Route和 Navigator。
一个route是一个屏幕或页面的抽象,Navigator是管理route的Widget。
Navigator可以通过route入栈和出栈来实现页面之间的跳转。
Flutter的路由有两种方式
基本路由就相当于Android和iOS原生的页面跳转方式。
命名路由就相当于VUE的Router插件一样,这种方式耦合性更低,功能更强大。
在一个项目中两种方式是可以同时使用的,推荐使用命名路由的方式,项目的结构看起来比较清晰。
基本路由
跳转
1 2 3 4
| Navigator.push(context, MaterialPageRoute(builder: (context) { return HomePage(); }));
|
替换形式跳转
适用于登录后跳转到主页面 不能再返回到登录页面
1 2 3 4
| Navigator.pushReplacement(context, MaterialPageRoute(builder: (context) { return HomePage(); }));
|
关闭页面
除了页面关闭用这个方法,窗口的关闭也是用这个方法,因为Flutter的Dialog的实现方式就是基于路由的。
返回根路由
1 2 3 4
| Navigator.of(context).pushAndRemoveUntil( new MaterialPageRoute(builder: (context) => new HomePage()), (route) => route == null );
|
路由传值
页面
1 2 3 4 5 6 7 8
| class MyHomePage extends StatefulWidget { MyHomePage({Key key, this.title}) : super(key: key); final String title;
@override _MyHomePageState createState() => new _MyHomePageState(); }
|
传入值的方式
1
| new MyHomePage(title: '带参数跳转')
|
命名路由
路由定义与初始化
路由定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import 'package:flutter/material.dart';
final Map<String, Function> routes = { '/': (context) => LoginPage(), '/home': (context) => HomePage(), '/search': (context,{arguments}) => SearchPage(arguments: arguments), };
var onGenerateRoute = (RouteSettings settings) { final String name = settings.name; final Function pageContentBuilder = routes[name]; if (pageContentBuilder != null) { if (settings.arguments != null) { final Route route = MaterialPageRoute( builder: (context) => pageContentBuilder(context, arguments: settings.arguments)); return route; } else { final Route route = MaterialPageRoute(builder: (context) => pageContentBuilder(context)); return route; } } };
|
两种路由
初始化路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| import 'package:flutter/material.dart'; import 'route/MyRoutes.dart';
void main() { runApp(MyApp()); }
class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( title: '', debugShowCheckedModeBanner: false, theme: ThemeData( primarySwatch: Colors.blue, visualDensity: VisualDensity.adaptivePlatformDensity, ), initialRoute: '/', onGenerateRoute: onGenerateRoute); } }
|
路由传值
接收参数页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import 'package:flutter/material.dart';
class SearchPage extends StatelessWidget { final arguments; SearchPage({this.arguments});
@override Widget build(BuildContext context) { return Scaffold( appBar:AppBar( title: Text("搜索页面"), ) , body: Text("搜索页面内容区域${arguments != null ? arguments['keyword'] : ''}"), ); } }
|
跳转传参
1 2 3 4
| Navigator.pushNamed(context, '/search',arguments: { "keyword":"资讯" });
|
页面跳转
不带参数
1
| Navigator.pushNamed(context, "/home");
|
带参数
1
| Navigator.pushNamed(context, '/search', arguments: {"id": 20});
|
替换形式跳转
1
| Navigator.pushReplacementNamed(context, "/home");
|
关闭页面
除了页面关闭用这个方法,窗口的关闭也是用这个方法,因为Flutter的Dialog的实现方式就是基于路由的。
返回根路由
1 2 3 4 5 6 7
| Navigator.of(context).pushNamedAndRemoveUntil( '/home', (route) => false, arguments: { }, );
|
导航返回拦截
为了避免用户误触返回按钮而导致APP退出,在很多APP中都拦截了用户点击返回键的按钮,然后进行一些防误触判断,比如当用户在某一个时间段内点击两次时,才会认为用户是要退出(而非误触)。Flutter中可以通过WillPopScope来实现返回按钮拦截,我们看看WillPopScope的默认构造函数:
1 2 3 4 5
| const WillPopScope({ ... @required WillPopCallback onWillPop, @required Widget child })
|
onWillPop是一个回调函数,当用户点击返回按钮时被调用(包括导航返回按钮及Android物理返回按钮)。该回调需要返回一个Future对象,如果返回的Future最终值为false时,则当前路由不出栈(不会返回);最终值为true时,当前路由出栈退出。我们需要提供这个回调来决定是否退出。
示例
为了防止用户误触返回键退出,我们拦截返回事件。当用户在1秒内点击两次返回按钮时,则退出;如果间隔超过1秒则不退出,并重新记时。
代码如下:
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
| import 'package:flutter/material.dart';
class LoginPage extends StatefulWidget { @override LoginPageState createState() { return new LoginPageState(); } }
class LoginPageState extends State<LoginPage> { DateTime _lastPressedAt;
@override Widget build(BuildContext context) { return new WillPopScope( onWillPop: () async { if (_lastPressedAt == null || DateTime.now().difference(_lastPressedAt) > Duration(seconds: 1)) { _lastPressedAt = DateTime.now(); return false; } return true; }, child: Scaffold( body: Text("这是登录页面"), ) ); } }
|