Flutter - 使用 MediaQuery 时防止重建
Posted
技术标签:
【中文标题】Flutter - 使用 MediaQuery 时防止重建【英文标题】:Flutter - prevent rebuild when using MediaQuery 【发布时间】:2021-04-19 19:42:57 【问题描述】:我想使用MediaQuery
来构建基于屏幕高度和宽度的小部件。在#26004 中也提到了这个问题,我只想查询一次尺寸数据,例如在initState
中。 MediaQuery
文档状态
使用 MediaQuery.of 查询当前媒体将导致您的小部件在 MediaQueryData 更改时自动重建(例如,如果用户旋转他们的设备)。
,但这会在我的应用程序中导致不必要的重建。具体来说,如果插入或填充发生变化(例如显示键盘时),它会导致小部件重建。
当MediaQueryData
更改时,MediaQuery
是否有替代方案不会导致重建?
【问题讨论】:
【参考方案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 提出的解决方案有效,但我最终还是这样做了,因为将它集成到我现有的应用程序中更简单一些。谢谢!以上是关于Flutter - 使用 MediaQuery 时防止重建的主要内容,如果未能解决你的问题,请参考以下文章
Flutter MediaQuery获取屏幕信息以及屏幕适配
错误记录Flutter 使用 MediaQuery 适配全面屏报错 ( No MediaQuery widget ancestor found. )
如何解决 Flutter 中的 MediaQuery 错误? [复制]
Flutter - 我如何使用 MediaQuery.of(context).copyWith(textScaleFactor)
Flutter iOS - MediaQuery.of(context).size.width 没有以像素为单位给出宽度