my_navigator.dart 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475
  1. import 'package:flutter/cupertino.dart';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
  4. import 'package:lszlgl/base/base_state.dart';
  5. import 'package:lszlgl/page/unknow_page.dart';
  6. /// 路由监听
  7. class NavigatorPageObserver extends NavigatorObserver {
  8. static final NavigatorPageObserver instance = NavigatorPageObserver._();
  9. NavigatorPageObserver._();
  10. /// 路由信息
  11. /// 外部可以调用[MyNavigator.routeInfo]取值
  12. final RouteModel routeInfo = RouteModel();
  13. @override
  14. void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
  15. // 路由入栈
  16. routeInfo.update(showRoute: route, hideRoute: previousRoute);
  17. debugPrint('路由入栈 didPush:$routeInfo');
  18. _pageRecord();
  19. }
  20. @override
  21. void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
  22. // 路由出栈
  23. routeInfo.update(showRoute: previousRoute, hideRoute: route);
  24. debugPrint('路由出栈 didPop:$routeInfo');
  25. _pageRecord();
  26. }
  27. @override
  28. void didRemove(Route<dynamic> route, Route<dynamic>? previousRoute) {
  29. /*
  30. routeInfo.update(showRoute: previousRoute, hideRoute: route);
  31. // 路由移除
  32. debugPrint('路由移除 didRemove:$routeInfo');
  33. */
  34. }
  35. @override
  36. void didReplace({Route<dynamic>? newRoute, Route<dynamic>? oldRoute}) {
  37. // 路由替换
  38. routeInfo.update(showRoute: newRoute, hideRoute: oldRoute);
  39. debugPrint('路由替换 didReplace:$routeInfo');
  40. _pageRecord();
  41. }
  42. @override
  43. void didStartUserGesture(Route<dynamic> route, Route<dynamic>? previousRoute) {
  44. // 手势控制路由
  45. }
  46. @override
  47. void didStopUserGesture() {
  48. // 手势控制路由结束
  49. }
  50. /// 页面统计
  51. void _pageRecord() {
  52. if (routeInfo.hideName != null) {
  53. // 页面关闭
  54. }
  55. if (routeInfo.showName != null) {
  56. // 页面显示
  57. }
  58. }
  59. }
  60. class RouteModel {
  61. /// 显示的路由
  62. Route<dynamic>? showRoute;
  63. /// 隐藏的路由
  64. Route<dynamic>? hideRoute;
  65. /// 前一个 widget 是否是一个页面(最好统一使用自定义路由)
  66. bool get isPage => hideRoute is PageRoute;
  67. /// 前一个 widget 是否是弹框
  68. bool get isDialog => !isPage;
  69. RouteModel();
  70. void update({Route<dynamic>? showRoute, Route<dynamic>? hideRoute}) {
  71. if (showRoute != null) this.showRoute = showRoute;
  72. if (hideRoute != null) this.hideRoute = hideRoute;
  73. }
  74. /// 显示路由的名称
  75. String? get showName => showRoute?.settings.name;
  76. /// 显示路由的配置信息
  77. Object? get showPageSetting => showRoute?.settings.arguments;
  78. /// 显示路由的业务参数
  79. Object? get showArguments {
  80. Object? args = showRoute?.settings.arguments;
  81. if (args != null) {
  82. if (args is PageSetting) {
  83. // PageSetting的参数
  84. return args.arguments;
  85. } else {
  86. // 原参数
  87. return args;
  88. }
  89. }
  90. return null;
  91. }
  92. /// 隐藏/移除路由的名称
  93. String? get hideName => hideRoute?.settings.name;
  94. /// 隐藏/移除路由的配置信息
  95. Object? get hidePageSetting => hideRoute?.settings.arguments;
  96. /// 隐藏/移除路由的业务参数
  97. Object? get hideArguments {
  98. Object? args = hideRoute?.settings.arguments;
  99. if (args != null) {
  100. if (args is PageSetting) {
  101. // PageSetting的参数
  102. return args.arguments;
  103. } else {
  104. // 原参数
  105. return args;
  106. }
  107. }
  108. return null;
  109. }
  110. @override
  111. String toString() {
  112. StringBuffer sb = StringBuffer();
  113. if (showRoute != null) {
  114. sb.write('showName:$showName showArgs:$showArguments ');
  115. }
  116. if (hideRoute != null) {
  117. sb.write('hideName:$hideName hideArgs:$hideArguments');
  118. }
  119. return sb.toString();
  120. }
  121. }
  122. /// 路由页面Builder函数
  123. typedef MyNavigatorBuilder = Widget Function(BuildContext context, Object? arguments);
  124. class MyNavigator {
  125. MyNavigator._();
  126. /// 全局Navigator
  127. static final GlobalKey<NavigatorState> navigator = GlobalKey<NavigatorState>();
  128. /// 全局路由监听
  129. static final NavigatorPageObserver navigatorObs = NavigatorPageObserver.instance;
  130. /// 最新路由信息
  131. static RouteModel routeInfo = navigatorObs.routeInfo;
  132. /// Widget路由监听
  133. static final RouteObserver<ModalRoute<void>> routeObs = RouteObserver<ModalRoute<void>>();
  134. /// 自定义路由拦截
  135. static Route<dynamic> generateRoute(RouteSettings settings) {
  136. var routeName = settings.name;
  137. // 是否dialog
  138. bool fullscreenDialog = false;
  139. // 是否可截屏
  140. bool allowSnapshotting = true;
  141. // 业务传参
  142. Object? args;
  143. // 页面路由样式
  144. RouteType routeType = RouteType.cupertino;
  145. // 获取路由配置
  146. if (settings.arguments != null && settings.arguments is PageSetting) {
  147. PageSetting pageSetting = settings.arguments as PageSetting;
  148. fullscreenDialog = pageSetting.fullscreenDialog ?? false;
  149. allowSnapshotting = pageSetting.allowSnapshotting ?? true;
  150. routeType = pageSetting.routeType ?? RouteType.cupertino;
  151. args = pageSetting.arguments;
  152. } else {
  153. args = settings.arguments;
  154. }
  155. // 创建路由
  156. MyNavigatorBuilder? builder = rRouteMap[routeName];
  157. builder ??= (context, args) => UnknownPage(name: settings.name);
  158. switch (routeType) {
  159. case RouteType.material:
  160. return MaterialPageRoute(
  161. builder: (context) => builder!.call(context, args),
  162. settings: settings,
  163. fullscreenDialog: fullscreenDialog,
  164. allowSnapshotting: allowSnapshotting,
  165. );
  166. case RouteType.normal:
  167. return PageRouteBuilder(
  168. pageBuilder: (context, animation, secondaryAnimation) => builder!.call(context, args),
  169. settings: settings,
  170. fullscreenDialog: fullscreenDialog,
  171. allowSnapshotting: allowSnapshotting,
  172. );
  173. case RouteType.cupertino:
  174. default:
  175. return CupertinoPageRoute(
  176. builder: (context) => builder!.call(context, args),
  177. settings: settings,
  178. fullscreenDialog: fullscreenDialog,
  179. allowSnapshotting: allowSnapshotting,
  180. );
  181. }
  182. }
  183. /// 添加新页面
  184. static Future<T?> push<T extends Object?>(
  185. String name, {
  186. Object? args,
  187. bool fullscreenDialog = false,
  188. bool allowSnapshotting = true,
  189. RouteType routeType = RouteType.cupertino,
  190. }) {
  191. PageSetting setting = PageSetting(
  192. arguments: args,
  193. fullscreenDialog: fullscreenDialog,
  194. allowSnapshotting: allowSnapshotting,
  195. routeType: routeType,
  196. );
  197. return navigator.currentState!.pushNamed(name, arguments: setting);
  198. }
  199. /// 添加新页面 并 替换当前页面
  200. static Future<T?> pushReplace<T extends Object?, TO extends Object?>(
  201. String name, {
  202. TO? result,
  203. Object? args,
  204. bool fullscreenDialog = false,
  205. bool allowSnapshotting = true,
  206. RouteType routeType = RouteType.cupertino,
  207. }) {
  208. PageSetting setting = PageSetting(
  209. arguments: args,
  210. fullscreenDialog: fullscreenDialog,
  211. allowSnapshotting: allowSnapshotting,
  212. routeType: routeType,
  213. );
  214. return navigator.currentState!.pushReplacementNamed(name, result: result, arguments: setting);
  215. }
  216. /// 添加新页面 并 移除一些页面,直至predicate返回true
  217. static Future<T?> pushRemoveUntil<T extends Object?>(
  218. String name,
  219. RoutePredicate predicate, {
  220. Object? args,
  221. bool fullscreenDialog = false,
  222. bool allowSnapshotting = true,
  223. RouteType routeType = RouteType.cupertino,
  224. }) {
  225. PageSetting setting = PageSetting(
  226. arguments: args,
  227. fullscreenDialog: fullscreenDialog,
  228. allowSnapshotting: allowSnapshotting,
  229. routeType: routeType,
  230. );
  231. return navigator.currentState!.pushNamedAndRemoveUntil(name, predicate, arguments: setting);
  232. }
  233. /// 移除路由栈顶页面
  234. static void pop<T extends Object?>([T? result]) {
  235. return navigator.currentState!.pop(result);
  236. }
  237. /// 移除路由栈顶页面 并 添加新页面
  238. static Future<T?> popAndPush<T extends Object?, TO extends Object?>(
  239. String name, {
  240. TO? result,
  241. Object? args,
  242. bool fullscreenDialog = false,
  243. bool allowSnapshotting = true,
  244. RouteType routeType = RouteType.cupertino,
  245. }) {
  246. PageSetting setting = PageSetting(
  247. arguments: args,
  248. fullscreenDialog: fullscreenDialog,
  249. allowSnapshotting: allowSnapshotting,
  250. routeType: routeType,
  251. );
  252. return navigator.currentState!.popAndPushNamed(name, result: result, arguments: setting);
  253. }
  254. /// 移除一些页面, 直至predicate返回true
  255. static void popUntil(RoutePredicate predicate) {
  256. return navigator.currentState!.popUntil(predicate);
  257. }
  258. /// 显示Dialog
  259. static Future<T?> showDialog<T>({
  260. required WidgetBuilder builder,
  261. SmartDialogController? controller,
  262. Alignment? alignment,
  263. bool? clickDismiss,
  264. bool? usePenetrate,
  265. Color? maskColor,
  266. Widget? maskWidget,
  267. VoidCallback? onDismiss,
  268. VoidCallback? onMask,
  269. Duration? displayTime,
  270. String? tag,
  271. bool? keepSingle,
  272. bool? permanent,
  273. bool? useSystem,
  274. bool? bindPage,
  275. BuildContext? bindWidget,
  276. Rect? ignoreArea,
  277. }) {
  278. return SmartDialog.show(
  279. builder: builder,
  280. controller: controller,
  281. alignment: alignment,
  282. clickMaskDismiss: clickDismiss,
  283. usePenetrate: usePenetrate,
  284. useAnimation: null,
  285. animationType: null,
  286. nonAnimationTypes: null,
  287. animationBuilder: null,
  288. animationTime: null,
  289. maskColor: maskColor,
  290. maskWidget: maskWidget,
  291. onDismiss: onDismiss,
  292. onMask: onMask,
  293. displayTime: displayTime,
  294. tag: tag,
  295. backDismiss: clickDismiss,
  296. keepSingle: keepSingle,
  297. permanent: permanent,
  298. useSystem: useSystem,
  299. bindPage: bindPage,
  300. bindWidget: bindWidget,
  301. ignoreArea: ignoreArea,
  302. );
  303. }
  304. static Future<void> showToast(
  305. String msg, {
  306. SmartDialogController? controller,
  307. Duration? displayTime,
  308. Alignment? alignment,
  309. bool? clickMaskDismiss,
  310. Color? maskColor,
  311. Widget? maskWidget,
  312. VoidCallback? onDismiss,
  313. SmartToastType? displayType,
  314. }) =>
  315. SmartDialog.showToast(
  316. msg,
  317. controller: controller,
  318. displayTime: displayTime,
  319. alignment: alignment ?? Alignment.center,
  320. clickMaskDismiss: clickMaskDismiss,
  321. animationType: null,
  322. nonAnimationTypes: null,
  323. animationBuilder: null,
  324. usePenetrate: null,
  325. useAnimation: null,
  326. animationTime: null,
  327. maskColor: maskColor,
  328. maskWidget: maskWidget,
  329. onDismiss: onDismiss,
  330. onMask: null,
  331. consumeEvent: null,
  332. debounce: null,
  333. displayType: displayType,
  334. builder: null,
  335. );
  336. static Future<T?> showLoading<T>({
  337. String msg = '加载中',
  338. bool? clickDismiss,
  339. SmartDialogController? controller,
  340. Alignment? alignment,
  341. bool? usePenetrate,
  342. Color? maskColor,
  343. Widget? maskWidget,
  344. VoidCallback? onDismiss,
  345. VoidCallback? onMask,
  346. Duration? displayTime,
  347. }) {
  348. return SmartDialog.showLoading(
  349. msg: msg,
  350. controller: controller,
  351. alignment: alignment,
  352. clickMaskDismiss: clickDismiss,
  353. animationType: null,
  354. nonAnimationTypes: null,
  355. animationBuilder: null,
  356. usePenetrate: usePenetrate,
  357. useAnimation: null,
  358. animationTime: null,
  359. maskColor: maskColor,
  360. maskWidget: maskWidget,
  361. onDismiss: onDismiss,
  362. onMask: onMask,
  363. displayTime: displayTime,
  364. backDismiss: clickDismiss,
  365. builder: null,
  366. );
  367. }
  368. static Future<void> dismiss<T>({
  369. SmartStatus status = SmartStatus.smart,
  370. String? tag,
  371. T? result,
  372. bool force = false,
  373. }) =>
  374. SmartDialog.dismiss(
  375. status: status,
  376. tag: tag,
  377. result: result,
  378. force: force,
  379. );
  380. static Future<void> dismissLoading<T>({
  381. String? tag,
  382. T? result,
  383. bool force = false,
  384. }) =>
  385. SmartDialog.dismiss(
  386. status: SmartStatus.loading,
  387. tag: tag,
  388. result: result,
  389. force: force,
  390. );
  391. }
  392. /// 页面设置信息
  393. class PageSetting {
  394. /// 全屏Dialog
  395. final bool? fullscreenDialog;
  396. /// 是否可截屏
  397. final bool? allowSnapshotting;
  398. /// 页面类型
  399. final RouteType? routeType;
  400. /// 业务参数
  401. final Object? arguments;
  402. PageSetting({
  403. this.fullscreenDialog,
  404. this.allowSnapshotting,
  405. this.routeType = RouteType.cupertino,
  406. this.arguments,
  407. });
  408. @override
  409. String toString() => {
  410. 'fullscreenDialog': fullscreenDialog,
  411. 'allowSnapshotting': allowSnapshotting,
  412. 'routeType': routeType,
  413. 'arguments': arguments,
  414. }.toString();
  415. }
  416. enum RouteType {
  417. /// iOS样式路由
  418. cupertino,
  419. /// Android样式路由
  420. material,
  421. /// 普通路由(无动画效果)
  422. normal,
  423. }