美文网首页
flutter 路由及Overlay

flutter 路由及Overlay

作者: liboxiang | 来源:发表于2020-04-24 00:45 被阅读0次

引用以下两篇文章,已经很详细说明路由原理了
https://juejin.im/post/5c8db8e8f265da2d864b889f

https://juejin.im/post/5c8db8fff265da2de52dd80f

Overlay rebuild的时候是会调用List<OverlayEntry> _entries内OverlayEntry的builder方法rebuild child widget的

MaterialApp

MaterialApp(
        routes: {'main': (context) => Counter()},
        onGenerateRoute: (setting) {
          // setting.name
          return MaterialPageRoute(builder: (_) => Counter());
        },
      ),

_MaterialAppState内使用了WidgetsApp,pushName的时候先判断routes是否有,routes没有再返回widget.onGenerateRoute(settings)

WidgetsApp(
      routes: widget.routes,
      onGenerateRoute: widget.onGenerateRoute,
)

Route<dynamic> _onGenerateRoute(RouteSettings settings) {
    final String name = settings.name;
    final WidgetBuilder pageContentBuilder = name == Navigator.defaultRouteName && widget.home != null
        ? (BuildContext context) => widget.home
        : widget.routes[name];

    if (pageContentBuilder != null) {
      final Route<dynamic> route = widget.pageRouteBuilder<dynamic>(
        settings,
        pageContentBuilder,
      );
      return route;
    }
    if (widget.onGenerateRoute != null)
      return widget.onGenerateRoute(settings);
    return null;
  }

Widget build(BuildContext context) {
    Widget navigator;
    if (_navigator != null) {
      navigator = Navigator(
        ...
        onGenerateRoute: _onGenerateRoute,
        ...
      );
    }
}

路由管理封装

为了达到的目的:

  • 使用popUntil等和路由名称相关的方法
  • 判断当前widget或者state是否是App正在显示的页面
  • 新接手项目的成员可以快速找到相关页面

不使用 Navigator.pushNamed(context, 'routeName',arguments:arguemntsOjbect);onGenerateRoute组合的原因是:pushNamed传参麻烦;没有参数类型提示;且通过pushName跳转页面的话不能通过代码直接跳到相应页面(如通过pushName跳转到A页面,那在pushName代码处不能通过点击A类名跳转到A类代码处)。

用法

  • 监听路由跳转
MaterialApp(
      navigatorObservers: [CustomNavigatorObserver()],
)
  • 跳转到页面A
Navigator.of(context).push(Routes.routeForPage(page:PageA(),),);
  • 跳转到页面A,同时传递Bloc
Navigator.of(context, rootNavigator: true).push(
      Routes.routeForPage(
          page: PageA(),
          pageWrapBuilder: (page, _) => BlocProvider(
                  create: (context) => bloc,
                  child: page,
          ),
      ),
);
  • 跳转到页面A,同时自定义Route
Navigator.of(context, rootNavigator: true).push(
      Routes.routeForPage(
          page: PageA(),
          customRouteBuilder: (pageBuilder, settings) => CustomRoute(
                settings: settings,
                pageBuilder: (builderContext, _, __) => pageBuilder(builderContext),
              ),
      ),
);
  • 返回页面A
Navigator.popUntil(context, Routes.filterRoute(Routes.pageA));
  • 判断页面A是否在路由最顶层,也就是正在显示页面A
Routes.isPageAtTop(context,[PageA页面对应的widget或者state对象])

源码

typedef PageWrapBuilder = Widget Function(Widget child, BuildContext context);
typedef RouteBuilder = Route<dynamic> Function(WidgetBuilder pageBuilder, RouteSettings setting);

class Routes {
  Routes._();
  static Routes _routes = Routes._();
  List<String> _routesName = [];

  ///页面A,此行代码可有可无,更多是为了让不熟悉项目的人快速找到相应页面。如果没用此方式重新定义路由名称的获取,则可以直接通过`Routes.routeNameForPage(PageA)`得到
  static String pageA = _routeNameForPage(PageA);

  static String _routeNameForPage(Type page) => page.toString();

  ///创建Route,routeName是page.runtimeType.toString()
  static Route<dynamic> routeForPage({
    @required Widget page,
    PageWrapBuilder pageWrapBuilder,
    RouteBuilder customRouteBuilder,
  }) {
    assert(page != null);
    String routeName = _routeNameForPage(page.runtimeType);
    WidgetBuilder builder;
    if (pageWrapBuilder != null) {
      builder = (context) => pageWrapBuilder(page, context);
    } else {
      builder = (_) => page;
    }
    if (customRouteBuilder != null) {
      return customRouteBuilder(builder, RouteSettings(name: routeName));
    } else {
      return MaterialPageRoute(builder: builder, settings: RouteSettings(name: routeName));
    }
  }

  ///当前页面是否在路由顶层
  static bool isPageAtTop(BuildContext context, dynamic page) {
    assert(page is State || page is Widget);
    String currentRouteName = ModalRoute.of(context).settings.name;
    Widget widget = page is Widget ? page : (page as State).widget;
    return _routeNameForPage(widget.runtimeType) == currentRouteName;
  }

  ///页面对应的路由名称
  static String routeNameForPage(dynamic page) {
    assert(page is State || page is Widget);
    Widget widget = page is Widget ? page : (page as State).widget;
    return _routeNameForPage(widget.runtimeType);
  }

  ///记录push的routeName
  static _pushRouteName(String routeName) {
    _routes._routesName.add(routeName ?? routeName.toString());
  }

  ///pop的时候删除最后一个routeName
  static _popRouteName() {
    if (_routes._routesName.isNotEmpty) {
      _routes._routesName.removeLast();
    }
  }
  ///最后一个route的name
  static String get previousRouteName {
    if (_routes._routesName.length > 1) {
      return _routes._routesName[_routes._routesName.length - 2];
    }
    return null;
  }

  ///最后一个route的name
  ///因为弹框之类的所有route name(为null)都记录在_routesName中,所以建此方法返回_routesName中在该类中定义的route name的最后一个
  static String get previousNotNullRouteName {
    return _routes._routesName?.reversed?.firstWhere((test) => test != null, orElse: () => null);
  }

  static RoutePredicate filterRoute(String routeName) => (route) => route.settings.name == routeName;
}

///用来监听路由push和pop
class CustomNavigatorObserver extends NavigatorObserver {
  @override
  void didPush(Route route, Route previousRoute) {
    super.didPush(route, previousRoute);

    ///如果没有给route设置name的话,默认name是null
    Routes._pushRouteName(route.settings.name);
  }

  @override
  void didPop(Route route, Route previousRoute) {
    super.didPop(route, previousRoute);
    Routes._popRouteName();
  }

  @override
  void didRemove(Route route, Route previousRoute) {
    super.didRemove(route, previousRoute);
    Routes._popRouteName();
  }

  @override
  void didReplace({Route newRoute, Route oldRoute}) {
    super.didReplace(newRoute: newRoute, oldRoute: oldRoute);
    Routes._popRouteName();
    Routes._pushRouteName(newRoute.settings.name);
  }
}

相关文章

网友评论

      本文标题:flutter 路由及Overlay

      本文链接:https://www.haomeiwen.com/subject/bubaihtx.html