likes
comments
collection
share

Flutter - 升级到3.24后页面还会多次rebuild吗?🧐

作者站长头像
站长
· 阅读数 41

BiliBili: www.bilibili.com/video/BV1ii…

一、问题回顾

在今年 2月份Flutter 官方正式了 3.19.0 版本,当时我们并没有着急尝鲜,直到 3月中旬,为了解决安卓端的崩溃率飙升问题,被迫无奈升级到 3.19.3, 结果踩了个坑,页面居然会多次 rebuild ~

二、波折

不过当时(日期:3.19)所提 PR 并不被接受,因为其修改内容是直接给了个选项 createDependency,代码如下:

static ModalRoute<T>? of<T extends Object?>(
  BuildContext context, {
  bool createDependency = true,
}) {
  _ModalScopeStatus? widget;
  if (createDependency) {
    widget = context.dependOnInheritedWidgetOfExactType<_ModalScopeStatus>();
  } else {
    widget = context
        .getElementForInheritedWidgetOfExactType<_ModalScopeStatus>()
        ?.widget as _ModalScopeStatus?;
  }
  return widget?.route as ModalRoute<T>?;
}

如果将 createDependency 参数设置为 false,则不与 BuildContext 建立依赖关系,这被认定为是 footgun

Flutter - 升级到3.24后页面还会多次rebuild吗?🧐

footgun: 指在尝试解决问题时,无意中导致更多问题或损害自己利益的行为

因为审核人担心如果允许了这个参数的加入,则后续可能会有开发者提出因为这个参数没有正确设置为 true,而导致在确实需要重新构建时却无法正常自动刷新视图的 bug 问题。最后建议提供类似 ModalRoute.argumentsOf(context) 的方式,进而尽量避免造成 rebuild

这种方式的好处就跟 MediaQuery.of(context).viewInsets 对比 MediaQuery.viewInsetsOf(context) 一样,后者只会在 viewInsets 发生变化时才会 rebuild, 而前者只要是 MediaQueryData 的值(如: size)有变化就会 rebuild

很好的提议 👍,不过我当时(日期:4.16)因为太忙,表明自己没时间去处理,并将 PR 关闭了,暂时靠补丁苟着。

当时有人提出另外一种较为安全的临时处理方案,就是遍历找出 _ModalScopeStatus,从而拿到 ModalRoute,以此来绕过绑定 BuildContext 的过程,相关代码如下。

/// replace ModalRoute.of(context)
class MyModalRoute {
  static ModalRoute<dynamic>? of(BuildContext context) {
    ModalRoute<dynamic>? route;
    context.visitAncestorElements((element) {
      if(element.widget.runtimeType.toString() == '_ModalScopeStatus') {
        dynamic widget = element.widget;
        route = widget.route as ModalRoute;
        return false;
      }
      return true;
    });
    return route;
  }
}

使用方式也很简单,只需要将原来的 ModalRoute.of(context) 替换为 MyModalRoute.of(context) 即可。

不过后面有人发现,在使用 代码混淆 功能时,该方案会失效。因为他是按字符串找到的 _ModalScopeStatus,但是在混淆的时候会重命名相关类名,所以还在 3.19.x 小伙伴们,请自行根据自身情况,考虑是否使用。

时间过的很快,转眼来到 5月份,一看这个问题还没有处理,于是抽了点时间按照建议,做了些调整,于 5.24 提交了 PR (github.com/flutter/flu…) ,并在 5.30 完成合并。

三、使用

PR 的改动也终于随 Flutter 3.24.0 正式版发布了,新增了如下三个方法:

  • isCurrentOf: 对应 isCurrent,用于判断当前路由是否为栈顶路由。
  • canPopOf: 对应 canPop,用于判断当前路由是否可以弹出/关闭。
  • settingsOf: 对应 settings,当前路由的配置(路由名、参数)。

之前取路由参数的方式如下代码所示

final arguments = ModalRoute.of(context)?.settings.arguments;

需调整为

final arguments = ModalRoute.settingsOf(context)?.arguments;

对,这样就可以了,不调整的话还是会造成 rebuild 的 ~

四、最后

其实对于我个人而言,我还是比较钟意添加 createDependency 参数这种方式的,让开发者自由选择。

Flutter - 升级到3.24后页面还会多次rebuild吗?🧐

而且在做路由监听的时候依旧还是得用到 ModalRoute.of(context) ~

void main() {
  runApp(MyApp());
}

RouteObserver<PageRoute> routeObserver = RouteObserver<PageRoute>();

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      ...
      navigatorObservers: [routeObserver],
      ...,
    );
  }
}

class _MyPageState extends State<MyPage> with RouteAware {
...
  @override
  void didChangeDependencies() {
    super.didChangeDependencies();

    // 这里用到了 ModalRoute.of(context)
    routeObserver.subscribe(this, ModalRoute.of(context)!);
  }

  @override
  void dispose() {
    routeObserver.unsubscribe(this);

    super.dispose();
  }
  ...
}

好吧,本篇到此结束,感谢大家的支持,我们下次再见! 👋

转载自:https://juejin.cn/post/7401065821597581348
评论
请登录