Skip to content

Commit 35726ae

Browse files
committed
Add companion app for the March 23rd 2021 meetup
1 parent 91c86c6 commit 35726ae

File tree

84 files changed

+2638
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

84 files changed

+2638
-0
lines changed

.gitignore

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Miscellaneous
2+
*.class
3+
*.log
4+
*.pyc
5+
*.swp
6+
.DS_Store
7+
.atom/
8+
.buildlog/
9+
.history
10+
.svn/
11+
12+
# IntelliJ related
13+
*.iml
14+
*.ipr
15+
*.iws
16+
.idea/
17+
18+
# The .vscode folder contains launch configuration and tasks you configure in
19+
# VS Code which you may wish to be included in version control, so this line
20+
# is commented out by default.
21+
#.vscode/
22+
23+
# Flutter/Dart/Pub related
24+
**/doc/api/
25+
**/ios/Flutter/.last_build_id
26+
.dart_tool/
27+
.flutter-plugins
28+
.flutter-plugins-dependencies
29+
.packages
30+
.pub-cache/
31+
.pub/
32+
/build/
33+
34+
# Web related
35+
lib/generated_plugin_registrant.dart
36+
37+
# Symbolication related
38+
app.*.symbols
39+
40+
# Obfuscation related
41+
app.*.map.json
42+
43+
# Android Studio will place build artifacts here
44+
/android/app/debug
45+
/android/app/profile
46+
/android/app/release

.metadata

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# This file tracks properties of this Flutter project.
2+
# Used by Flutter tool to assess capabilities and perform upgrades etc.
3+
#
4+
# This file should be version controlled and should not be manually edited.
5+
6+
version:
7+
revision: 4d7946a68d26794349189cf21b3f68cc6fe61dcb
8+
channel: stable
9+
10+
project_type: app

README.md

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
# Creating Responsive Apps With Flutter
2+
3+
- [Creating Responsive Apps With Flutter](#creating-responsive-apps-with-flutter)
4+
- [1.1. Box Contraints](#11-box-contraints)
5+
- [1.2. Responsive Apps On Various Mobile Screen Sizes](#12-responsive-apps-on-various-mobile-screen-sizes)
6+
- [1.2.1. Columns](#121-columns)
7+
- [1.2.2. Handling Orientation](#122-handling-orientation)
8+
- [1.3. Handling Wider Screens](#13-handling-wider-screens)
9+
- [1.3.1. MediaQuery](#131-mediaquery)
10+
- [1.3.2. Layout Builder](#132-layout-builder)
11+
12+
## 1.1. Box Contraints
13+
14+
Flutter framework is constraint based framework, i.e, a widget bounds is constrained by the parent widget. If for example, you container has a dimension of height 900px and width 500px but when you run the code on your simulator, you see that its actually smaller than what you have intended to, this is due to the box constraints coming in from the parent widget which passes contraints of maybe 800px 400px. If the parent of the container is a scaffold, then the box constraint coming in from the scaffold to the container would be the screen dimensions. Box contraints are passed automatically by the parent.
15+
16+
<details>
17+
<summary>Example 1</summary>
18+
19+
<p>
20+
21+
```dart
22+
import 'package:flutter/material.dart';
23+
import 'package:flutter/widgets.dart';
24+
25+
class Example1 extends StatelessWidget {
26+
const Example1({Key key}) : super(key: key);
27+
28+
@override
29+
Widget build(BuildContext context) {
30+
//Scaffold passes down screen dimensions as constraints
31+
return Scaffold(
32+
appBar: AppBar(
33+
title: Text('Example 1'),
34+
),
35+
body: Container(
36+
color: Colors.green,
37+
width: double.infinity,
38+
height: double.infinity,
39+
),
40+
);
41+
}
42+
}
43+
```
44+
</p>
45+
</details>
46+
47+
<details>
48+
<summary>Example 1</summary>
49+
50+
<p>
51+
52+
```dart
53+
import 'package:flutter/material.dart';
54+
import 'package:flutter/widgets.dart';
55+
56+
class Example2 extends StatelessWidget {
57+
const Example1({Key key}) : super(key: key);
58+
59+
@override
60+
Widget build(BuildContext context) {
61+
//Scaffold passes down screen dimensions as constraints
62+
return Scaffold(
63+
appBar: AppBar(
64+
title: Text('Example 1'),
65+
),
66+
body: Container(
67+
width: 200,
68+
height: 200,
69+
child: Container(
70+
color: Colors.green,
71+
width: double.infinity,
72+
height: double.infinity,
73+
),
74+
),
75+
);
76+
}
77+
}
78+
```
79+
</p>
80+
</details>
81+
82+
<br/>
83+
84+
*A widget can decide its own size only within the constraints given to it by its parent. This means a widget usually can’t have any size it wants.*
85+
86+
## 1.2. Responsive Apps On Various Mobile Screen Sizes
87+
When creating an app across various mobile screen, we want to make sure they are responsive and expand or collapse based on the screen dimensions. So we will look at how to achieve that with Columns and Rows.
88+
89+
### 1.2.1. Columns
90+
91+
So, Columns, unlike Scaffold or Container don't pass constraint along the main axis and will build the height from the children. This is because Columns children are dynamic.
92+
93+
<details>
94+
<summary>Example 1</summary>
95+
96+
<p>
97+
98+
```dart
99+
import 'package:flutter/material.dart';
100+
import 'package:flutter/widgets.dart';
101+
102+
class ColumnExample1 extends StatelessWidget {
103+
const ColumnExample1({Key key}) : super(key: key);
104+
105+
@override
106+
Widget build(BuildContext context) {
107+
return Scaffold(
108+
appBar: AppBar(
109+
title: Text('Example 1'),
110+
),
111+
//if the containers height are infite here, the app will //crash
112+
body: Column(
113+
children: [
114+
Container(
115+
color: Colors.green,
116+
height: 100,
117+
),
118+
Container(
119+
color: Colors.blue,
120+
height: 300,
121+
),
122+
Container(
123+
color: Colors.orange,
124+
height: 500,
125+
),
126+
],
127+
),
128+
);
129+
}
130+
}
131+
```
132+
</p>
133+
</details>
134+
135+
As you saw with the example, the overflow error generally happens with Columns and Rows because they don't pass constraint to the children along the main axis.
136+
137+
So how do we solve the problem of widgets going out of bounds? We use a special widget called **Expanded** or **Flex**.
138+
139+
**Expanded** will fill the remaining space with the child widget, which in our case is the orange widget. Note that when using Expanded, it will completely ignore the child's height.
140+
141+
<details>
142+
<summary>Example 1</summary>
143+
144+
<p>
145+
146+
```dart
147+
import 'package:flutter/material.dart';
148+
import 'package:flutter/widgets.dart';
149+
150+
class ColumnExampleResponsive extends StatelessWidget {
151+
const ColumnExampleResponsive({Key key}) : super(key: key);
152+
153+
@override
154+
Widget build(BuildContext context) {
155+
return Scaffold(
156+
appBar: AppBar(
157+
title: Text('Example 1'),
158+
),
159+
body: Column(
160+
children: [
161+
Container(
162+
color: Colors.green,
163+
height: 100,
164+
),
165+
Container(
166+
color: Colors.blue,
167+
height: 300,
168+
),
169+
Expanded(
170+
child: Container(
171+
color: Colors.orange,
172+
height: 500,
173+
),
174+
),
175+
],
176+
),
177+
);
178+
}
179+
}
180+
```
181+
</p>
182+
</details>
183+
184+
Additonal Notes:
185+
- **Flexible** is similar to Expanded but with more options on the Columns on how children should take up space.
186+
- Rows is pretty similar to Column, except that the main axis is controlled by width.
187+
188+
### 1.2.2. Handling Orientation
189+
190+
- Use OrientationBuilder to know what the current orientation and return the respective widget
191+
- Disable particular orientation
192+
193+
## 1.3. Handling Wider Screens
194+
195+
So for handling different mobile device screen, using Columns/Rows with Expanded is sufficient but to
196+
expand the repsonsiveness to wider screens like desktop apps and desktop browsers, we have to rely on
197+
either MediaQuery or LayoutBuilder.
198+
199+
### 1.3.1. MediaQuery
200+
201+
Using MediaQuery, you can get information like screen dimensions, accessibilty information which you can use to handle various screen sizes.
202+
203+
<details>
204+
<summary>Media Query Example</summary>
205+
206+
<p>
207+
208+
```dart
209+
import 'package:flutter/material.dart';
210+
import 'package:flutter/widgets.dart';
211+
212+
import '../responsive_util.dart';
213+
214+
class MediaQueryResponsive extends StatelessWidget {
215+
const MediaQueryResponsive({Key key}) : super(key: key);
216+
217+
@override
218+
Widget build(BuildContext context) {
219+
return Scaffold(
220+
appBar: ResponsiveUtil.isWideScreen(context)
221+
? null
222+
: AppBar(
223+
title: Text('MediaQuery Responsive'),
224+
),
225+
body: GridView.count(
226+
crossAxisCount: MediaQuery.of(context).size.width < 500 ? 2 : 4,
227+
children: List.generate(100, (index) {
228+
return Container(
229+
child: Center(
230+
child: Image.network(
231+
'https://picsum.photos/id/${index + 100}/${MediaQuery.of(context).size.width < 500 ? (MediaQuery.of(context).size.width / 2).round() : (MediaQuery.of(context).size.width / 4).round()}',
232+
loadingBuilder: (BuildContext context, Widget child,
233+
ImageChunkEvent loadingProgress) {
234+
if (loadingProgress == null) return child;
235+
return Center(
236+
child: CircularProgressIndicator(),
237+
);
238+
},
239+
),
240+
),
241+
);
242+
}),
243+
));
244+
}
245+
}
246+
247+
```
248+
</p>
249+
</details>
250+
251+
### 1.3.2. Layout Builder
252+
253+
Layout builder is similar to MediaQuery when it comes to screen sizes but it can used with any widget
254+
and get the parent constraints.
255+
256+
<details>
257+
<summary>Media Query Example</summary>
258+
259+
<p>
260+
261+
```dart
262+
import 'package:flutter/material.dart';
263+
import 'package:flutter/widgets.dart';
264+
265+
import '../responsive_util.dart';
266+
267+
class LayoutBuilderResponsive extends StatelessWidget {
268+
const LayoutBuilderResponsive({Key key}) : super(key: key);
269+
270+
@override
271+
Widget build(BuildContext context) {
272+
return Scaffold(
273+
appBar: ResponsiveUtil.isWideScreen(context)
274+
? null
275+
: AppBar(
276+
title: Text('LayoutBuilder Responsive'),
277+
),
278+
body: LayoutBuilder(
279+
builder: (context, constraints) {
280+
return GridView.count(
281+
crossAxisCount: constraints.maxWidth < 500 ? 2 : 4,
282+
children: List.generate(100, (index) {
283+
return Container(
284+
child: Center(
285+
child: Image.network(
286+
'https://picsum.photos/id/${index + 400}/${constraints.maxWidth < 500 ? (constraints.maxWidth / 2).round() : (constraints.maxWidth / 4).round()}',
287+
loadingBuilder: (BuildContext context, Widget child,
288+
ImageChunkEvent loadingProgress) {
289+
if (loadingProgress == null) return child;
290+
return Center(
291+
child: CircularProgressIndicator(),
292+
);
293+
},
294+
),
295+
),
296+
);
297+
}),
298+
);
299+
},
300+
),
301+
);
302+
}
303+
}
304+
```
305+
</p>
306+
</details>

android/.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
gradle-wrapper.jar
2+
/.gradle
3+
/captures/
4+
/gradlew
5+
/gradlew.bat
6+
/local.properties
7+
GeneratedPluginRegistrant.java
8+
9+
# Remember to never publicly share your keystore.
10+
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11+
key.properties

0 commit comments

Comments
 (0)