import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:lszlgl/base/base_state.dart'; import 'package:lszlgl/page/unknow_page.dart'; /// 路由监听 class NavigatorPageObserver extends NavigatorObserver { static final NavigatorPageObserver instance = NavigatorPageObserver._(); NavigatorPageObserver._(); /// 路由信息 /// 外部可以调用[MyNavigator.routeInfo]取值 final RouteModel routeInfo = RouteModel(); @override void didPush(Route route, Route? previousRoute) { // 路由入栈 routeInfo.update(showRoute: route, hideRoute: previousRoute); debugPrint('路由入栈 didPush:$routeInfo'); _pageRecord(); } @override void didPop(Route route, Route? previousRoute) { // 路由出栈 routeInfo.update(showRoute: previousRoute, hideRoute: route); debugPrint('路由出栈 didPop:$routeInfo'); _pageRecord(); } @override void didRemove(Route route, Route? previousRoute) { /* routeInfo.update(showRoute: previousRoute, hideRoute: route); // 路由移除 debugPrint('路由移除 didRemove:$routeInfo'); */ } @override void didReplace({Route? newRoute, Route? oldRoute}) { // 路由替换 routeInfo.update(showRoute: newRoute, hideRoute: oldRoute); debugPrint('路由替换 didReplace:$routeInfo'); _pageRecord(); } @override void didStartUserGesture(Route route, Route? previousRoute) { // 手势控制路由 } @override void didStopUserGesture() { // 手势控制路由结束 } /// 页面统计 void _pageRecord() { if (routeInfo.hideName != null) { // 页面关闭 } if (routeInfo.showName != null) { // 页面显示 } } } class RouteModel { /// 显示的路由 Route? showRoute; /// 隐藏的路由 Route? hideRoute; /// 前一个 widget 是否是一个页面(最好统一使用自定义路由) bool get isPage => hideRoute is PageRoute; /// 前一个 widget 是否是弹框 bool get isDialog => !isPage; RouteModel(); void update({Route? showRoute, Route? hideRoute}) { if (showRoute != null) this.showRoute = showRoute; if (hideRoute != null) this.hideRoute = hideRoute; } /// 显示路由的名称 String? get showName => showRoute?.settings.name; /// 显示路由的配置信息 Object? get showPageSetting => showRoute?.settings.arguments; /// 显示路由的业务参数 Object? get showArguments { Object? args = showRoute?.settings.arguments; if (args != null) { if (args is PageSetting) { // PageSetting的参数 return args.arguments; } else { // 原参数 return args; } } return null; } /// 隐藏/移除路由的名称 String? get hideName => hideRoute?.settings.name; /// 隐藏/移除路由的配置信息 Object? get hidePageSetting => hideRoute?.settings.arguments; /// 隐藏/移除路由的业务参数 Object? get hideArguments { Object? args = hideRoute?.settings.arguments; if (args != null) { if (args is PageSetting) { // PageSetting的参数 return args.arguments; } else { // 原参数 return args; } } return null; } @override String toString() { StringBuffer sb = StringBuffer(); if (showRoute != null) { sb.write('showName:$showName showArgs:$showArguments '); } if (hideRoute != null) { sb.write('hideName:$hideName hideArgs:$hideArguments'); } return sb.toString(); } } /// 路由页面Builder函数 typedef MyNavigatorBuilder = Widget Function(BuildContext context, Object? arguments); class MyNavigator { MyNavigator._(); /// 全局Navigator static final GlobalKey navigator = GlobalKey(); /// 全局路由监听 static final NavigatorPageObserver navigatorObs = NavigatorPageObserver.instance; /// 最新路由信息 static RouteModel routeInfo = navigatorObs.routeInfo; /// Widget路由监听 static final RouteObserver> routeObs = RouteObserver>(); /// 自定义路由拦截 static Route generateRoute(RouteSettings settings) { var routeName = settings.name; // 是否dialog bool fullscreenDialog = false; // 是否可截屏 bool allowSnapshotting = true; // 业务传参 Object? args; // 页面路由样式 RouteType routeType = RouteType.cupertino; // 获取路由配置 if (settings.arguments != null && settings.arguments is PageSetting) { PageSetting pageSetting = settings.arguments as PageSetting; fullscreenDialog = pageSetting.fullscreenDialog ?? false; allowSnapshotting = pageSetting.allowSnapshotting ?? true; routeType = pageSetting.routeType ?? RouteType.cupertino; args = pageSetting.arguments; } else { args = settings.arguments; } // 创建路由 MyNavigatorBuilder? builder = rRouteMap[routeName]; builder ??= (context, args) => UnknownPage(name: settings.name); switch (routeType) { case RouteType.material: return MaterialPageRoute( builder: (context) => builder!.call(context, args), settings: settings, fullscreenDialog: fullscreenDialog, allowSnapshotting: allowSnapshotting, ); case RouteType.normal: return PageRouteBuilder( pageBuilder: (context, animation, secondaryAnimation) => builder!.call(context, args), settings: settings, fullscreenDialog: fullscreenDialog, allowSnapshotting: allowSnapshotting, ); case RouteType.cupertino: default: return CupertinoPageRoute( builder: (context) => builder!.call(context, args), settings: settings, fullscreenDialog: fullscreenDialog, allowSnapshotting: allowSnapshotting, ); } } /// 添加新页面 static Future push( String name, { Object? args, bool fullscreenDialog = false, bool allowSnapshotting = true, RouteType routeType = RouteType.cupertino, }) { PageSetting setting = PageSetting( arguments: args, fullscreenDialog: fullscreenDialog, allowSnapshotting: allowSnapshotting, routeType: routeType, ); return navigator.currentState!.pushNamed(name, arguments: setting); } /// 添加新页面 并 替换当前页面 static Future pushReplace( String name, { TO? result, Object? args, bool fullscreenDialog = false, bool allowSnapshotting = true, RouteType routeType = RouteType.cupertino, }) { PageSetting setting = PageSetting( arguments: args, fullscreenDialog: fullscreenDialog, allowSnapshotting: allowSnapshotting, routeType: routeType, ); return navigator.currentState!.pushReplacementNamed(name, result: result, arguments: setting); } /// 添加新页面 并 移除一些页面,直至predicate返回true static Future pushRemoveUntil( String name, RoutePredicate predicate, { Object? args, bool fullscreenDialog = false, bool allowSnapshotting = true, RouteType routeType = RouteType.cupertino, }) { PageSetting setting = PageSetting( arguments: args, fullscreenDialog: fullscreenDialog, allowSnapshotting: allowSnapshotting, routeType: routeType, ); return navigator.currentState!.pushNamedAndRemoveUntil(name, predicate, arguments: setting); } /// 移除路由栈顶页面 static void pop([T? result]) { return navigator.currentState!.pop(result); } /// 移除路由栈顶页面 并 添加新页面 static Future popAndPush( String name, { TO? result, Object? args, bool fullscreenDialog = false, bool allowSnapshotting = true, RouteType routeType = RouteType.cupertino, }) { PageSetting setting = PageSetting( arguments: args, fullscreenDialog: fullscreenDialog, allowSnapshotting: allowSnapshotting, routeType: routeType, ); return navigator.currentState!.popAndPushNamed(name, result: result, arguments: setting); } /// 移除一些页面, 直至predicate返回true static void popUntil(RoutePredicate predicate) { return navigator.currentState!.popUntil(predicate); } /// 显示Dialog static Future showDialog({ required WidgetBuilder builder, SmartDialogController? controller, Alignment? alignment, bool? clickDismiss, bool? usePenetrate, Color? maskColor, Widget? maskWidget, VoidCallback? onDismiss, VoidCallback? onMask, Duration? displayTime, String? tag, bool? keepSingle, bool? permanent, bool? useSystem, bool? bindPage, BuildContext? bindWidget, Rect? ignoreArea, }) { return SmartDialog.show( builder: builder, controller: controller, alignment: alignment, clickMaskDismiss: clickDismiss, usePenetrate: usePenetrate, useAnimation: null, animationType: null, nonAnimationTypes: null, animationBuilder: null, animationTime: null, maskColor: maskColor, maskWidget: maskWidget, onDismiss: onDismiss, onMask: onMask, displayTime: displayTime, tag: tag, backDismiss: clickDismiss, keepSingle: keepSingle, permanent: permanent, useSystem: useSystem, bindPage: bindPage, bindWidget: bindWidget, ignoreArea: ignoreArea, ); } static Future showToast( String msg, { SmartDialogController? controller, Duration? displayTime, Alignment? alignment, bool? clickMaskDismiss, Color? maskColor, Widget? maskWidget, VoidCallback? onDismiss, SmartToastType? displayType, }) => SmartDialog.showToast( msg, controller: controller, displayTime: displayTime, alignment: alignment ?? Alignment.center, clickMaskDismiss: clickMaskDismiss, animationType: null, nonAnimationTypes: null, animationBuilder: null, usePenetrate: null, useAnimation: null, animationTime: null, maskColor: maskColor, maskWidget: maskWidget, onDismiss: onDismiss, onMask: null, consumeEvent: null, debounce: null, displayType: displayType, builder: null, ); static Future showLoading({ String msg = '加载中', bool? clickDismiss, SmartDialogController? controller, Alignment? alignment, bool? usePenetrate, Color? maskColor, Widget? maskWidget, VoidCallback? onDismiss, VoidCallback? onMask, Duration? displayTime, }) { return SmartDialog.showLoading( msg: msg, controller: controller, alignment: alignment, clickMaskDismiss: clickDismiss, animationType: null, nonAnimationTypes: null, animationBuilder: null, usePenetrate: usePenetrate, useAnimation: null, animationTime: null, maskColor: maskColor, maskWidget: maskWidget, onDismiss: onDismiss, onMask: onMask, displayTime: displayTime, backDismiss: clickDismiss, builder: null, ); } static Future dismiss({ SmartStatus status = SmartStatus.smart, String? tag, T? result, bool force = false, }) => SmartDialog.dismiss( status: status, tag: tag, result: result, force: force, ); static Future dismissLoading({ String? tag, T? result, bool force = false, }) => SmartDialog.dismiss( status: SmartStatus.loading, tag: tag, result: result, force: force, ); } /// 页面设置信息 class PageSetting { /// 全屏Dialog final bool? fullscreenDialog; /// 是否可截屏 final bool? allowSnapshotting; /// 页面类型 final RouteType? routeType; /// 业务参数 final Object? arguments; PageSetting({ this.fullscreenDialog, this.allowSnapshotting, this.routeType = RouteType.cupertino, this.arguments, }); @override String toString() => { 'fullscreenDialog': fullscreenDialog, 'allowSnapshotting': allowSnapshotting, 'routeType': routeType, 'arguments': arguments, }.toString(); } enum RouteType { /// iOS样式路由 cupertino, /// Android样式路由 material, /// 普通路由(无动画效果) normal, }