參見: Layouts in Flutter
程式碼參見:pavlovasizingrow_column
grid_and_listbottom_navigation_demo.dart

壹、Lay out multiple widgets vertically and horizontally

一、元件樹

通常在Flutter中,我們使用的元件會有相互層遞的父子關係,每個圓圈都代表一個元件群,我們在撰寫程式時也可以透過類似的書寫方式來釐清元件間的關聯

  • 越上層是包裹越外圍的元件

  • 越下層是包裹越內層的元件

  • 葉子的部分則為單一元件

二、Row與Column

通常我們在規劃構圖時,都會針對「水平(Row)、垂直(Column)」進行組件的分割。以下圖為例,可以看到不同的Row與Column規劃,並且圖中的紅框皆代表同一Row、綠框皆代表同一Column

  • 外層的紅框: 包含兩個子元件,一個圖片與Column元件

  • 左方綠框: 包含了四個子元件,包含了兩段文字、星等與其他資訊

而按照同樣邏輯,我們可以把綠框內的元件再行細分為不同的Row與Column組成,直到我們的目標對象已經是獨立元件或是群組為止。

如果以元件樹的方式概略可以畫為下圖

同時,Flutter也提供更為方便的元件來管理Row與Column的行為,像是ListTile與ListView等。

  • ListTile:提供了用於設置leading 和trailing 的屬性,支持單列最多3行文字的列表項目。

  • ListView:通常會包覆在ListTile外,具有自動滾動功能

三、Aligning widgets

在Row與Column中,Flutter也提供了對齊的元件,mainAxisAlignment與crossAxisAlignment,並且我們在定義Main Axis與Cross Axis時會針對目前的”主方向”而定

  • 若是對象為Row,則橫向為Main Axis、縱向為Cross Axis

  • 若是對象為Column,則縱向為Main Axis、橫向為Cross Axis

並且,提供不同的對齊方式

  • center: 置中

  • end: 靠近尾端

  • start: 靠近起點

  • spaceAround: 去除子元素空間的均分,兩端有留空

  • spaceBetween: 兩端貼齊頭與尾,中間元件空間均分

  • spaceEvenly: 所有空間均分,兩端有留空

Row 的 mainAxisAlignment使用範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Row Example'),
),
body: Center(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround, // 元件在Row主軸上對齊方式,空間的均分,兩端有留空
children: <Widget>[
Container(
color: Colors.red,
width: 50.0,
height: 50.0,
),
Container(
color: Colors.green,
width: 50.0,
height: 50.0,
),
Container(
color: Colors.blue,
width: 50.0,
height: 50.0,
),
],
),
),
),
);
}
}

Row 的 mainAxisAlignment使用範例

  • 輸出結果

Column 的 mainAxisAlignment使用範例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Column Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center, // // 元件在Row主軸上對齊方式,置中
children: <Widget>[
Container(
color: Colors.red,
width: 50.0,
height: 50.0,
),
Container(
color: Colors.green,
width: 50.0,
height: 50.0,
),
Container(
color: Colors.blue,
width: 50.0,
height: 50.0,
),
],
),
),
),
);
}
}

Column 的 mainAxisAlignment使用範例

  • 輸出結果

四、Sizing widgets: Expanded 元件

在Flutter中,若是使用的元件在呈現上超過了頁面大小,會出現Error與在畫面上呈現黃黑交錯的膠條,這個時候,我們可以選擇使用Expanded元件來避免這個問題

  • 報錯顯示

Expanded元件使用方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Row Example'),
),
body: Center(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded( //使用Expended元件,避免圖片超過大小而出現Error
child: Image.network(
'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl.jpg',
fit: BoxFit.cover, //圖片填充方式
),
),
Expanded(
flex: 2,//可以更改係數來調整圖片格式,預設為1
child: Image.network(
'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl.jpg',
fit: BoxFit.cover,
),
),
Expanded(
child: Image.network(
'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl.jpg',
fit: BoxFit.cover,
),
),
],
),
),
),
);
}
}

Expanded元件使用方式

  • 輸出結果

我們也可以更改flex係數,來改變圖片呈現的大小比例,例如我們在剛剛的程式碼第二張圖片加上flex: 2的描述

  • 輸出結果

可以看到第二張圖片的呈現比例變大,具有凸顯的效果

五、Packing widgets: 包裝元件

有時候,我們會需要元件間的距離更為緊密,例如: 製作星等的效果。那麼,我們就可以用mainAxisSize: MainAxisSize.min的指定方式,將Row的主軸設為最小,以達成此效果

星等包裝使用方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import 'package:flutter/material.dart';

void main() {
runApp(MyApp());
}

class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Star Icons'),
),
body: Center(
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
Icon(Icons.star, color: Colors.green[500]),
const Icon(Icons.star, color: Colors.black),
const Icon(Icons.star, color: Colors.black),
],
),
),
),
);
}
}

星等包裝使用方式

  • 輸出結果