管理多个页面时有两个核心概念和类:Route和 Navigator。
一个route是一个屏幕或页面的抽象,Navigator是管理route的Widget。
Navigator可以通过route入栈和出栈来实现页面之间的跳转。
Flutter的路由有两种方式
基本路由就相当于Android和iOS原生的页面跳转方式。
命名路由就相当于VUE的Router插件一样,这种方式耦合性更低,功能更强大。
在一个项目中两种方式是可以同时使用的,推荐使用命名路由的方式,项目的结构看起来比较清晰。
## 基本路由
Navigator.push(context,
MaterialPageRoute(builder: (context) {
return HomePage();
}));
适用于登录后跳转到主页面 不能再返回到登录页面
Navigator.pushReplacement(context,
MaterialPageRoute(builder: (context) {
return HomePage();
}));
Navigator.pop(context);
除了页面关闭用这个方法,窗口的关闭也是用这个方法,因为Flutter的Dialog的实现方式就是基于路由的。
### 返回根路由
Navigator.of(context).pushAndRemoveUntil(
new MaterialPageRoute(builder: (context) => new HomePage()),
(route) => route == null
);
页面
class MyHomePage extends StatefulWidget {
// 类的构造器,用来接收传递的值
MyHomePage({Key key, this.title}) : super(key: key);
final String title; // 用来储存传递过来的值
@override
_MyHomePageState createState() => new _MyHomePageState();
}
传入值的方式
new MyHomePage(title: '带参数跳转')
## 命名路由
路由定义
import 'package:flutter/material.dart';
import 'package:qggj_android/page/HomePage.dart';
import 'package:qggj_android/page/LoginPage.dart';
final Map<String, Function> routes = {
'/': (context) => LoginPage(),
'/home': (context) => HomePage(),
'/kaoqin': (context) => KaoqinPage(),
'/kaoqin_detail': (context) => KaoqinDetailPage(),
'/kaoqin_history': (context) => KaoqinHistoryPage(),
};
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;
}
}
};
初始化路由
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);
}
}
不带参数
Navigator.pushNamed(context, "/home");
带参数
Navigator.pushNamed(context, '/home', arguments: {"id": 20});
Navigator.pushReplacementNamed(context, "/home");
Navigator.pop(context);
除了页面关闭用这个方法,窗口的关闭也是用这个方法,因为Flutter的Dialog的实现方式就是基于路由的。
### 返回根路由
//flutter 登录后跳转到根路由
Navigator.of(context).pushNamedAndRemoveUntil(
'/home',
(route) => false,//true 保留当前栈 false 销毁所有 只留下HomePage
arguments: {
},
);
接收参数页面
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'] : ''}"),
);
}
}
跳转传参
//路由跳转
Navigator.pushNamed(context, '/search',arguments: {
"keyword":"资讯"
});
为了避免用户误触返回按钮而导致APP退出,在很多APP中都拦截了用户点击返回键的按钮,然后进行一些防误触判断,比如当用户在某一个时间段内点击两次时,才会认为用户是要退出(而非误触)。Flutter中可以通过WillPopScope
来实现返回按钮拦截,我们看看WillPopScope
的默认构造函数:
const WillPopScope({
...
@required WillPopCallback onWillPop,
@required Widget child
})
onWillPop
是一个回调函数,当用户点击返回按钮时被调用(包括导航返回按钮及Android物理返回按钮)。该回调需要返回一个Future
对象,如果返回的Future
最终值为false
时,则当前路由不出栈(不会返回);最终值为true
时,当前路由出栈退出。我们需要提供这个回调来决定是否退出。
示例
为了防止用户误触返回键退出,我们拦截返回事件。当用户在1秒内点击两次返回按钮时,则退出;如果间隔超过1秒则不退出,并重新记时。
代码如下:
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)) {
//两次点击间隔超过1秒则重新计时
_lastPressedAt = DateTime.now();
return false;
}
return true;
},
child: Scaffold(
body: Text("这是登录页面"),
)
);
}
}