原生路由
初始化
在MyApp的build方法中添加常规路由处理方法onGenerateRoute,未知路由处理方法onUnknownRoute,以及定义初始路由initialRoute。
class MyApp extends StatelessWidget {const MyApp({Key? key}) : super(key: key);// This widget is the root of your application.@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),initialRoute: 'splash',onGenerateRoute: onGenerateRoute,onUnknownRoute: onUnknownRoute,);}}//固定写法var onGenerateRoute = (RouteSettings settings) {final String name = settings.name ??"404";final Function pageContentBuilder = routes[name] as Function;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;}}};// 未知路由var onUnknownRoute = (RouteSettings settings) {final String name = "404";final Function pageContentBuilder = routes[name] as Function;if (pageContentBuilder != null) {final Route route =MaterialPageRoute(builder: (context) => pageContentBuilder(context));return route;}};final routes = {'404': (context) => NotFoundPage(),/// 404 notfound/// 往这里加};
添加了onGenerateRoute之后,Flutter的路由优先执行onGenerateRoute方法,initialRoute配置的初始路由也是通过onGenerateRoute去查找。当代码传递的路由名称在routes中找不到时,就会执行onUnknownRoute方法,跳转到自定义未知路由。
跳转
使用Navigator.pushNamed 跳转到指定的路由。
Navigator.pushNamed(context, 'RouteName',arguments: 携带的数据);
这是Navigator.pushNamed方法的源码:
@optionalTypeArgsstatic Future<T?> pushNamed<T extends Object?>(BuildContext context,String routeName, {Object? arguments,}) {return Navigator.of(context).pushNamed<T>(routeName, arguments: arguments);}
返回的是一个 Future
Navigator.pushNamed(context, 'RouteName',arguments: 携带的数据).then((value) {// todo something});
关闭页面
使用Navigator.pop来关闭当前页面。
Navigator.pop(context);
这是Navigator.pop方法的源码:
@optionalTypeArgsstatic void pop<T extends Object?>(BuildContext context, [ T? result ]) {Navigator.of(context).pop<T>(result);}
若果需要带参返回,则将参数赋值给result,由Navigator.pushNamed().then接收数据。如:
Navigator.pop(context,data);
其他方法
- popAndPushNamed 关闭当前页并根据名称跳转到指定页
- pushReplacementNamed 根据名称更换当前路由堆栈
- pushNamedAndRemoveUntil 根据名称跳转指定页并移除之前的路由堆栈(将跳转到的页面作为根路由)
- push 跳转到指定页
- pushReplacement 更换当前路由堆栈
- pushAndRemoveUntil 跳转指定页并移除之前的路由堆栈(将跳转到的页面作为根路由)
- replace 替换路由
- replaceRouteBelow 更换路由路线
- popUntil 关闭所有路由
- removeRoute 移除路由
- removeRouteBelow 移除路由路线
- canPop 判断当前页面是否可关闭
GetX(推荐使用)※
getx是一款功能强大的插件,路由管理是其中一项功能。
初始化
在pubspec.yaml中引入getx,当前使用的版本为4.3.4。
dependencies:flutter:sdk: flutter# The following adds the Cupertino Icons font to your application.# Use with the CupertinoIcons class for iOS style icons.cupertino_icons: ^1.0.2get: ^4.3.4 # 路由和状态管理
在main.dart的MyApp中使用GetMaterialApp替换原有的MaterialApp:
void main() {runApp(MyApp());}class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return GetMaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),home: HomePage(),);}}
跳转
使用 Get.to(()=>Page()) (或Get.to(Page()) ,没有其他参数的情况下推荐使用前者) 实现跳转,表示从当前页面跳转到Page页面,也可以使用Get.toNamed(‘path’):
Get.to(()=>Page()) ; // Get.to(Page());Get.toNamed('path');
使用Get.to可以在Page()中传递构造参数,也可以使用get中的arguments传参:
Get.to(Page(data:data),arguments: data);Get.toNamed('path',arguments: data); ///Get.toNamed 不能使用构造参数传值
如果使用Get.toNamed,需要配置path解析方法getPages:
class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return GetMaterialApp(title: 'Flutter Demo',theme: ThemeData(primarySwatch: Colors.blue,),initialRoute: '/home',getPages: getPageList,);}}var getPageList = [GetPage(name: '/home', page: () => HomePage()),];
getPageList是个数组,按GetPage(name: ‘/home’, page: () => HomePage()),格式往里边添加即可。
相同的,Get.to方法返回一个Future
Get.to(()=>HomePage())?.then((value) {ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('返回值为:$value'),),// 弹出底部提示框,显示返回值);});
关闭页面
关闭当前页面:
Get.back();
关闭当前页面并跳转到新页面:
Get.off(()=>Page());Get.offNamed('path');
清除所有路由并跳转到新页面(以新页面作为根节点):
Get.offAll(()=>Page());Get.offAllNamed('path');
其他方法
其他用法和原生相似,具体方法可在getx的GetNavigation类中查询,附上源码:
extension GetNavigation on GetInterface {/// **Navigation.push()** shortcut.<br><br>////// Pushes a new `page` to the stack////// It has the advantage of not needing context,/// so you can call from your business logic////// You can set a custom [transition], and a transition [duration].////// You can send any type of value to the other route in the [arguments].////// Just like native routing in Flutter, you can push a route/// as a [fullscreenDialog],////// [id] is for when you are using nested navigation,/// as explained in documentation////// If you want the same behavior of ios that pops a route when the user drag,/// you can set [popGesture] to true////// If you're using the [Bindings] api, you must define it here////// By default, GetX will prevent you from push a route that you already in,/// if you want to push anyway, set [preventDuplicates] to falseFuture<T?>? to<T>(dynamic page, {bool? opaque,Transition? transition,Curve? curve,Duration? duration,int? id,String? routeName,bool fullscreenDialog = false,dynamic arguments,Bindings? binding,bool preventDuplicates = true,bool? popGesture,double Function(BuildContext context)? gestureWidth,}) {// var routeName = "/${page.runtimeType}";routeName ??= "/${page.runtimeType}";routeName = _cleanRouteName(routeName);if (preventDuplicates && routeName == currentRoute) {return null;}return global(id).currentState?.push<T>(GetPageRoute<T>(opaque: opaque ?? true,page: _resolvePage(page, 'to'),routeName: routeName,gestureWidth: gestureWidth,settings: RouteSettings(name: routeName,arguments: arguments,),popGesture: popGesture ?? defaultPopGesture,transition: transition ?? defaultTransition,curve: curve ?? defaultTransitionCurve,fullscreenDialog: fullscreenDialog,binding: binding,transitionDuration: duration ?? defaultTransitionDuration,),);}GetPageBuilder _resolvePage(dynamic page, String method) {if (page is GetPageBuilder) {return page;} else if (page is Widget) {Get.log('''WARNING, consider using: "Get.$method(() => Page())" instead of "Get.$method(Page())".Using a widget function instead of a widget fully guarantees that the widget and its controllers will be removed from memory when they are no longer used.''');return () => page;} else if (page is String) {throw '''Unexpected String,use toNamed() instead''';} else {throw '''Unexpected format,you can only use widgets and widget functions here''';}}/// **Navigation.pushNamed()** shortcut.<br><br>////// Pushes a new named `page` to the stack.////// It has the advantage of not needing context, so you can call/// from your business logic.////// You can send any type of value to the other route in the [arguments].////// [id] is for when you are using nested navigation,/// as explained in documentation////// By default, GetX will prevent you from push a route that you already in,/// if you want to push anyway, set [preventDuplicates] to false////// Note: Always put a slash on the route ('/page1'), to avoid unnexpected errorsFuture<T?>? toNamed<T>(String page, {dynamic arguments,int? id,bool preventDuplicates = true,Map<String, String>? parameters,}) {if (preventDuplicates && page == currentRoute) {return null;}if (parameters != null) {final uri = Uri(path: page, queryParameters: parameters);page = uri.toString();}return global(id).currentState?.pushNamed<T>(page,arguments: arguments,);}/// **Navigation.pushReplacementNamed()** shortcut.<br><br>////// Pop the current named `page` in the stack and push a new one in its place////// It has the advantage of not needing context, so you can call/// from your business logic.////// You can send any type of value to the other route in the [arguments].////// [id] is for when you are using nested navigation,/// as explained in documentation////// By default, GetX will prevent you from push a route that you already in,/// if you want to push anyway, set [preventDuplicates] to false////// Note: Always put a slash on the route ('/page1'), to avoid unnexpected errorsFuture<T?>? offNamed<T>(String page, {dynamic arguments,int? id,bool preventDuplicates = true,Map<String, String>? parameters,}) {if (preventDuplicates && page == currentRoute) {return null;}if (parameters != null) {final uri = Uri(path: page, queryParameters: parameters);page = uri.toString();}return global(id).currentState?.pushReplacementNamed(page,arguments: arguments,);}/// **Navigation.popUntil()** shortcut.<br><br>////// Calls pop several times in the stack until [predicate] returns true////// [id] is for when you are using nested navigation,/// as explained in documentation////// [predicate] can be used like this:/// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,////// or also like this:/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the/// dialog is closedvoid until(RoutePredicate predicate, {int? id}) {// if (key.currentState.mounted) // add this if appear problems on future with route navigate// when widget don't mountedreturn global(id).currentState?.popUntil(predicate);}/// **Navigation.pushAndRemoveUntil()** shortcut.<br><br>////// Push the given `page`, and then pop several pages in the stack until/// [predicate] returns true////// [id] is for when you are using nested navigation,/// as explained in documentation////// Obs: unlike other get methods, this one you need to send a function/// that returns the widget to the page argument, like this:/// Get.offUntil(GetPageRoute(page: () => HomePage()), predicate)////// [predicate] can be used like this:/// `Get.offUntil(page, (route) => (route as GetPageRoute).routeName == '/home')`/// to pop routes in stack until home,/// or also like this:/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog/// is closedFuture<T?>? offUntil<T>(Route<T> page, RoutePredicate predicate, {int? id}) {// if (key.currentState.mounted) // add this if appear problems on future with route navigate// when widget don't mountedreturn global(id).currentState?.pushAndRemoveUntil<T>(page, predicate);}/// **Navigation.pushNamedAndRemoveUntil()** shortcut.<br><br>////// Push the given named `page`, and then pop several pages in the stack/// until [predicate] returns true////// You can send any type of value to the other route in the [arguments].////// [id] is for when you are using nested navigation,/// as explained in documentation////// [predicate] can be used like this:/// `Get.offNamedUntil(page, ModalRoute.withName('/home'))`/// to pop routes in stack until home,/// or like this:/// `Get.offNamedUntil((route) => !Get.isDialogOpen())`,/// to make sure the dialog is closed////// Note: Always put a slash on the route name ('/page1'), to avoid unexpected errorsFuture<T?>? offNamedUntil<T>(String page,RoutePredicate predicate, {int? id,dynamic arguments,Map<String, String>? parameters,}) {if (parameters != null) {final uri = Uri(path: page, queryParameters: parameters);page = uri.toString();}return global(id).currentState?.pushNamedAndRemoveUntil<T>(page,predicate,arguments: arguments,);}/// **Navigation.popAndPushNamed()** shortcut.<br><br>////// Pop the current named page and pushes a new `page` to the stack/// in its place////// You can send any type of value to the other route in the [arguments]./// It is very similar to `offNamed()` but use a different approach////// The `offNamed()` pop a page, and goes to the next. The/// `offAndToNamed()` goes to the next page, and removes the previous one./// The route transition animation is different.Future<T?>? offAndToNamed<T>(String page, {dynamic arguments,int? id,dynamic result,Map<String, String>? parameters,}) {if (parameters != null) {final uri = Uri(path: page, queryParameters: parameters);page = uri.toString();}return global(id).currentState?.popAndPushNamed(page,arguments: arguments,result: result,);}/// **Navigation.removeRoute()** shortcut.<br><br>////// Remove a specific [route] from the stack////// [id] is for when you are using nested navigation,/// as explained in documentationvoid removeRoute(Route<dynamic> route, {int? id}) {return global(id).currentState?.removeRoute(route);}/// **Navigation.pushNamedAndRemoveUntil()** shortcut.<br><br>////// Push a named `page` and pop several pages in the stack/// until [predicate] returns true. [predicate] is optional////// It has the advantage of not needing context, so you can/// call from your business logic.////// You can send any type of value to the other route in the [arguments].////// [predicate] can be used like this:/// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,/// or also like/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog/// is closed////// [id] is for when you are using nested navigation,/// as explained in documentation////// Note: Always put a slash on the route ('/page1'), to avoid unexpected errorsFuture<T?>? offAllNamed<T>(String newRouteName, {RoutePredicate? predicate,dynamic arguments,int? id,Map<String, String>? parameters,}) {if (parameters != null) {final uri = Uri(path: newRouteName, queryParameters: parameters);newRouteName = uri.toString();}return global(id).currentState?.pushNamedAndRemoveUntil<T>(newRouteName,predicate ?? (_) => false,arguments: arguments,);}/// Returns true if a Snackbar, Dialog or BottomSheet is currently OPENbool get isOverlaysOpen =>(isSnackbarOpen! || isDialogOpen! || isBottomSheetOpen!);/// Returns true if there is no Snackbar, Dialog or BottomSheet openbool get isOverlaysClosed =>(!isSnackbarOpen! && !isDialogOpen! && !isBottomSheetOpen!);/// **Navigation.popUntil()** shortcut.<br><br>////// Pop the current page, snackbar, dialog or bottomsheet in the stack////// if your set [closeOverlays] to true, Get.back() will close the/// currently open snackbar/dialog/bottomsheet AND the current page////// [id] is for when you are using nested navigation,/// as explained in documentation////// It has the advantage of not needing context, so you can call/// from your business logic.void back<T>({T? result,bool closeOverlays = false,bool canPop = true,int? id,}) {if (closeOverlays && isOverlaysOpen) {navigator?.popUntil((route) {return (isOverlaysClosed);});}if (canPop) {if (global(id).currentState?.canPop() == true) {global(id).currentState?.pop<T>(result);}} else {global(id).currentState?.pop<T>(result);}}/// **Navigation.popUntil()** (with predicate) shortcut .<br><br>////// Close as many routes as defined by [times]////// [id] is for when you are using nested navigation,/// as explained in documentationvoid close(int times, [int? id]) {if (times < 1) {times = 1;}var count = 0;var back = global(id).currentState?.popUntil((route) => count++ == times);return back;}/// **Navigation.pushReplacement()** shortcut .<br><br>////// Pop the current page and pushes a new `page` to the stack////// It has the advantage of not needing context,/// so you can call from your business logic////// You can set a custom [transition], define a Tween [curve],/// and a transition [duration].////// You can send any type of value to the other route in the [arguments].////// Just like native routing in Flutter, you can push a route/// as a [fullscreenDialog],////// [id] is for when you are using nested navigation,/// as explained in documentation////// If you want the same behavior of ios that pops a route when the user drag,/// you can set [popGesture] to true////// If you're using the [Bindings] api, you must define it here////// By default, GetX will prevent you from push a route that you already in,/// if you want to push anyway, set [preventDuplicates] to falseFuture<T?>? off<T>(dynamic page, {bool opaque = false,Transition? transition,Curve? curve,bool? popGesture,int? id,String? routeName,dynamic arguments,Bindings? binding,bool fullscreenDialog = false,bool preventDuplicates = true,Duration? duration,double Function(BuildContext context)? gestureWidth,}) {routeName ??= "/${page.runtimeType.toString()}";routeName = _cleanRouteName(routeName);if (preventDuplicates && routeName == currentRoute) {return null;}return global(id).currentState?.pushReplacement(GetPageRoute(opaque: opaque,gestureWidth: gestureWidth,page: _resolvePage(page, 'off'),binding: binding,settings: RouteSettings(arguments: arguments,name: routeName,),routeName: routeName,fullscreenDialog: fullscreenDialog,popGesture: popGesture ?? defaultPopGesture,transition: transition ?? defaultTransition,curve: curve ?? defaultTransitionCurve,transitionDuration: duration ?? defaultTransitionDuration));}////// Push a `page` and pop several pages in the stack/// until [predicate] returns true. [predicate] is optional////// It has the advantage of not needing context,/// so you can call from your business logic////// You can set a custom [transition], a [curve] and a transition [duration].////// You can send any type of value to the other route in the [arguments].////// Just like native routing in Flutter, you can push a route/// as a [fullscreenDialog],////// [predicate] can be used like this:/// `Get.until((route) => Get.currentRoute == '/home')`so when you get to home page,/// or also like/// `Get.until((route) => !Get.isDialogOpen())`, to make sure the dialog/// is closed////// [id] is for when you are using nested navigation,/// as explained in documentation////// If you want the same behavior of ios that pops a route when the user drag,/// you can set [popGesture] to true////// If you're using the [Bindings] api, you must define it here////// By default, GetX will prevent you from push a route that you already in,/// if you want to push anyway, set [preventDuplicates] to falseFuture<T?>? offAll<T>(dynamic page, {RoutePredicate? predicate,bool opaque = false,bool? popGesture,int? id,String? routeName,dynamic arguments,Bindings? binding,bool fullscreenDialog = false,Transition? transition,Curve? curve,Duration? duration,double Function(BuildContext context)? gestureWidth,}) {routeName ??= "/${page.runtimeType.toString()}";routeName = _cleanRouteName(routeName);return global(id).currentState?.pushAndRemoveUntil<T>(GetPageRoute<T>(opaque: opaque,popGesture: popGesture ?? defaultPopGesture,page: _resolvePage(page, 'offAll'),binding: binding,gestureWidth: gestureWidth,settings: RouteSettings(name: routeName,arguments: arguments,),fullscreenDialog: fullscreenDialog,routeName: routeName,transition: transition ?? defaultTransition,curve: curve ?? defaultTransitionCurve,transitionDuration: duration ?? defaultTransitionDuration,),predicate ?? (route) => false);}/// 省略部分代码}
