Flutter上线项目实战——路由篇

Posted callme大帅

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter上线项目实战——路由篇相关的知识,希望对你有一定的参考价值。

1. 应用场景

开发中经常遇到

  • 路由跳转时拿不到context怎么办,eg: token失效/异地登录跳转登录页面。
  • 获取不到当前路由名称怎么办,eg: 点击push推送跳转指定路由,如果已经在当前页面就replace,如果不在就push。
  • 注册监听路由跳转,做一些想做的事情,eg:不同路由,显示不同状态栏颜色。
  • 监听当前页面获取、失去焦点
  • 等等…

2. 解决方案

解决思路:

  1. MaterialApp 的routes属性赋值路由数组,navigatorObservers属性赋值路由监听对象NavigationUtil。
  2. 在NavigationUtil里实现NavigatorObserver的didPush/didReplace/didPop/didRemove,并记录到路由栈
    List _mRoutes中。
  3. 将实时记录的路由跳转,用stream发一个广播,哪里需要哪里注册。
  4. 用mixin实现当前页面获取、失去焦点,监听当前路由变化,触发onFocus,onBlur。

3. 具体实现

支持空安全版本 戳我

main.dart

MaterialApp(
    navigatorObservers: [NavigationUtil.getInstance()],
    routes: NavigationUtil.configRoutes,
    ...
)

navigation_util.dart

class RouteInfo 
  Route currentRoute;
  List<Route> routes;

  RouteInfo(this.currentRoute, this.routes);

  @override
  String toString() 
    return 'RouteInfocurrentRoute: $currentRoute, routes: $routes';
  


class NavigationUtil extends NavigatorObserver 
  static NavigationUtil _instance;

  static Map<String, WidgetBuilder> configRoutes = 
    SplashPage.sName: (_) => SplashPage(),
    ScanQrPage.sName: (_) => ScanQrPage(),
  ;

  ///路由信息
  RouteInfo _routeInfo;
  RouteInfo get routeInfo => _routeInfo;
  ///stream相关
  static StreamController _streamController;
  StreamController<RouteInfo> get streamController=> _streamController;
  ///用来路由跳转
  static NavigatorState navigatorState;


  static NavigationUtil getInstance() 
    if (_instance == null) 
      _instance = new NavigationUtil();
      _streamController = StreamController<RouteInfo>.broadcast();
    
    return _instance;
  

  ///push页面
  Future<T> pushNamed<T>(String routeName, WidgetBuilder builder, bool fullscreenDialog) 
    return navigatorState.push<T>(
      MaterialPageRoute(
        builder: builder ?? configRoutes[routeName],
        settings: RouteSettings(name: routeName),
        fullscreenDialog: fullscreenDialog ?? false,
      ),
    );
  

  ///replace页面
  Future<T> pushReplacementNamed<T, R>(String routeName, WidgetBuilder builder, bool fullscreenDialog) 
    return navigatorState.pushReplacement<T, R>(
      MaterialPageRoute(
        builder: builder ?? configRoutes[routeName],
        settings: RouteSettings(name: routeName),
        fullscreenDialog: fullscreenDialog ?? false,
      ),
    );
  

  /// pop 页面
  pop<T>([T result]) 
    navigatorState.pop<T>(result);
  

  pushNamedAndRemoveUntil(String newRouteName) 
    return navigatorState.pushNamedAndRemoveUntil(newRouteName, (Route<dynamic> route) => false);
  

  @override
  void didPush(Route route, Route previousRoute) 
    super.didPush(route, previousRoute);
    if (_routeInfo == null) 
      _routeInfo = new RouteInfo(null, new List<Route>());
    
    ///这里过滤调push的是dialog的情况
    if (route is CupertinoPageRoute || route is MaterialPageRoute) 
      _routeInfo.routes.add(route);
      routeObserver();
    
  

  @override
  void didReplace(Route newRoute, Route oldRoute) 
    super.didReplace();
    if (newRoute is CupertinoPageRoute || newRoute is MaterialPageRoute) 
      _routeInfo.routes.remove(oldRoute);
      _routeInfo.routes.add(newRoute);
      routeObserver();
    
  

  @override
  void didPop(Route route, Route previousRoute) 
    super.didPop(route, previousRoute);
    if (route is CupertinoPageRoute || route is MaterialPageRoute) 
      _routeInfo.routes.remove(route);
      routeObserver();
    
  

  @override
  void didRemove(Route removedRoute, Route oldRoute) 
    super.didRemove(removedRoute, oldRoute);
    if (removedRoute is CupertinoPageRoute || removedRoute is MaterialPageRoute) 
      _routeInfo.routes.remove(removedRoute);
      routeObserver();
    
  



  void routeObserver() 
    _routeInfo.currentRoute = _routeInfo.routes.last;
    navigatorState = _routeInfo.currentRoute.navigator;
    _streamController.sink.add(_routeInfo);
  

navigation_mixin.dart

mixin NavigationMixin<T extends StatefulWidget> on State<T> 
  StreamSubscription<RouteInfo> streamSubscription;
  Route lastRoute;

  @override
  void initState() 
    super.initState();

    streamSubscription = NavigationUtil.getInstance().streamController.stream.listen((RouteInfo routeInfo) 
      if (routeInfo.currentRoute.settings.name == routName) 
        onFocus();
      
      /// 第一次监听到路由变化
      if (lastRoute == null) 
        onBlur();
      
      /// 上一个是该页面,新的路由不是该页面
      if (lastRoute?.settings?.name == routName && routeInfo.currentRoute.settings.name != routName) 
        onBlur();
      
      lastRoute = routeInfo.currentRoute;

    );
  

  @override
  void dispose() 
    super.dispose();
    streamSubscription?.cancel();
    streamSubscription = null;
  

  @protected
  String get routName;

  @protected
  void onBlur() 

  

  @protected
  void onFocus() 

  

4. 如何使用

token失效跳转

case 401:
    ToastUtil.showRed('登录失效,请重新登陆');
    UserDao.clearAll();
    NavigationUtil.getInstance().pushNamedAndRemoveUntil(LoginPage.sName);
    break;

点击push推送跳转

static jumpPage(String pageName, [WidgetBuilder builder]) 
    String currentRouteName = NavigationUtil.getInstance().currentRoute.settings.name;
    // 如果是未登录,不跳转
    if (NavigationUtil.getInstance().routes[0].settings.name != MainPage.sName) 
      return;
    

    // 如果已经是当前页面就replace
    if (currentRouteName == pageName) 
      NavigationUtil.getInstance().pushReplacementNamed(pageName, builder);
     else 
      NavigationUtil.getInstance().pushNamed(pageName, builder);
    

监听路由改变状态栏颜色

class StatusBarUtil 
      static List<String> lightRouteNameList = [
        TaskhallPage.sName,
        //...
      ];
      static List darkRoutNameList = [
        SplashPage.sName,
        LoginPage.sName,
        MainPage.sName,
        //...
      ];
      
      static init() 
        NavigationUtil.getInstance().streamController.stream.listen((state) 
            setupStatusBar(state[state.length - 1]);
        )
      
    
      setupStatusBar(Route currentRoute) 
        if (lightRouteNameList.contains(currentRoute.settings.name)) 
          setLight();
         else if (darkRoutNameList.contains(currentRoute.settings.name)) 
          setDart();
        
      

当前页面获取、失去焦点

class _ChatPageState extends State<ChatPage> with NavigationMixin<ChatPage> 
  ...
  @override
  String get routName => ChatPage.sName;
  
  @override
  void onBlur() 
  	super.onBlur();
  	// do something
  
  
  @override
  void onFocus() 
  	super.onFocus();
  	// do something
  

  

完结,撒花🎉

以上是关于Flutter上线项目实战——路由篇的主要内容,如果未能解决你的问题,请参考以下文章

Flutter上线项目实战——性能优化篇(未完待续)

Flutter上线项目实战——性能优化篇(未完待续)

VUE项目实战66上线-通过node创建Web服务器

VUE项目实战66上线-通过node创建Web服务器

Flutter上线项目实战——防止录屏

Flutter上线项目实战——防止录屏