Flutter - 使用 MediaQuery 时防止重建
Flutter - prevent rebuild when using MediaQuery
我想使用 MediaQuery
来构建基于屏幕高度和宽度的小部件。 #26004 中也提到的问题是我只想查询尺寸数据一次,例如在 initState
中。 MediaQuery
文档状态
Querying the current media using MediaQuery.of will cause your widget to rebuild automatically whenever the MediaQueryData changes (e.g., if the user rotates their device).
,但这会导致我的应用程序进行不必要的重建。具体来说,如果插入或填充发生变化(例如显示键盘时),它会导致小部件的重建。
是否有 MediaQuery
的替代方案,当 MediaQueryData
发生变化时不会导致重建?
我也有这个问题,最初认为 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);
}
}
我在 ResponsiveApp.setMq(context)
开头设置了 mediaQueryData,并且我使用了 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
并在 Web 中调整 window 的大小将导致小部件重建多次并显示所需的布局。
但是如果你根本不想使用 MediaQuery
,你可以从 dart:ui
.
查看 Window class
LayoutBuilder 似乎比每次使用 MediaQuery 来调整视口大小(整个屏幕,或者 space 留在列或其他布局中)更可取。
LayoutBuilder 也会努力避免在大小未更改且父级不必重新布局时重建其子级。
The builder function is called in the following situations:
- The first time the widget is laid out.
- When the parent widget passes different layout constraints.
- When the parent widget updates this widget.
- When the dependencies that the builder function subscribes to change.
The builder function is not called during layout if the parent passes
the same constraints repeatedly.
而且您不必再考虑“appbar 的高度”,因为您得到的是 space left,而不是全部space 屏幕上.
查看:https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html
就我而言,问题的发生是因为我使用以下方法手动控制焦点:
onEditingComplete: () {
FocusScope.of(context).nextFocus();
}
使用的 context
是父级的 context
,它导致重建。不知道它为什么会发生,但是一旦我用 Builder
包裹 TextFormField
并开始使用它的 context
代替它就停止了。
注意:我也正常使用 MediaQuery.of(context).size.height
(没有重建副作用)来设置 Widget 的父级高度
我想使用 MediaQuery
来构建基于屏幕高度和宽度的小部件。 #26004 中也提到的问题是我只想查询尺寸数据一次,例如在 initState
中。 MediaQuery
文档状态
Querying the current media using MediaQuery.of will cause your widget to rebuild automatically whenever the MediaQueryData changes (e.g., if the user rotates their device).
,但这会导致我的应用程序进行不必要的重建。具体来说,如果插入或填充发生变化(例如显示键盘时),它会导致小部件的重建。
是否有 MediaQuery
的替代方案,当 MediaQueryData
发生变化时不会导致重建?
我也有这个问题,最初认为 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);
}
}
我在 ResponsiveApp.setMq(context)
开头设置了 mediaQueryData,并且我使用了 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
并在 Web 中调整 window 的大小将导致小部件重建多次并显示所需的布局。
但是如果你根本不想使用 MediaQuery
,你可以从 dart:ui
.
LayoutBuilder 似乎比每次使用 MediaQuery 来调整视口大小(整个屏幕,或者 space 留在列或其他布局中)更可取。
LayoutBuilder 也会努力避免在大小未更改且父级不必重新布局时重建其子级。
The builder function is called in the following situations:
- The first time the widget is laid out.
- When the parent widget passes different layout constraints.
- When the parent widget updates this widget.
- When the dependencies that the builder function subscribes to change.
The builder function is not called during layout if the parent passes the same constraints repeatedly.
而且您不必再考虑“appbar 的高度”,因为您得到的是 space left,而不是全部space 屏幕上.
查看:https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html
就我而言,问题的发生是因为我使用以下方法手动控制焦点:
onEditingComplete: () {
FocusScope.of(context).nextFocus();
}
使用的 context
是父级的 context
,它导致重建。不知道它为什么会发生,但是一旦我用 Builder
包裹 TextFormField
并开始使用它的 context
代替它就停止了。
注意:我也正常使用 MediaQuery.of(context).size.height
(没有重建副作用)来设置 Widget 的父级高度