当前位置:   article > 正文

Flutter 2进阶(四):基于Navigator 2.0封装_flutter navigator封装

flutter navigator封装

Navigator 2.0 提供了一系列全新接口,可以实现将路由状态成为应用状态的一部分,新增的 API 如下:

Page :表示 Navigator 路由栈中各个页面的不可变对象;Page 是个抽象类,通常使用他的派生类: MaterialPage 或 CupertinoPage;

Router:用来配置要由 Navigator 展示的页面列表,通常改页面列表会根据系统的状态而改变;除了使用 Router 本身还可以使用 MaterialApp.router()来创建 Router;

RouterDelegate:定义程序中的路由行为,如 Router 如何知道应用程序状态变化以及如何响应;主要工作监听 RouterInformationParser 和应用状态,并使用当前列表构建 Pages;

还有另外几个不常用的:RouterInformationParser、RouterInformationProvider 和 BackButtonDispatcher ,可自行百度了解。

大概流程图如下:

当 app 打开进入 RouterDelegate 中的 build 方法,在里面手动创建堆栈,将要显示的页面放入到堆栈,然后走 setNewRoutePath 方法,然后再重新 build。当我们手动进入新的页面后,再次执行RouterDelegate 中的 build 方法。返回到上一页面会调用 onPopPage 方法。

这个思想和 Android 的是一样的,在 Android 中,也会创建堆栈,将要显示的页面放到里面,如果要显示已经存在的页面,将其它页面出栈然后显示出来。不过堆栈和页面跳转是分开的。

不过 flutter 中的 Navigator 2.0 将堆栈和路由导航封装到了一起,所以看起来十分麻烦,但是利于后面的项目维护。

这是封装后的效果:

大概的思路是在 RouterDelegate 中的 build 方法中,创建了堆栈,如果页面已经存在,则将该页面和上面的页面进行出栈,然后新建改页面放入堆栈中,如果该页面不存在,那么新建页面放入栈中。 

CSDN 下载源码:https://download.csdn.net/download/wuqingsen1/53386218

GitHub 下载源码:GitHub - wuqingsen/FlutterLearnDemo: flutter学习记录

主要代码:

my_navigator.dart ,主要工具类,创建页面,路由状态,监听跳转等:

  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_blbl/page/home_page.dart';
  3. import 'package:flutter_blbl/page/login_page.dart';
  4. import 'package:flutter_blbl/page/registration_page.dart';
  5. import 'package:flutter_blbl/page/video_detail_page.dart';
  6. typedef RouteChangeListener(RouteStatusInfo current, RouteStatusInfo pre);
  7. ///创建页面
  8. pageWrap(Widget child) {
  9. return MaterialPage(key: ValueKey(child.hashCode), child: child);
  10. }
  11. ///获取routeStatus在页面栈中的位置,
  12. int getPageIndex(List<MaterialPage> pages, RouteStatus routeStatus) {
  13. for (int i = 0; i < pages.length; i++) {
  14. MaterialPage page = pages[i];
  15. if (getStatus(page) == routeStatus) {
  16. return i;
  17. }
  18. }
  19. return -1;
  20. }
  21. ///路由封装,路由状态
  22. enum RouteStatus { login, registration, home, detail, unknown }
  23. ///获取page对应的routeStatus
  24. RouteStatus getStatus(MaterialPage page) {
  25. if (page.child is LoginPage) {
  26. return RouteStatus.login;
  27. } else if (page.child is RegistrationPage) {
  28. return RouteStatus.registration;
  29. } else if (page.child is HomePage) {
  30. return RouteStatus.home;
  31. } else if (page.child is VideoDetailPage) {
  32. return RouteStatus.detail;
  33. } else {
  34. return RouteStatus.unknown;
  35. }
  36. }
  37. ///路由信息
  38. class RouteStatusInfo {
  39. final RouteStatus routeStatus;
  40. final Widget page;
  41. RouteStatusInfo(this.routeStatus, this.page);
  42. }
  43. class MyNavigator extends _RouteJumpListener {
  44. static MyNavigator _instance;
  45. RouteJumpListener _routeJump;
  46. List<RouteChangeListener> _listeners = [];
  47. RouteStatusInfo _current; //打开过的页面
  48. MyNavigator._();
  49. ///注册路由跳转逻辑
  50. void registerRouteJump(RouteJumpListener routeJumpListener) {
  51. this._routeJump = routeJumpListener;
  52. }
  53. ///监听路由页面跳转
  54. void addListener(RouteChangeListener listener) {
  55. if (!_listeners.contains(listener)) {
  56. _listeners.add(listener);
  57. }
  58. }
  59. ///移除监听
  60. void removeListener(RouteChangeListener listener) {
  61. _listeners.remove(listener);
  62. }
  63. @override
  64. void onJumpTo(RouteStatus routeStatus, {Map args}) {
  65. _routeJump.onJumpTo(routeStatus, args: args);
  66. }
  67. ///通知路由页面变化,currentPages当前页面,prePages上一次页面
  68. void notify(List<MaterialPage> currentPages, List<MaterialPage> prePages) {
  69. //路由堆栈无变化不处理
  70. if (currentPages == prePages) return;
  71. var current =
  72. RouteStatusInfo(getStatus(currentPages.last), currentPages.last.child);
  73. _notify(current);
  74. }
  75. void _notify(RouteStatusInfo current) {
  76. print('my_navigator:current:' + current.page.toString());
  77. print('my_navigator:pre:' + _current?.page.toString());
  78. _listeners.forEach((listener) {
  79. listener(current, _current);
  80. });
  81. _current = current;
  82. }
  83. static MyNavigator getInstance() {
  84. if (_instance == null) {
  85. _instance = MyNavigator._();
  86. }
  87. return _instance;
  88. }
  89. }
  90. abstract class _RouteJumpListener {
  91. void onJumpTo(RouteStatus routeStatus, {Map args});
  92. }
  93. typedef OnJumpTo = void Function(RouteStatus routeStatus, {Map args});
  94. ///定义路由跳转逻辑要实现功能
  95. class RouteJumpListener {
  96. final OnJumpTo onJumpTo;
  97. RouteJumpListener({this.onJumpTo});
  98. }

main.dart :

  1. import 'package:data_plugin/bmob/bmob.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:data_plugin/data_plugin.dart';
  4. import 'package:flutter_blbl/db/hi_cache.dart';
  5. import 'package:flutter_blbl/model/navigator/my_navigator.dart';
  6. import 'package:flutter_blbl/model/video_model.dart';
  7. import 'package:flutter_blbl/page/home_page.dart';
  8. import 'package:flutter_blbl/page/login_page.dart';
  9. import 'package:flutter_blbl/page/registration_page.dart';
  10. import 'package:flutter_blbl/page/video_detail_page.dart';
  11. import 'package:flutter_blbl/utils/Constants.dart';
  12. import 'package:flutter_blbl/utils/color.dart';
  13. import 'package:permission_handler/permission_handler.dart';
  14. import 'package:shared_preferences/shared_preferences.dart';
  15. import 'db/PersistentStorage.dart';
  16. import 'db/my_shared_preferences.dart';
  17. import 'utils/toast.dart';
  18. void main() {
  19. SharedPreferences.setMockInitialValues({});
  20. runApp(const MyApp());
  21. }
  22. class MyApp extends StatelessWidget {
  23. // This widget is the root of your application.
  24. const MyApp({Key key}) : super(key: key);
  25. @override
  26. Widget build(BuildContext context) {
  27. return MaterialApp(title: 'An App', home: LoadingPage());
  28. }
  29. }
  30. class LoadingPage extends StatefulWidget {
  31. const LoadingPage({Key key}) : super(key: key);
  32. @override
  33. _MyAppState createState() => _MyAppState();
  34. }
  35. class _MyAppState extends State<LoadingPage> {
  36. BiliRouteDelegate _routeDelegate = BiliRouteDelegate();
  37. // Future<dynamic> fetchName() async {
  38. // SharedPreferences pf = await SharedPreferences.getInstance();
  39. // String response1 = pf.getString(SPName);
  40. // String response = await PersistentStorage().getStorage(SPName);
  41. // userName = response;
  42. // print('姓名' + response + "," + userName + response1);
  43. // return response;
  44. // }
  45. @override
  46. void initState() {
  47. Bmob.initMasterKey(
  48. "https://api2.bmob.cn",
  49. "bdb1a27490a65408db30a862b5023ffa",
  50. "1ef17ded7609019652b448773b06e1bd",
  51. "574fb1ffbe28cce59414d678d1817eca");
  52. super.initState();
  53. }
  54. @override
  55. Widget build(BuildContext context) {
  56. var widget = Router(
  57. //定义route
  58. routerDelegate: _routeDelegate,
  59. );
  60. return MaterialApp(
  61. home: widget,
  62. theme: ThemeData(primarySwatch: white),
  63. );
  64. //如果要请求网络或初始化数据可以调用下面
  65. // return FutureBuilder<dynamic>(
  66. // //进行初始化
  67. // future: fetchName(),
  68. // builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
  69. // //加载完成显示页面,否则显示加载中
  70. // print("加载状态:" + snapshot.toString());
  71. // var widget;
  72. // if (snapshot.connectionState == ConnectionState.done) {
  73. // widget = Router(
  74. // //定义route
  75. // routerDelegate: _routeDelegate,
  76. // );
  77. // }
  78. // return MaterialApp(
  79. // home: widget,
  80. // theme: ThemeData(primarySwatch: white),
  81. // );
  82. // });
  83. }
  84. }
  85. ///路由代理
  86. class BiliRouteDelegate extends RouterDelegate
  87. with ChangeNotifier, PopNavigatorRouterDelegateMixin {
  88. final GlobalKey<NavigatorState> navigatorKey;
  89. //默认进入首页
  90. RouteStatus _routeStatus = RouteStatus.home;
  91. BiliRouteDelegate() : navigatorKey = GlobalKey<NavigatorState>() {
  92. //实现跳转逻辑
  93. MyNavigator.getInstance().registerRouteJump(
  94. RouteJumpListener(onJumpTo: (RouteStatus routeStatus, {Map args}) {
  95. _routeStatus = routeStatus;
  96. //主页会往详情页传值
  97. if (routeStatus == RouteStatus.detail) {
  98. this.videoModel = args['videoMo'];
  99. }
  100. notifyListeners();
  101. }));
  102. }
  103. List<MaterialPage> pages = []; //存放所有页面的列表
  104. VideoModel videoModel;
  105. @override
  106. Widget build(BuildContext context) {
  107. var index = getPageIndex(pages, routeStatus);
  108. List<MaterialPage> tempPages = pages;
  109. //如果需要打开的页面已经存在,改页面和上面的页面进行出栈
  110. if (index != -1) {
  111. tempPages = tempPages.sublist(0, index);
  112. }
  113. var page;
  114. print('routeStatus:' + routeStatus.toString());
  115. if (routeStatus == RouteStatus.home) {
  116. //跳转到首页,将其它页面都出栈,然后创建新的首页
  117. pages.clear();
  118. page = pageWrap(HomePage());
  119. } else if (routeStatus == RouteStatus.detail) {
  120. //打开详情页
  121. page = pageWrap(VideoDetailPage(videoModel));
  122. } else if (routeStatus == RouteStatus.registration) {
  123. //打开注册页面
  124. page = pageWrap(RegistrationPage());
  125. } else if (routeStatus == RouteStatus.login) {
  126. page = pageWrap(LoginPage());
  127. }
  128. //重新创建堆栈数组
  129. tempPages = [...tempPages, page];
  130. pages = tempPages;
  131. //通知路由发生变化
  132. MyNavigator.getInstance().notify(tempPages, pages);
  133. return WillPopScope(
  134. child: Navigator(
  135. key: navigatorKey,
  136. pages: pages,
  137. onPopPage: (route, result) {
  138. if (route.settings is MaterialPage) {
  139. if ((route.settings as MaterialPage).child is LoginPage) {
  140. //
  141. }
  142. }
  143. //在这里控制是否可以返回上一页
  144. if (!route.didPop(result)) {
  145. return false;
  146. }
  147. var temPages = [...pages];
  148. pages.removeLast();
  149. //通知路由变化
  150. MyNavigator.getInstance().notify(pages, temPages);
  151. return true;
  152. },
  153. ),
  154. //手机返回键返回到上一页
  155. onWillPop: () async => !await navigatorKey.currentState.maybePop());
  156. }
  157. RouteStatus get routeStatus {
  158. //返回要进入的页面,登录或主页等
  159. if (_routeStatus != RouteStatus.registration && USERNAME == null) {
  160. return _routeStatus = RouteStatus.login;
  161. } else if (videoModel != null) {
  162. return _routeStatus = RouteStatus.detail;
  163. } else {
  164. return _routeStatus;
  165. }
  166. }
  167. @override
  168. Future<void> setNewRoutePath(configuration) {}
  169. }

页面跳转:

  1. //不传值
  2. MyNavigator.getInstance().onJumpTo(RouteStatus.registration);
  3. //传值
  4. MyNavigator.getInstance().onJumpTo(RouteStatus.detail,
  5. args: {'videoMo': VideoModel(1212)});

====== 扩展

flutter 中无法监听 Android 原生的 onResume 和 onPause 方法,在里面也封装了如何监听 onResume 和 onPause :

  1. var listener;
  2. @override
  3. void initState() {
  4. super.initState();
  5. MyNavigator.getInstance().addListener((current, pre) {
  6. print('current:' + current.page.toString());
  7. print('current:' + pre.page.toString());
  8. if (widget == current.page || current.page is HomePage) {
  9. print('打开了首页:onResume');
  10. } else if (widget == pre?.page || pre?.page is HomePage) {
  11. print('首页:onPause');
  12. }
  13. });
  14. }
  15. @override
  16. void dispose() {
  17. MyNavigator.getInstance().removeListener(this.listener);
  18. super.dispose();
  19. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Cpp五条/article/detail/418424
推荐阅读
相关标签
  

闽ICP备14008679号