关于 MediaQuery 咱们介绍过不少,比方在之前的《MediaQuery 和 build 优化你不知道的隐秘》里就介绍过,要慎重在 Scaffold 之外运用 MediaQuery.of(context) ,这是因为 MediaQuery.ofBuildContext 的绑定或许会导致一些不必要的性能开销,例如键盘弹起时,会导致相关的 MediaQuery.of(context) 绑定的页面出现重构。

比方下面这个比方,咱们在 MyHomePage 里运用了 MediaQuery.of(context).size 并打印输出,然后跳转到 EditPage 页面,弹出键盘 ,这时分会发生什么情况?


class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("######### MyHomePage ${MediaQuery.of(context).size}");
    return Scaffold(
      body: Container(
        alignment: Alignment.center,
        child: InkWell(
          onTap: () {
            Navigator.of(context).push(CupertinoPageRoute(builder: (context) {
              return EditPage();
            }));
          },
          child: new Text(
            "Click",
            style: TextStyle(fontSize: 50),
          ),
        ),
      ),
    );
  }
}
class EditPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: new Text("ControllerDemoPage"),
      ),
      extendBody: true,
      body: Column(
        children: [
          new Spacer(),
          new Container(
            margin: EdgeInsets.all(10),
            child: new Center(
              child: new TextField(),
            ),
          ),
          new Spacer(),
        ],
      ),
    );
  }
}

如下图 log 所示 , 能够看到在键盘弹起来的进程,因为 bottom 发生改动,所以 MediaQueryData 发生了改动,然后导致上一级的 MyHomePage 虽然不可见,可是在键盘弹起的进程里也被不断 build 。

Flutter 小技巧之 3.10 全新的  MediaQuery 优化与 InheritedModel

虽然在之前的小技巧里咱们介绍了解决办法,可是 3.10 开端有更高雅的做法,一起也更便利咱们自足操控更细的颗粒度地去办理 InheritedWidget 里的绑定联系,那便是运用 InheritedModel

MediaQuery

在 3.10 里 MediaQuery 增加了需求针对特定参数的 ****of 办法,例如 MediaQuery.platformBrightnessOf(context); ,这些办法对应在 _MediaQueryAspect 里都有一个枚举类型,而在 Flutter Framework 里,这些参数的调用都修改成了新的 ****of 类型办法。

Flutter 小技巧之 3.10 全新的  MediaQuery 优化与 InheritedModel

例如一开端的比方,只需求将 MediaQuery.of(context).size 修改为 MediaQuery.sizeOf(context) ,那么跳转到 EditPage 页面,弹出键盘 ,在键盘弹起来的进程中不会再导致 MyHomePage rebuild 输出 log。

而之所以会这样的原因,其实是因为这些 MediaQuery.******Of(context); 内部调用的是 InheritedModel.inheritFrom 完成。

是的,3.10 开端 MediaQuery 承继从 InheritedWidget 变成 InheritedModel ,而 InheritedModelinheritFrom 办法能够让开发者能够经过 aspect 来决议数据改动时是否调用对应更新

Flutter 小技巧之 3.10 全新的  MediaQuery 优化与 InheritedModel

Flutter 小技巧之 3.10 全新的  MediaQuery 优化与 InheritedModel

小技巧一:3.10 现在能够经过将 MediaQuery.of 获取参数的办法替换成 MediaQuery.******Of(context); 来减少不必要的 rebuild

InheritedModel

运用 InheritedModel 只需求承继它就能够,之后需求要点关注的是 updateShouldNotifyDependent 办法,它用于决议应该什么时分 rebuild

如下图所示是 MediaQuery 的完成,在 updateShouldNotifyDependent 里咱们能够经过 dependencies 里的类型来进行差异,比方调用时是经过 InheritedModel.inheritFrom<MediaQuery>(context, aspect: _MediaQueryAspect.size) 输入,那么判断时就会进入到 _MediaQueryAspect. size 这个 case

Flutter 小技巧之 3.10 全新的  MediaQuery 优化与 InheritedModel

假如此刻 size 参数发生了改动,就回来 true ,然后发生 rebuild,反之回来放 false,这便是 InheritedModel 能够依据绑定具体变量来更新页面的原因。

当然这儿你纷歧定要传枚举,你喜爱的话传 String 也能够,具体能够依据你的爱好来设定。

那为什么 InheritedModelinheritFrom 办法能够达到这样的效果?

咱们简略看一下 inheritFrom 的源码完成,如下图所示:

  • 在没有 aspect 的时分直接调用 dependOnInheritedWidgetOfExactType() 那便是和之前普通的 of(context) 没什么差异
  • 在有 aspect 的时分,会先经过 _findModels 找到对应的 InheritedElement ,然后调用 dependOnInheritedElement() 绑定

Flutter 小技巧之 3.10 全新的  MediaQuery 优化与 InheritedModel

或许这时分你有些疑问,不要急慢慢来,咱们先看 dependOnInheritedWidgetOfExactType()dependOnInheritedElement() , 其实 dependOnInheritedWidgetOfExactType() 内部也是调用 dependOnInheritedElement() 来完成绑定,那么这儿前后的差异是什么?

假如你仔细看,这儿的差异在于 dependOnInheritedElement() 多运用了 aspect ,对应到 MediaQuery 里便是如 _MediaQueryAspect.size 这个 aspect ,这样在后续 updateShouldNotifyDependent 时就会被用上。

如下图所示是 InheritedModelInheritedModelElement ,能够看到 inheritFrom 传入的 aspect 会变成 dependencies ,而这个 dependencies 便是咱们在 updateShouldNotifyDependent 里用来判断的类型依据。

Flutter 小技巧之 3.10 全新的  MediaQuery 优化与 InheritedModel

最终,在 notifyDependent 办法里能够看到,只有 updateShouldNotifyDependent 回来 true 时,才会调用 didChangeDependencies 去更新。

所以 inheritFrom 的特别之处在于:当存在 aspect 时,该 aspect 会变成 dependencies 集合,然后经过 updateShouldNotifyDependent 来决议是否触发更新

至于 _findModels 办法其实你无需纠结,虽然它是传入一个 List,可是一般情况下你只会获取到一个。什么时分会或许有多个,便是你 override 了 InheritedModelisSupportedAspect 办法,而且会依据 aspect 条件有不同判断回来时或许会有多个。

Flutter 小技巧之 3.10 全新的  MediaQuery 优化与 InheritedModel

例如都是承继 InheritedModel ,可是 isSupportedAspect 能够依据条件来决议你这个实例是否支撑 Aspect 绑定。

  @override
  bool isSupportedAspect(Object aspect) {
    return aspects == null || aspects!.contains(aspect);
  }

另外 _findModels 里用的是 getElementForInheritedWidgetOfExactType(),它和 dependOnInheritedWidgetOfExactType() 的差异便是前者会注册依赖联系,而后者不会,所以 _findModels 顾名思义仅仅找出契合条件的 InheritedModel

Flutter 小技巧之 3.10 全新的  MediaQuery 优化与 InheritedModel

最终

最终总结一下,今日的小技巧其实很简略,便是更新你的 MediaQuery.of 到对应参数的 MediaQuery.*****of 然后提升使用性能,而且了解到 InheritedModel 的完成逻辑和自定义支撑,然后学会优化你现在的 InheritedWidget 的运用。

假如你还有什么问题,欢迎留言谈论交流。