Flutter - предотвратить перестроение при использовании MediaQuery
Я хочу использовать для создания виджетов на основе высоты и ширины экрана. Проблема, на которую также ссылается в #26004 , заключается в том, что я хочу запросить данные о размере только один раз, например, в
initState
. в документации говорится
Запрос текущего мультимедиа с помощью MediaQuery.of приведет к автоматическому перестроению вашего виджета при изменении MediaQueryData (например, если пользователь поворачивает свое устройство).
, но это вызывает ненужные перестройки в моем приложении. В частности, он вызывает перестройку виджетов, если есть изменения во вставках или отступах (например, при отображении клавиатуры).
Есть ли альтернатива
MediaQuery
что не приведет к перестройке, когда
MediaQueryData
изменения?
4 ответа
У меня тоже была эта проблема, и сначала я подумал, что 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, потому что вы можете использовать только MediaQuery one
context
ниже
MaterialApp
виджет. После
_mediaQueryData
установлен, вы можете получить его, когда захотите создавать виджеты на основе размера экрана.
В этом коде я просто меняю направление оси, когда устройство поворачивается, и виджету необходимо перестроить, чтобы показать измененное направление.
У вас также может быть что-то вроде:
if (_mediaQueryData.size.shortestSide < 400)
//phone layout
else if(_mediaQueryData.size.shortestSide >= 400 && _mediaQueryData.size.shortestSide < 600)
//tablet layout
else
//web layout
а изменение размера окна в сети приведет к многократной перестройке виджетов и отображению желаемого макета.
Но если вы не хотите использовать
MediaQuery
вообще, вы можете проверить класс Window из
dart:ui
.
LayoutBuilder кажется предпочтительнее любого использования MediaQuery для изменения размера области просмотра (либо всего экрана, либо пространства, оставшегося в столбце или другом макете).
LayoutBuilder также прилагает все усилия, чтобы избежать перестройки своего дочернего элемента, если размер не изменился и родителям не пришлось менять макет.
Функция построителя вызывается в следующих ситуациях:
- Первый раз виджет выкладывается.
- Когда родительский виджет передает различные ограничения макета.
- Когда родительский виджет обновляет этот виджет.
- Когда зависимости, на которые подписывается функция построителя, изменяются.
Функция построителя не вызывается во время макета, если родительский элемент многократно передает одни и те же ограничения.
И вам больше не нужно думать о «высоте панели приложений», потому что вы получаете оставшееся пространство , а не общее пространство на экране.
Проверьте это: https://api.flutter.dev/flutter/widgets/LayoutBuilder-class.html
Вместо:
MediaQuery.of(context).size..
Попробуй использовать:
MediaQuery.sizeOf(context)...
никаких ненужных перестроек
В моем случае проблема возникла из-за того, что я управлял фокусом вручную, используя:
onEditingComplete: () {
FocusScope.of(context).nextFocus();
}
Использованный принадлежал Родителю, и он был причиной перестройки. Не уверен, почему это произошло, но это прекратилось, как только я завернул
TextFormField
с
Builder
и начал использовать
context
вместо.
Примечание: я также использую
MediaQuery.of(context).size.height
обычно (без побочного эффекта перестройки), чтобы установить высоту родительского виджета 🤔