routing stack
There are two very important concepts in Flutter routing management:
- Route: Routing is an abstraction of application pages, corresponding to Activity in Android and ViewController in iOS, managed by Navigator.
- Navigator: Navigator is a component that manages and maintains a stack-based history, and jumps pages through push and pop.
simple example
class _YcHomeBodyState extends State<YcHomeBody> {
Widget build(BuildContext context) {
return Center(
child: ElevatedButton(
onPressed: () {
// 使用PageRouteBuilder来加入淡入淡出的切换效果
Navigator.of(context).push(PageRouteBuilder(
transitionDuration: const Duration(milliseconds: 500),
transitionsBuilder:
(context, animation, secondaryAnimation, child) {
return FadeTransition(
opacity: animation,
child: child,
);
},
pageBuilder: (context, animation, secondaryAnimation) =>
const SecondPage(),
));
},
child: const Text("跳转到B页面"),
),
);
}
}
class SecondPage extends StatelessWidget {
const SecondPage({
Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text("B页面", style: TextStyle(color: Colors.white)),
),
body: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text("返回主页面"),
),
),
);
}
}
named route
Using named routes requires configuring route names in MaterialApp
configuration
MaterialApp(
title: 'Flutter Demo',
routes: <String, WidgetBuilder>{
'/A': (context) => APage(),
'/B': (context) => BPage(),
},
home: Scaffold(
body: APage(),
),
)
jump method
Navigator.of(context).pushNamed('/B');
// 页面替换,一般用于不希望再返回上一个页面,比如欢迎页和登录页
Navigator.of(context).pushReplacementNamed('/B');
// 用于判断路由是否可以出栈
Navigator.of(context).canPop()
// 路由出栈
Navigator.of(context).pop();
// 实现在跳转到新页面的同时,将之前的所有页面都清除掉,只保留当前页面
// 比如:应用程序进入首页,点击登录进入登录页面,然后进入注册页面或者忘记密码页面...,登录成功后进入其他页面,此时不希望返回到登录相关页面,此场景可以使用
Navigator.pushNamedAndRemoveUntil
Global routing switching animation
Use the route provided by the system to switch animations
fade animation
MaterialApp(
routes: <String, WidgetBuilder>{
'/A': (context) => const YcHomePage(),
'/B': (context) => const SecondPage(),
},
// 设置页面过度主题
theme: ThemeData(
pageTransitionsTheme: const PageTransitionsTheme(
builders: {
TargetPlatform.android:FadeUpwardsPageTransitionsBuilder(),
TargetPlatform.iOS:FadeUpwardsPageTransitionsBuilder()
}
)
),
//指定显示哪一个页面
home: const YcHomePage(),
);
}
}
FadeUpwardsPageTransitionsBuilder
: Realize the effect of fading in from bottom to top when switching pages.OpenUpwardsPageTransitionsBuilder
: Used to build page transition animations from bottom to top. In this animation, new pages slide up from the bottom of the screen while old pages fade up.ZoomPageTransitionsBuilder
: Realize the zoom effect when the page is switched.CupertinoPageTransitionsBuilder
: IOS style page switching animation
Routing parameters
Constructor pass parameters
This method cannot be used for the jump method of the named route
Navigator.of(context).push(MaterialPageRoute(builder: (context){
return ProductDetail(productInfo: productInfo,);
}));
class ProductDetail extends StatelessWidget {
final ProductInfo productInfo;
const ProductDetail({
Key key, this.productInfo}) : super(key: key);
Widget build(BuildContext context) {
return Container();
}
}
Ways to set parameters via named routes
Main Page -> B Page
// 发送参数
Navigator.of(context).pushNamed('/B', arguments: '来自主页面');
// 接收参数
Text("B页面-${
ModalRoute.of(context)?.settings.arguments}")
Page B -> Main Page
// 发送参数
Navigator.of(context).pop('从B页面返回');
// 接收参数
ElevatedButton(
onPressed: () async {
String res = await Navigator.of(context).pushNamed('/B', arguments: '来自主页面') as String;
setState((){
title = res.toString();
});
},
child: Text(title),
),
Listen for application routing stack changes
For details, see: Monitor application routing stack changes
Customize RouteObserver
, inherit RouteObserver
and override the methods in it:
class MyRouteObserver<R extends Route<dynamic>> extends RouteObserver<R> {
void didPush(Route route, Route? previousRoute) {
super.didPush(route, previousRoute);
print('didPush route: $route,previousRoute:$previousRoute');
}
void didPop(Route route, Route? previousRoute) {
super.didPop(route, previousRoute);
print('didPop route: $route,previousRoute:$previousRoute');
}
void didReplace({
Route? newRoute, Route? oldRoute}) {
super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
print('didReplace newRoute: $newRoute,oldRoute:$oldRoute');
}
void didRemove(Route route, Route? previousRoute) {
super.didRemove(route, previousRoute);
print('didRemove route: $route,previousRoute:$previousRoute');
}
void didStartUserGesture(Route route, Route? previousRoute) {
super.didStartUserGesture(route, previousRoute);
print('didStartUserGesture route: $route,previousRoute:$previousRoute');
}
void didStopUserGesture() {
super.didStopUserGesture();
print('didStopUserGesture');
}
}
use
void main() {
runApp(MyApp());
}
MyRouteObserver<PageRoute> myRouteObserver = MyRouteObserver<PageRoute>();
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
navigatorObservers: [myRouteObserver],
initialRoute: '/A',
home: APage(),
);
}
}
intercept return event
WillPopScope
is a widget in Flutter that can be used to control the behavior of the back button on Android and iOS platforms. Normally, when the user clicks the back button, the application closes the current page and returns to the previous page. However, sometimes we need to perform some custom operations when the user clicks the back button, such as popping up a confirmation dialog box or performing some data saving operations.
WillPopScope
It is used to implement this custom back button behavior. It can listen to the event when the user clicks the back button and perform some action as needed. WillPopScope
In , we can onWillPop
handle the back button event through the callback function. If onWillPop
it returns true
, it means that it is allowed to close the current page and return to the previous page; if it returns false
, it means that it is not allowed to close the current page.
Typically, it's placed in a property of the main page Scaffold
so body
that when the user presses the back button, Scaffold
the return action can be handled in .
Scaffold(
//导航条
appBar: AppBar(
title: const Text("路由", style: TextStyle(color: Colors.white)),
),
//页面主题内容
body: WillPopScope(
onWillPop: () async {
bool confirmExit = await showDialog(
context: context,
builder: (content) {
return AlertDialog(
title: const Text('确定要推出吗?'),
actions: [
ElevatedButton(
child: const Text('退出'),
onPressed: () => Navigator.of(context).pop(true)),
ElevatedButton(
child: const Text('取消'),
onPressed: () => Navigator.of(context).pop(false)),
],
);
});
return confirmExit;
},
child: const YcHomeBody(),
));
Custom independent routing
Navigator
It is a control that manages routing. Usually, it is directly used Navigator.of(context)
to jump pages. The reason why it can be used directly is Navigator.of(context)
because WidgetsApp
this control is used in MaterialApp
. .MaterialApp
Navigator
Navigator
The usage is very simple, as follows:
Navigator(
initialRoute: '/',
onGenerateRoute: (RouteSettings settings) {
WidgetBuilder builder;
switch (settings.name) {
case 'home':
builder = (context) => PageA();
break;
case 'user':
builder = (context) => PageB();
break;
}
return MaterialPageRoute(builder: builder, settings: settings);
},
)
initialRoute
It means to initialize the route, onGenerateRoute
and means to generate a route according to RouteSettings.
common scene
So when do you need to use Navigator? Use Navigator where partial page jumps are required, as in the following scenarios:
Toutiao client report scene
Toutiao client has a "cross" under each news item, click on the relevant information to pop up, click on the report, and you will jump to the report page in the current small window. It is not a full-screen switch page, but just Switch on the currently popped-up page.
home page
class _YcHomeBodyState extends State<YcHomeBody> {
Widget build(BuildContext context) {
return Center(
child: SizedBox(
height: 350,
width: 300,
child: Navigator(
initialRoute: '/',
onGenerateRoute: (RouteSettings settins) {
// 设置默认值
WidgetBuilder builder = (content) => const Center();
switch (settins.name) {
case '/':
builder = (context) => const SecondPage();
break;
}
return MaterialPageRoute(builder: builder);
},
),
),
);
}
}
SecondPage
page
class SecondPage extends StatelessWidget {
const SecondPage({
Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Center(
child: SizedBox(
height: 100,
child: Card(
child: Column(
children: <Widget>[
_buildItem(Icons.access_alarm, '举报', '标题夸张,内容质量差 等',
showArrow: true, onPress: () {
Navigator.of(context).push(MaterialPageRoute(builder: (context) {
return const ThirdPage();
}));
}),
],
),
),
));
}
_buildItem(IconData iconData, String title, String content,
{
bool showArrow = false, required VoidCallback onPress}) {
return Row(
children: <Widget>[
Icon(iconData),
const SizedBox(
width: 20,
),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
title,
style: const TextStyle(fontSize: 18),
),
Text(
content,
style: TextStyle(
color: Colors.black.withOpacity(.5), fontSize: 14),
)
],
),
),
!showArrow
? Container()
: IconButton(
icon: const Icon(Icons.arrow_forward_ios),
iconSize: 16,
onPressed: onPress,
),
],
);
}
}
ThirdPage
class ThirdPage extends StatelessWidget {
const ThirdPage({
Key? key}) : super(key: key);
Widget build(BuildContext context) {
return Container(
height: 50,
width: 250,
color: Colors.grey.withOpacity(.5),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
IconButton(
icon: const Icon(Icons.arrow_back_ios),
onPressed: () {
Navigator.of(context).pop();
},
),
const Text("抱歉系统升级中,暂时无法举报")
],
),
],
),
);
}
}