【问题标题】:Flutter - prevent rebuild when using MediaQueryFlutter - 使用 MediaQuery 时防止重建
【发布时间】:2021-04-19 19:42:57
【问题描述】:

我想使用MediaQuery 来构建基于屏幕高度和宽度的小部件。在#26004 中也提到了这个问题,我只想查询一次尺寸数据,例如在initState 中。 MediaQuery 文档状态

使用 MediaQuery.of 查询当前媒体将导致您的小部件在 MediaQueryData 更改时自动重建(例如,如果用户旋转他们的设备)。

,但这会在我的应用程序中导致不必要的重建。具体来说,如果插入或填充发生变化(例如显示键盘时),它会导致小部件重建。

MediaQueryData 更改时,MediaQuery 是否有替代方案不会导致重建?

【问题讨论】:

    标签: flutter flutter-widget flutter-build


    【解决方案1】:

    在我的例子中,问题的发生是因为我手动控制焦点:

    onEditingComplete: () {
       FocusScope.of(context).nextFocus();
    }
    

    使用的context 是父级的context,它导致了重建。不知道为什么会这样,但是一旦我用Builder 包裹TextFormField 并开始使用它的context,它就停止了。

    注意:我也正常使用MediaQuery.of(context).size.height(没有重建副作用)来设置小部件的父高度?

    【讨论】:

      【解决方案2】:

      LayoutBuilder 似乎优于每次使用 MediaQuery 来调整视口(整个屏幕,或列中的剩余空间或其他布局)。

      LayoutBuilder 还努力避免在大小不变且父级不必重新布局的情况下重建其子级。

      builder函数在以下情况下被调用:

      • 第一次布局小部件。
      • 当父小部件传递不同的布局约束时。
      • 当父小部件更新此小部件时。
      • 当构建器函数订阅的依赖发生变化时。

      如果父级通过,则布局期间不会调用构建器函数 重复相同的约束。

      而且您不必再考虑“应用栏的高度”,因为您获得的是 left 空间,而不是屏幕上的总 空间 em>。

      查看:https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html

      【讨论】:

        【解决方案3】:

        我也遇到过这个问题,最初认为 MediaQuery 会导致不必要的重建,但如果您考虑一下,您确实希望小部件重建(在设备旋转、键盘弹出的情况下)以使应用程序具有响应设计。

        你可以这样做:

        import 'package:flutter/material.dart';
        
        void main() {
          runApp(MyApp());
        }
        
        class MyApp extends StatelessWidget {
          @override
          Widget build(BuildContext context) {
            return MaterialApp(
              title: 'Flutter Demo',
              theme: ThemeData(
                primarySwatch: Colors.blue,
                visualDensity: VisualDensity.adaptivePlatformDensity,
              ),
              home: Builder(builder: (context) {
                ResponsiveApp.setMq(context);
                return MyHomePage(title: 'Flutter Demo Home Page');
              }),
            );
          }
        }
        
        class MyHomePage extends StatefulWidget {
          MyHomePage({Key key, this.title}) : super(key: key);
        
          final String title;
        
          @override
          _MyHomePageState createState() => _MyHomePageState();
        }
        
        class _MyHomePageState extends State<MyHomePage> {
          int _counter = 0;
        
          void _incrementCounter() {
            setState(() {
              _counter++;
            });
          }
        
          @override
          Widget build(BuildContext context) {
            
            return Scaffold(
              appBar: AppBar(
                title: Text(widget.title),
              ),
              body: Center(
                child: Flex(
                  direction:
                      ResponsiveApp().mq.size.width > ResponsiveApp().mq.size.height
                          ? Axis.horizontal
                          : Axis.vertical,
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: <Widget>[
                    Text(
                      'You have pushed the button this many times:',
                    ),
                    Text(
                      '$_counter',
                      style: Theme.of(context).textTheme.headline4,
                    ),
                  ],
                ),
              ),
              floatingActionButton: FloatingActionButton(
                onPressed: _incrementCounter,
                tooltip: 'Increment',
                child: Icon(Icons.add),
              ),
            );
          }
        }
        
        class ResponsiveApp {
          static MediaQueryData _mediaQueryData;
        
          MediaQueryData get mq => _mediaQueryData;
        
          static void setMq(BuildContext context) {
            _mediaQueryData = MediaQuery.of(context);
          }
        }
        
        

        我将 mediaQueryData 设置在 ResponsiveApp.setMq(context) 开头,我使用了 Builder,因为您只能在 MaterialApp 小部件下方使用一个 context 的 MediaQuery。设置_mediaQueryData 后,您可以随时根据屏幕尺寸构建小部件。

        在这段代码中,我只是在设备旋转时更改轴方向,并且小部件需要重建以显示更改的方向。

        你也可以有类似的东西:

        
            if (_mediaQueryData.size.shortestSide < 400)
                 //phone layout
            else if(_mediaQueryData.size.shortestSide >= 400 && _mediaQueryData.size.shortestSide < 600)
                  //tablet layout
            else
                  //web layout
        
        

        在网页中调整窗口大小将导致小部件多次重建并显示所需的布局。

        但是如果你根本不想使用MediaQuery,你可以检查dart:ui的Window类。

        【讨论】:

        • 虽然Randal Schwartz 提出的解决方案有效,但我最终还是这样做了,因为将它集成到我现有的应用程序中更简单一些。谢谢!
        猜你喜欢
        • 2021-07-07
        • 1970-01-01
        • 1970-01-01
        • 2021-04-02
        • 1970-01-01
        • 2021-11-14
        • 1970-01-01
        • 2021-12-22
        • 2014-02-10
        相关资源
        最近更新 更多