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

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 ,主要工具类,创建页面,路由状态,监听跳转等:

import 'package:flutter/material.dart';
import 'package:flutter_blbl/page/home_page.dart';
import 'package:flutter_blbl/page/login_page.dart';
import 'package:flutter_blbl/page/registration_page.dart';
import 'package:flutter_blbl/page/video_detail_page.dart';

typedef RouteChangeListener(RouteStatusInfo current, RouteStatusInfo pre);

///创建页面
pageWrap(Widget child) {
  return MaterialPage(key: ValueKey(child.hashCode), child: child);
}

///获取routeStatus在页面栈中的位置,
int getPageIndex(List<MaterialPage> pages, RouteStatus routeStatus) {
  for (int i = 0; i < pages.length; i++) {
    MaterialPage page = pages[i];
    if (getStatus(page) == routeStatus) {
      return i;
    }
  }
  return -1;
}

///路由封装,路由状态
enum RouteStatus { login, registration, home, detail, unknown }

///获取page对应的routeStatus
RouteStatus getStatus(MaterialPage page) {
  if (page.child is LoginPage) {
    return RouteStatus.login;
  } else if (page.child is RegistrationPage) {
    return RouteStatus.registration;
  } else if (page.child is HomePage) {
    return RouteStatus.home;
  } else if (page.child is VideoDetailPage) {
    return RouteStatus.detail;
  } else {
    return RouteStatus.unknown;
  }
}

///路由信息
class RouteStatusInfo {
  final RouteStatus routeStatus;
  final Widget page;

  RouteStatusInfo(this.routeStatus, this.page);
}

class MyNavigator extends _RouteJumpListener {
  static MyNavigator _instance;

  RouteJumpListener _routeJump;

  List<RouteChangeListener> _listeners = [];
  RouteStatusInfo _current; //打开过的页面
  MyNavigator._();

  ///注册路由跳转逻辑
  void registerRouteJump(RouteJumpListener routeJumpListener) {
    this._routeJump = routeJumpListener;
  }

  ///监听路由页面跳转
  void addListener(RouteChangeListener listener) {
    if (!_listeners.contains(listener)) {
      _listeners.add(listener);
    }
  }

  ///移除监听
  void removeListener(RouteChangeListener listener) {
    _listeners.remove(listener);
  }

  @override
  void onJumpTo(RouteStatus routeStatus, {Map args}) {
    _routeJump.onJumpTo(routeStatus, args: args);
  }

  ///通知路由页面变化,currentPages当前页面,prePages上一次页面
  void notify(List<MaterialPage> currentPages, List<MaterialPage> prePages) {
    //路由堆栈无变化不处理
    if (currentPages == prePages) return;
    var current =
        RouteStatusInfo(getStatus(currentPages.last), currentPages.last.child);
    _notify(current);
  }

  void _notify(RouteStatusInfo current) {
    print('my_navigator:current:' + current.page.toString());
    print('my_navigator:pre:' + _current?.page.toString());
    _listeners.forEach((listener) {
      listener(current, _current);
    });
    _current = current;
  }

  static MyNavigator getInstance() {
    if (_instance == null) {
      _instance = MyNavigator._();
    }
    return _instance;
  }
}

abstract class _RouteJumpListener {
  void onJumpTo(RouteStatus routeStatus, {Map args});
}

typedef OnJumpTo = void Function(RouteStatus routeStatus, {Map args});

///定义路由跳转逻辑要实现功能
class RouteJumpListener {
  final OnJumpTo onJumpTo;

  RouteJumpListener({this.onJumpTo});
}

main.dart :

import 'package:data_plugin/bmob/bmob.dart';
import 'package:flutter/material.dart';
import 'package:data_plugin/data_plugin.dart';
import 'package:flutter_blbl/db/hi_cache.dart';
import 'package:flutter_blbl/model/navigator/my_navigator.dart';
import 'package:flutter_blbl/model/video_model.dart';
import 'package:flutter_blbl/page/home_page.dart';
import 'package:flutter_blbl/page/login_page.dart';
import 'package:flutter_blbl/page/registration_page.dart';
import 'package:flutter_blbl/page/video_detail_page.dart';
import 'package:flutter_blbl/utils/Constants.dart';
import 'package:flutter_blbl/utils/color.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:shared_preferences/shared_preferences.dart';

import 'db/PersistentStorage.dart';
import 'db/my_shared_preferences.dart';
import 'utils/toast.dart';

void main() {
  SharedPreferences.setMockInitialValues({});
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  const MyApp({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(title: 'An App', home: LoadingPage());
  }
}

class LoadingPage extends StatefulWidget {
  const LoadingPage({Key key}) : super(key: key);

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<LoadingPage> {
  BiliRouteDelegate _routeDelegate = BiliRouteDelegate();

  // Future<dynamic> fetchName() async {
  //   SharedPreferences pf = await SharedPreferences.getInstance();
  //   String response1 = pf.getString(SPName);
  //   String response = await PersistentStorage().getStorage(SPName);
  //   userName = response;
  //   print('姓名' + response + "," + userName + response1);
  //   return response;
  // }

  @override
  void initState() {
    Bmob.initMasterKey(
        "https://api2.bmob.cn",
        "bdb1a27490a65408db30a862b5023ffa",
        "1ef17ded7609019652b448773b06e1bd",
        "574fb1ffbe28cce59414d678d1817eca");
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    var widget = Router(
      //定义route
      routerDelegate: _routeDelegate,
    );
    return MaterialApp(
      home: widget,
      theme: ThemeData(primarySwatch: white),
    );

    //如果要请求网络或初始化数据可以调用下面
    // return FutureBuilder<dynamic>(
    //     //进行初始化
    //     future: fetchName(),
    //     builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
    //       //加载完成显示页面,否则显示加载中
    //       print("加载状态:" + snapshot.toString());
    //       var widget;
    //       if (snapshot.connectionState == ConnectionState.done) {
    //         widget = Router(
    //           //定义route
    //           routerDelegate: _routeDelegate,
    //         );
    //       }
    //       return MaterialApp(
    //         home: widget,
    //         theme: ThemeData(primarySwatch: white),
    //       );
    //     });
  }
}

///路由代理
class BiliRouteDelegate extends RouterDelegate
    with ChangeNotifier, PopNavigatorRouterDelegateMixin {
  final GlobalKey<NavigatorState> navigatorKey;

  //默认进入首页
  RouteStatus _routeStatus = RouteStatus.home;

  BiliRouteDelegate() : navigatorKey = GlobalKey<NavigatorState>() {
    //实现跳转逻辑
    MyNavigator.getInstance().registerRouteJump(
        RouteJumpListener(onJumpTo: (RouteStatus routeStatus, {Map args}) {
      _routeStatus = routeStatus;
      //主页会往详情页传值
      if (routeStatus == RouteStatus.detail) {
        this.videoModel = args['videoMo'];
      }
      notifyListeners();
    }));
  }
  List<MaterialPage> pages = []; //存放所有页面的列表
  VideoModel videoModel;

  @override
  Widget build(BuildContext context) {
    var index = getPageIndex(pages, routeStatus);
    List<MaterialPage> tempPages = pages;
    //如果需要打开的页面已经存在,改页面和上面的页面进行出栈
    if (index != -1) {
      tempPages = tempPages.sublist(0, index);
    }
    var page;
    print('routeStatus:' + routeStatus.toString());
    if (routeStatus == RouteStatus.home) {
      //跳转到首页,将其它页面都出栈,然后创建新的首页
      pages.clear();
      page = pageWrap(HomePage());
    } else if (routeStatus == RouteStatus.detail) {
      //打开详情页
      page = pageWrap(VideoDetailPage(videoModel));
    } else if (routeStatus == RouteStatus.registration) {
      //打开注册页面
      page = pageWrap(RegistrationPage());
    } else if (routeStatus == RouteStatus.login) {
      page = pageWrap(LoginPage());
    }

    //重新创建堆栈数组
    tempPages = [...tempPages, page];
    pages = tempPages;
    //通知路由发生变化
    MyNavigator.getInstance().notify(tempPages, pages);
    return WillPopScope(
        child: Navigator(
          key: navigatorKey,
          pages: pages,
          onPopPage: (route, result) {
            if (route.settings is MaterialPage) {
              if ((route.settings as MaterialPage).child is LoginPage) {
                //
              }
            }
            //在这里控制是否可以返回上一页
            if (!route.didPop(result)) {
              return false;
            }
            var temPages = [...pages];
            pages.removeLast();
            //通知路由变化
            MyNavigator.getInstance().notify(pages, temPages);
            return true;
          },
        ),
        //手机返回键返回到上一页
        onWillPop: () async => !await navigatorKey.currentState.maybePop());
  }

  RouteStatus get routeStatus {
    //返回要进入的页面,登录或主页等
    if (_routeStatus != RouteStatus.registration && USERNAME == null) {
      return _routeStatus = RouteStatus.login;
    } else if (videoModel != null) {
      return _routeStatus = RouteStatus.detail;
    } else {
      return _routeStatus;
    }
  }

  @override
  Future<void> setNewRoutePath(configuration) {}
}

页面跳转:

//不传值 
MyNavigator.getInstance().onJumpTo(RouteStatus.registration);

//传值
MyNavigator.getInstance().onJumpTo(RouteStatus.detail,
  args: {'videoMo': VideoModel(1212)});

====== 扩展

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

  var listener;
  @override
  void initState() {
    super.initState();
    MyNavigator.getInstance().addListener((current, pre) {
      print('current:' + current.page.toString());
      print('current:' + pre.page.toString());
      if (widget == current.page || current.page is HomePage) {
        print('打开了首页:onResume');
      } else if (widget == pre?.page || pre?.page is HomePage) {
        print('首页:onPause');
      }
    });
  }

  @override
  void dispose() {
    MyNavigator.getInstance().removeListener(this.listener);
    super.dispose();
  }

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值