Flutter学习笔记之-路由和导航
在iOS原生开发中页面的跳转是通过UINavigationController来实现的,而Flutter中页面的跳转是通过Navigator和Route来实现的。
- 页面的基本跳转案例
案例包括页面跳转到下一个页面,返回上一个页面。
// 第一个页面,通过点击按钮跳转
class DemoWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FlatButton(
child: Text("跳转下一个页面"),
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) {
return SecondPage();
})
);
},
);
}
}
// 第二个页面,点击按钮返回上一个页面
class SecondPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("第二个页面")
),
body: Center(
child: Column(children: <Widget>[
Text("第二个页面"),
FlatButton(child: Text("返回上一个页面"), onPressed: () => Navigator.pop(context),)
], mainAxisAlignment: MainAxisAlignment.center,
),
)
);
}
}
- 页面之间跳转参数的传递
参数的传递包括:
1、上一个页面向下一个页面传递参数
2、返回上一个页面传递参数
方法一:通过下一个页面的构造函数传递参数
class DemoWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return FlatButton(
child: Text("跳转下一个页面"),
onPressed: () {
Future result = Navigator.of(context).push(
MaterialPageRoute(builder: (context) {
// 采用构造函数的方式传递参数
return SecondPage("首页传递的数据");
})
);
// 这里接收pop回来的数据
result.then((value) {
print("接收pop回来的值 $value");
});
},
);
}
}
class SecondPage extends StatelessWidget {
final String name;
SecondPage(this.name);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("第二个页面")
),
body: Center(
child: Column(children: <Widget>[
Text("第二个页面 - 接收首页传递的参数 name = ${this.name}"),
FlatButton(child: Text("返回上一个页面"), onPressed: () {
_backLastPage(context);
},)
], mainAxisAlignment: MainAxisAlignment.center,
),
)
);
}
void _backLastPage(BuildContext context) {
// 向上一个页面pop返回参数
Navigator.pop(context, "返回上一个页面 参数 value = 3");
}
}
- 默认返回按钮的修改以及事件的监听
由上图可以知道push到写一个页面的时候,第二个页面默认会有一个返回按钮,
那么问题来了:
1、如何修改默认的返回按钮样式呢?
2、如何监听当前页面pop事件呢(或者叫点击了默认返回按钮截取这个事件)?
1、首先来修改默认返回按钮的样式和点击事件。
这里利用AppBar中leading替换原有的默认返回按钮并编写自己的事件。
class SecondPage extends StatelessWidget {
final String name;
SecondPage(this.name);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("第二个页面"),
// 替换原有的返回按钮事件
leading: FlatButton(
child: Icon(Icons.arrow_back, color: Colors.white,),
onPressed: () => _backLastPage(context),
),
),
body: Center(
child: Column(children: <Widget>[
Text("第二个页面 - 接收首页传递的参数 name = ${this.name}"),
FlatButton(child: Text("返回上一个页面"), onPressed: () {
_backLastPage(context);
},)
], mainAxisAlignment: MainAxisAlignment.center,
),
)
);
}
void _backLastPage(BuildContext context) {
print("_backLastPage");
Navigator.pop(context, "返回上一个页面 value = 3");
}
}
这里将默认的返回按钮修改成一个箭头按钮了.
2、如果任然使用默认的返回按钮,那么怎么样来截获点击按钮之后的事件处理呢?
这里将使用一个WillPopScope的widget来实现。当页面widget将要pop的时候就会触发
WillPopScope中的onWillPop方法。
class SecondPage extends StatelessWidget {
final String name;
SecondPage(this.name);
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () {
// 监听pop事件
_backLastPage(context);
return Future.value(true);
},
child: Scaffold(
appBar: AppBar(
title: Text("第二个页面"),
),
body: Center(
child: Column(children: <Widget>[
Text("第二个页面 - 接收首页传递的参数 name = ${this.name}"),
FlatButton(child: Text("返回上一个页面"), onPressed: () {
_backLastPage(context);
},)
], mainAxisAlignment: MainAxisAlignment.center,
),
)
),
);
}
void _backLastPage(BuildContext context) {
print("_backLastPage");
Navigator.pop(context, "返回上一个页面 value = 3");
}
}
- 命名路由的基本使用
// 首先定义路由映射规则
class RouterConfig {
static const HomePageRouteName = "/home";
static const LoginPageRouteName = "/login";
static const MainPageRouteName = "/main";
static const OtherPageRouteName = "/other";
static final Map<String, WidgetBuilder> routesMap = {
RouterConfig.MainPageRouteName: (context) => MainPage(),
RouterConfig.HomePageRouteName: (context) => HomePage(),
RouterConfig.LoginPageRouteName: (context) => LoginPage(),
RouterConfig.OtherPageRouteName: (context) => OtherPage()
};
}
// 在MaterialApp中设置路由规则和初始化路由
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
key: UniqueKey(),
title: "Welcome to Flutter",
initialRoute: RouterConfig.MainPageRouteName,
routes: RouterConfig.routesMap,
);
}
}
// 利用路由名称跳转下一个页面
class MainPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Welcome to Flutter")
),
body: Center(
child: FlatButton(
child: Text("跳转下一个页面"),
onPressed: () {
Navigator.pushNamed(context, RouterConfig.HomePageRouteName);
},
),
),
);
}
}
- 命名路由参数的传递
// Navigator.pushNamed 携带参数
class MainPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Welcome to Flutter")
),
body: Center(
child: FlatButton(
child: Text("跳转下一个页面"),
onPressed: () {
Navigator.pushNamed(context, RouterConfig.HomePageRouteName, arguments: "命名路由的参数传递");
},
),
),
);
}
}
// 在HomePage中接收传递过来额参数
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
// 获取命名路由传递过来的参数
Object arguments = ModalRoute.of(context).settings.arguments;
String name = ModalRoute.of(context).settings.name;
print("获取传递过来的参数 arguments = $arguments - name = $name");
return Scaffold(
appBar: AppBar(
title: Text("Home Page"),
),
body: Center(
child: Text("home"),
),
);
}
}
上面传递的方式是HomePage页面没有带参数的构造方式,如果HomePage只提供了带参数的构造函数,那么在MaterialApp中构建routes时创建HomePage就不太合适了。
下面就利用onGenerateRoute函数来解决这种情况。
// 跳转方式不用变化,依然采用这种方式
Navigator.pushNamed(context, RouterConfig.HomePageRouteName, arguments: "命名路由的参数传递");
// 在构造routes就需要做一些改变了
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
key: UniqueKey(),
title: "Welcome to Flutter",
initialRoute: RouterConfig.MainPageRouteName,
routes: RouterConfig.routesMap,
// 核心修改点
onGenerateRoute: (setting) {
if (setting.name == RouterConfig.HomePageRouteName) {
return MaterialPageRoute(builder: (context) {
// 采用构造函数的方式传递参数
return HomePage(setting.arguments);
});
}
return null;
},
);
}
}