Skip to content

Commit 9566f3f

Browse files
replaced settings_page mediaquery with layoutbuilder and added reason to readme
1 parent 90c1f39 commit 9566f3f

File tree

2 files changed

+52
-44
lines changed

2 files changed

+52
-44
lines changed

README.md

+22-14
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ If you have any questions, feedback, or suggestions, feel free to reach out to m
5656
- [Design a Prototype first](#design-a-prototype-first)
5757
- [Hardcoded Sizes and Values](#hardcoded-sizes-and-values)
5858
- [Builder vs MediaQuery](#builder-vs-mediaquery)
59+
- [Why not just use `LayoutBuilder` and `OrientationBuilder`?](#why-not-just-use-layoutbuilder-and-orientationbuilder)
5960
- [Conclusion](#conclusion)
6061

6162
## Introduction
@@ -262,22 +263,26 @@ Wrap the `popUntil` method within a `SchedulerBinding.instance.addPostFrameCallb
262263
### Center ListView with whitespace
263264
On larger screens, constraining the width of scrollable lists and centering them improves aesthetics and usability. Directly wrapping a `ListView` with a `ConstrainedBox` may restrict scrollable area to the constrained width, excluding the white space. A workaround involves using the `padding` parameter of `ListView` to dynamically calculate and apply horizontal padding based on screen size:
264265
```dart
265-
final width = MediaQuery.sizeOf(context).width;
266-
double horizontalPadding = width > ScreenSize.large.size
267-
? ((width - ScreenSize.large.size) / 2)
268-
: 16;
269-
return Scaffold(
270-
body: ListView(
271-
padding: EdgeInsets.symmetric(
272-
horizontal: horizontalPadding, vertical: 16)
266+
return Scaffold(body:
267+
LayoutBuilder(builder: (context, constraints) {
268+
double horizontalPadding = constraints.maxWidth > ScreenSize.large.size
269+
? ((constraints.maxWidth - ScreenSize.large.size) / 2)
270+
: Spacing.x3;
271+
return ListView(
272+
padding: EdgeInsets.symmetric(
273+
horizontal: horizontalPadding, vertical: Spacing.x3),
273274
//...
274-
));
275+
)}));
275276
```
276277

277278
![Responsive List View](docs/flutter_responsive_listview.gif)
278279

279280
This approach uses again the `ScreenSize` enum to remain consistent.
280281

282+
**Why do we use `LayoutBuilder` instead of `MediaQuery`?**
283+
Since we are showing the `NavigationRail` on larger screens, the `MediaQuery` would ignore the constrain from the `NavigationRail` and the `SafeArea`. So we use `LayoutBuilder` to get the actual constraints of the screen.
284+
Read more in this section: [Builder vs MediaQuery](#builder-vs-mediaquery)
285+
281286
## Widgets
282287
With our layout in place, it's time to explore the widgets that will enable us to construct a responsive UI. Flutter offers a rich set of widgets which are essential for creating responsive layouts and widgets. I strongly recommend reading documentation provided by the Flutter Team, which presents an extensive array of widgets in various formats. Here are some resources to get you started:
283288
- **Flutter Docs:**
@@ -461,11 +466,14 @@ Avoid relying on hardcoded sizes and values within your app, as they frequently
461466
Additionally, consider utilizing ConstrainedBox to introduce a degree of flexibility to your widgets. This approach allows you to set minimum and maximum constraints, providing your layout with the adaptability it needs to accommodate different screen sizes and orientations without compromising on design integrity.
462467

463468
### Builder vs MediaQuery
464-
Using `LayoutBuilder` and `OrientationBuilder` can sometimes get a bit hacky to use especially when using them in combination or for complex layouts. That is why I prefer to use `MediaQuery`.
465-
466-
⚠️**Warning** There are few things you should keep in mind when using MediaQuery:
467-
- `MediaQuery` comes with different methods like `sizeOf` or `orientationOf` which you should use instead of `MediaQuery.of(context).size` or `MediaQuery.of(context).orientation`. The reason you only want automatic rebuilds whenever that specific property changes.
468-
- Using `MediaQuery` can have unwanted rebuilds. So make sure to only use it in a parent widget to define the whole Layout.
469+
⚠️**Warning** `MediaQuery` should be used with caution:
470+
- `MediaQuery` comes with different methods like `sizeOf` or `orientationOf` which you should use instead of `MediaQuery.of(context).size` or `MediaQuery.of(context).orientation`. The reason is you only want rebuilds whenever that specific property changes.
471+
- Using `MediaQuery` can have unwanted rebuilds. So make sure to only use it in the very top of your Widget Tree to define the whole Layout.
472+
- `MediaQuery` ignores paddings, SafeArea and other constraints because you get the size of the app window itself
473+
- For child widgets you should use `LayoutBuilder` or `OrientationBuilder` to get the actual constraints for the widget
474+
475+
#### Why not just use `LayoutBuilder` and `OrientationBuilder`?
476+
Using `LayoutBuilder` and `OrientationBuilder` can sometimes get a bit hacky to use especially when using them in combination for complex layouts. For that reason I prefer to use `MediaQuery`. But you could use `LayoutBuilder` and `OrientationBuilder` to get the same results.
469477

470478
## Conclusion
471479
Our app now dynamically responds to every screen size. The beauty of Flutter is that it provides all the tools necessary for responsive design across any device.

lib/pages/settings_page.dart

+30-30
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,36 @@ class SettingsPage extends StatelessWidget {
55
const SettingsPage({super.key});
66
@override
77
Widget build(BuildContext context) {
8-
final width = MediaQuery.sizeOf(context).width;
9-
double horizontalPadding = width > ScreenSize.large.size
10-
? ((width - ScreenSize.large.size) / 2)
11-
: Spacing.x3;
12-
return Scaffold(
13-
body: ListView(
14-
padding: EdgeInsets.symmetric(
15-
horizontal: horizontalPadding, vertical: Spacing.x3),
16-
children: [
17-
ListTile(
18-
title: const Text('Dark Mode'),
19-
trailing: Switch(
20-
value: Theme.of(context).brightness == Brightness.dark,
21-
onChanged: (value) {},
8+
return Scaffold(body: LayoutBuilder(builder: (context, constraints) {
9+
double horizontalPadding = constraints.maxWidth > ScreenSize.large.size
10+
? ((constraints.maxWidth - ScreenSize.large.size) / 2)
11+
: Spacing.x3;
12+
return ListView(
13+
padding: EdgeInsets.symmetric(
14+
horizontal: horizontalPadding, vertical: Spacing.x3),
15+
children: [
16+
ListTile(
17+
title: const Text('Dark Mode'),
18+
trailing: Switch(
19+
value: Theme.of(context).brightness == Brightness.dark,
20+
onChanged: (value) {},
21+
),
2222
),
23-
),
24-
ListTile(
25-
title: const Text('Setting 1'),
26-
onTap: () {},
27-
),
28-
ListTile(
29-
title: const Text('Setting 2'),
30-
onTap: () {},
31-
),
32-
ListTile(
33-
title: const Text('Licenses'),
34-
onTap: () {
35-
showLicensePage(context: context);
36-
},
37-
),
38-
]));
23+
ListTile(
24+
title: const Text('Setting 1'),
25+
onTap: () {},
26+
),
27+
ListTile(
28+
title: const Text('Setting 2'),
29+
onTap: () {},
30+
),
31+
ListTile(
32+
title: const Text('Licenses'),
33+
onTap: () {
34+
showLicensePage(context: context);
35+
},
36+
),
37+
]);
38+
}));
3939
}
4040
}

0 commit comments

Comments
 (0)