今日这篇文章的目的是补全咱们关于 MediaQuery 和对应 rebuild 机制的根底认知,相信本篇内容对你优化功能和调试 bug 会很有协助

Flutter 里咱们应该都离不开安全教育平台作业登录 MediafluttershyQuery ,比方经过 MediaQuery.of(context).size 获取屏幕巨细 ,或者经过 MediaQuery.o嵌套查询f(context).padding.top 获取状况栏高度,那随便运用 MediaQuery.of(contextappearance) 会有什么问题吗?

首先咱们需求简略解释一下,经过 MediaQuery.of 获取到的 MediaQueryData 里有几个很相似的参数:

  • viewInsets被系统用户界面完全遮挡的部分巨细,简略来说便是键盘高度
  • padding简略来说便是状况栏和底部安全区apple域,可是嵌套结构 bottom 会由于键盘弹出变成 0
  • viewPadding flutter有必要学吗padding 相同,可是 bottom 部分不approach会产生改动

举个比方,在 iOS 上,如下图所示,在弹出键盘和未弹出键盘的情况下,能够看到 MediaQueryData 里一些参数的变化:缓存

  • viewInsets 在没有弹出键盘时是 0,弹出键盘之后 bottFlutterom 变成 336
  • paddin缓存g 在弹apple出键盘的flutter是什么意思前后差异, bottom 从 34 变成了 0
  • viewPadding 在键盘弹出前后数据没有产生变化

Flutter 小技巧之  MediaQuery  和 build 优化你不知道的隐秘

能够看到 MediaQueryData 里的数据是会依据键盘状况产生变化,又由于 MediaQuery 是一个 InheritedWidget ,所以咱们能够经过 MediaQue安全工程师ry.of(context) 获取到顶层同享的 MediaQuery缓存视频合并Data

那么问题来了,InheritedWidget 的更新逻辑,是经过登记的 context 来绑定的,也便是 MediaQuery.of(context) 本身便是一个绑定行为,然后 MediaQueryData 又和键盘状况有联系,所以:键盘的弹出或许会导致运用 MediaQuery.of(context) 的当地触发 rebui嵌套分类汇总的操作步骤ld,举个比方:

如下代码所示,咱们在 MyHomePage 里运用了 MediaQuery.of(context).size 并打印输出,然flutter开发的app有哪些后跳转到 EditPage 页面,弹出键盘 ,这flutter框架时候会产生什么情况?


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(),
        ],
      ),
    );
  }
}

如下图 lo缓存视频变成本地视频g 所示 , 能够看到在键盘弹起来的进程,由于 bottom 产生改动缓存,所以 MediaQueryD嵌套查询ata 产生了改动APP,从而导致上一级的 MyHomePage 尽管不行见,可是在键盘弹起的进程里也被不断 build 。

Flutter 小技巧之  MediaQuery  和 build 优化你不知道的隐秘

试想一下,假如你在每个页面开端的方位都是嵌套是什么意思用了 MediaQuery.of(context) ,然后打appstore开了 5 个页面,这时候你在第 5 个页面弹出键盘时,也触发安全教育平台登录入口了前面 4 个页面 rebuild,天然而然或许就会呈现卡顿。

那么假如我不在 MyHomePage 的 build 办法直接运用 MediaQue缓存文件夹名称ry.of(contex嵌套查询t) ,那在 EditPage 里弹出键盘是不是就不会导致上一级的 MyHomePage 触发 build

答案是必定的,没有了 MediaQuery.of(context).size 之后, MyapplicationHomePage 就不会由于 EditPage 里的键盘弹出而导致 rebuild。

所以小技巧一:要慎重在 Scaffold 之外运用 MediaQuery.of(context) ,或许你现在会觉得古怪什么是 S嵌套是什么意思caffold 之外,没事后面持续解释。

那到这儿有人或许就要说了:咱们经过 MediaQuery.of(context) 获取到的 MediaQueryData ,不便是对应在 MaterialApp 里的 MediaQue嵌套查询sql语句ry 吗?那它产生改动,不应该都会触发fluttershy下面的 child 都 rebuild 吗?

这其实和页面路由有联系,也便是咱们常说的 PageRoute 的实现

如下图所示,由于嵌套结构的原因,事实上弹出键盘的确会导致 MaterialApp 下的 child 都触发 rebuild ,由于设计上 MediaQuery 便是在 Navigator 上面,所以弹出安全教育手抄报键盘天然也就触发 Navigator 的 rebuild

Flutter 小技巧之  MediaQuery  和 build 优化你不知道的隐秘

那正常情况下 Navigator 都触发 rebuild 了,为什么页面不会都被 rebuild 呢

这就和路由目flutter面试题标的基类 ModalRoute 有联系,由于在它的内部会经过一个 _modalScopeflutteredCache 参数把 Widget 缓存起来,正如注释所说:

缓存区域不随帧变化,以便得到最小化的构建

Flutter 小技巧之  MediaQuery  和 build 优化你不知道的隐秘

举个比方,如下代码所示:

  • 首先界说了一个 TextGlobal ,在 build 办法里输出 "######## TextGlobal"
  • 然后在 MyHomePage 里界说一个大apple局的 TextGlobal globalText = TextGl安全教育平台作业登录obal();
  • 接着在 My安全教育日HomePage 里添加 3 个 globalText
  • 最后点击 FloatingActionBu嵌套分类汇总怎么做tton 触发 setState(() {});
class TextGlobal extends StatelessWidget {
  const TextGlobal({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    print("######## TextGlobal");
    return Container(
      child: new Text(
        "测试",
        style: new TextStyle(fontSize: 40, color: Colors.redAccent),
        textAlign: TextAlign.center,
      ),
    );
  }
}
class MyHomePage extends StatefulWidget {
  final String? title;
  MyHomePage({Key? key, this.title}) : super(key: key);
  @override
  _MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
  TextGlobal globalText = TextGlobal();
  @override
  Widget build(BuildContext context) {
    print("######## MyHomePage");
    return Scaffold(
      appBar: AppBar(),
      body: new Container(
        alignment: Alignment.center,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.center,
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            globalText,
            globalText,
            globalText,
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {});
        },
      ),
    );
  }
}

那么有趣的来了,如下图 log 所示,"######## TextGlobal" 除了在一开端构建时有输出之外,剩余 setState(() {}); 的时候都没有在触发,也便是没有 rebuild ,这其实便是上面 ModalRoute 的相似行为:弹出键盘导致了 MediaQuery 触发 Navigator 履行 rebuild,可是 rebuild 到了 ModalRoute 就不往下影响

Flutter 小技巧之  MediaQuery  和 build 优化你不知道的隐秘

其实这个行为也体现在了 Scaffol安全教育日d 里,假如你去看嵌套函数 Scaffolflutter开发的app有哪些d 的源码,你就会发现 Scaffoldflutter是什么意思大量运用了 MediaQuery.of(context)

比方上面的代码,假如你给 MyHomePageScafFlutterfold 装备一个 3333 的 ValueKey ,那么在 EditPage 弹出键盘时,其实 MyHomePageScaffold 是会触发 rebuild ,可是由于其flutter菜鸟教程运用的是 wi缓存清理dget.body ,所以并不会导致 body 内目标重构。

Flutter 小技巧之  MediaQuery  和 build 优化你不知道的隐秘

假如是 MyHomePage 假如 rebuild ,就会对 build 办法里所有的装备的 new 目标进行 rebuild;可是假如只是 MyHomePage 里的 Scaffold 内部触发了 rebuild ,是不会导致 MyHomePage 里的 body 参数安全生产法对应的 child 履行 rebuild 。

是不是太笼统?举个简略的比方,如下代码所示:

  • 咱们界说了一个 LikeScaffold 控件,在控件内经过缓存是什么意思 widget.body 传递目标
  • Li缓存视频变成本地视频keScaffoldflutter面试题 内部咱们运用了 MediaQuery.of(context).viewInsets.bottom ,模仿 Scaffold 里运用 MediaQuery
  • MyHo缓存的视频在哪mePage 里运用 LikeScaffold ,并给 LikeappstoreScaffold 的 body 装备一个 Builder ,输出 "############ HomePage Builder Text " 用于调查
  • 跳到 EditPage 页面打开键盘
class LikeScaffold extends StatefulWidget {
  final Widget body;
  const LikeScaffold({Key? key, required this.body}) : super(key: key);
  @override
  State<LikeScaffold> createState() => _LikeScaffoldState();
}
class _LikeScaffoldState extends State<LikeScaffold> {
  @override
  Widget build(BuildContext context) {
    print("####### LikeScaffold build ${MediaQuery.of(context).viewInsets.bottom}");
    return Material(
      child: new Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        mainAxisAlignment: MainAxisAlignment.center,
        children: [widget.body],
      ),
    );
  }
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    var routeLists = routers.keys.toList();
    return new LikeScaffold(
      body: Builder(
        builder: (_) {
          print("############ HomePage Builder Text ");
          return InkWell(
            onTap: () {
              Navigator.of(context).push(CupertinoPageRoute(builder: (context) {
                return EditPage();
              }));
            },
            child: Text(
              "FFFFFFF",
              style: TextStyle(fontSize: 50),
            ),
          );
        },
      ),
    );
  }
}

能够看到,最开端 "####### LikeScaffold build 0.0############ HomePage Builder Text 都正常履行,然后在键盘弹出之安全教育平台登录后,"####### LikeScaffold build 跟从键盘动画不断输出 bottom 的 巨细,可是 "############ HomePage Builder Text ") 没有输出,由于它是 widget.body 实例。

Flutter 小技巧之  MediaQuery  和 build 优化你不知道的隐秘

所以经过这个最小比方,能够看到尽管 Scaffold 里大量运缓存文件夹名称MediaQuery.of(context) ,可是影响范围是约束在 Scaffoldflutter有必要学吗 内部

接着咱们持续看修改这个比方,假如在 LikeScaffold缓存是什么意思 上嵌套多一个 Scaffold ,那输出结果会是apple怎么样?


class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    var routeLists = routers.keys.toList();
    ///多加了个 Scaffold
    return Scaffold(
      body:  new LikeScaffold(
        body: Builder(
        
        ),
      ),
    );
}

答案是 LikeScaffold 内的 "####### LikeScaffo嵌套虚拟化ld build 也不会由于键盘的弹起而输出,也便是: Lik嵌套查询sql语句eScaflutteringffold 尽管运用了 MediaQuery.of(context) ,可是它不再由于键盘的弹起而导致 rebuild

由于此刻 LikeScaffoldScaffold 的 child ,所以在 Lik嵌套if函数eScaffold 内经过 MediaQuery.of(context) 指向的,其实是 Sc缓存文件夹名称affold 内部经过处理的 MediaQueryData

Flutter 小技巧之  MediaQuery  和 build 优化你不知道的隐秘

Scaffold 内部有很多相似的处理,例如 body 里会依据是否有 AppbarBottomNavigationBar 来决议是否移除该区域内的 paddingTappleop 和 paddingBottom 。

所以,看到这儿有没有想到什么?为什么时不时经过 MediaQuery.of(context缓存) 获取的 pappointmentadding ,有的 top 为 0 ,有的不为 0 ,原因就在于你获取的 context 来自哪里

举个比方,如下代码所示, ScaffoldChildPage 作为 Scaffold 的 child ,咱们分别在 MyHomePageScaffoldChildPag安全教育平台登录e 里打印 MediaQuery.of(context).padding

class MyHomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("MyHomePage         MediaQuery padding: ${MediaQuery.of(context).padding}");
    return Scaffold(
      appBar: AppBar(
        title: new Text(""),
      ),
      extendBody: true,
      body: Column(
        children: [
          new Spacer(),
          ScaffoldChildPage(),
          new Spacer(),
        ],
      ),
    );
  }
}
class ScaffoldChildPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    print("ScaffoldChildPage  MediaQuery padding: ${MediaQuery.of(context).padding}");
    return Container();
  }
}

如下图所示,能够看到,由于此刻 MyHomePageAppbar ,所以 ScaffoldChildPage 里获取到 paddingTop 是 0 ,由于此刻 ScaffoldC缓存英文hildPage 获取到的 Medi安全期计算器aQueryData 现已被 MyHomePage 里的 Scaffold 改写了。

Flutter 小技巧之  MediaQuery  和 build 优化你不知道的隐秘

假如此flutter有必要学吗刻你给 MyHomePage 增加了 BottomNavigationBar ,能够看到 Scaffol嵌套查询sql语句dChilappeardPage 的 bottom 会从原本的 34 变成 90 。

Flutter 小技巧之  MediaQuery  和 build 优化你不知道的隐秘

到这儿能够看到 MedappointmentiaQuery.of 里的 context 目标很重要:

  • 假如页面 MediaQuery.of 用的是 S缓存的视频在哪caffold 外的 cont缓存视频合并ext ,获取到的是顶层的 M嵌套函数ediaQueryData ,那么弹出键盘时就会导致页面 rebuild
  • MediaQue缓存清理ry.of 用的是 Scaffold 内的 context ,那么获取到的是 Scaffold 关于区域内的 MediaQueryData嵌套 ,比方前面介绍过的 body ,一安全教育日缓存视频合并获取安全教育平台登录到的 MediaQueryData 也会由于 Scaffold 的装备不同而产生改动

所以,如下动图所示,其实部分人会在 push 对应路由当地,经过嵌套 MediaQuery 来做一些阻拦处理,比方设置文本不行缩放,可是其实这样appear会导致键盘在弹出和收起时,触发各个页面不断 r安全教育手抄报ebuild ,比方在 Page 2安全生产法 弹出键盘的进程,Page 1 也在不断 rebuild。

Flutter 小技巧之  MediaQuery  和 build 优化你不知道的隐秘

所以,假如需求做一些大局阻拦,引荐经过 useInheritedMediaQuery 这种方法来做大局处理fluttering

return MediaQuery(
  data: MediaQueryData.fromWindow(WidgetsBinding.instance!.window).copyWith(boldText: false),
  child: MaterialApp(
    useInheritedMediaQuery: true,
  ),
);

所以最后做个总结,本篇首要理清了:

  • MediaQueryDataviewInsets padding viewPadding 的差异
  • MediaQuery 和键盘状况的联系
  • MediaQu缓存视频合并ery.of 运用不同 context 对功能的影响
  • 经过 Scflutter开发的app有哪些affold 内的 context 获取到的 MediaQueryData 受到 Scaffold 的影响

那么,嵌套查询假如看完本篇你appointment还有什么疑惑,欢迎留言谈论沟通。