I want to use MediaQuery
to build widgets based on screen height and width. The problem, also referenced in #26004, is that I only want to query the size data once, for example in initState
. MediaQuery
documentation states
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).
, but that causes unnecessary rebuilds in my application. Specifically, it causes rebuild of widgets if there are changes to insets or padding (such as when keyboard is displayed).
Is there an alternative to MediaQuery
which wouldn't cause rebuilds when MediaQueryData
changes?
I had this issue as well and initially thought that the MediaQuery is causing unnecessary rebuilds, but if you think about it you do want the widgets to rebuild (in cases of device rotation, keyboard popup) for the app to have a responsive design.
You could do something like this:
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);
}
}
I set the mediaQueryData at the beginning with ResponsiveApp.setMq(context)
and I used the Builder because you can only use the MediaQuery one context
below the MaterialApp
widget. After the _mediaQueryData
is set you can get it whenever you want to build widgets based on the screen size.
In this code I just change the Axis direction when the device is rotated and the widget needs to rebuild to show the changed direction.
You could also have something like :
if (_mediaQueryData.size.shortestSide < 400)
//phone layout
else if(_mediaQueryData.size.shortestSide >= 400 && _mediaQueryData.size.shortestSide < 600)
//tablet layout
else
//web layout
and resizing the window in web will cause the widgets to rebuild multiple times and display the desired layout.
But if you don't want to use MediaQuery
at all, you can check the Window class from dart:ui
.