Flutter 佈局基礎 ——Column 豎直佈局#
Column
- 是豎直方向佈局子視圖的 Widget,和Row
相似,如果想要子視圖充滿,可使用Expanded
把子視圖包括起來。
Column
不能滑動(通常來說使用Column
時,子視圖內容不能超過父視圖的高度),如果真的有很多子視圖,需要滑動的時候,建議使用ListView
。
如果想要橫向佈局,使用Row
。
如果只有一個元素,可考慮使用Align
或者Center
來佈局。
基礎使用#
Column 常用屬性如下:
- Column 常用屬性
- children: 子視圖
- textDirection: 子視圖水平佈局方向
- TextDirection.ltr: 從左到右
- TextDirection.rtl: 從右到左
- verticalDirection: 子視圖豎直佈局方向
- VerticalDirection.down: 從上到下,默認為這個
- VerticalDirection.up: 從下到上
- mainAxisSize: 子視圖在父視圖上豎直方向,占用大小
- MainAxisSize.min: 最小,設置了這個之後,再設置 mainAxisAlignment,顯示效果一致都是 start 的效果
- MainAxisSize.max: 最大,默認是這個,按父視圖大小來
- mainAxisAlignment: 子視圖在父視圖上的佈局方式,豎直方向佈局
- MainAxisAlignment.spaceAround: 子視圖之間和子視圖距離父視圖都留有間距
- MainAxisAlignment.center: 所有子視圖居中
- MainAxisAlignment.end: 所有子視圖居最末尾
- MainAxisAlignment.spaceBetween: 子視圖之間留有相等間距,與父視圖不留間距
- MainAxisAlignment.spaceEvenly: 子視圖之間和子視圖距離父視圖都留有間距,且間距都相等
- MainAxisAlignment.start,所有子視圖居於最開始
- crossAxisAlignment: 子視圖水平方向佈局方式
- CrossAxisAlignment.start: 水平居左佈局
- CrossAxisAlignment.end: 水平居右佈局
- CrossAxisAlignment.center: 水平居中佈局,默認為這個
- CrossAxisAlignment.stretch
- CrossAxisAlignment.baseline
下面一個個來看:
textDirection 效果:#
代碼如下:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('Column Learn'),
),
body: Column(
textDirection: TextDirection.ltr,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAß'),
Text('TextDirection rtl'),
Text('CrossAxisAlignment start'),
FlutterLogo()
],
),
),
);
}
}
效果如下:
從效果可以看出,textDirection
為子視圖水平佈局的方向,但是需要注意的是,這裡還同時設置了crossAxisAlignment
。因為不設置時,顯示的效果和CrossAxisAlignment.center
一致,而且只設置textDirection
並沒有效果,感興趣的可以自己驗證一下試試。
Ps:需要注意的是CrossAxisAlignment.center
的效果,不是想像中的,整個螢幕的寬度居中對齊。而實際是和最長的子視圖的寬度然後居中對齊。
verticalDirection 的效果#
代碼如下:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('Column Learn'),
),
body: Column(
verticalDirection: VerticalDirection.down,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAß'),
Text('VerticalDirection up'),
Text('CrossAxisAlignment start'),
FlutterLogo()
],
),
),
);
}
}
效果如下:
mainAxisSize && mainAxisAlignment 的效果#
mainAxisSize 效果:
代碼如下:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('Column Learn'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceAround,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAß'),
Text('MainAxisAlignment spaceAround'),
Text('MainAxisSize max'),
Text('CrossAxisAlignment start'),
FlutterLogo()
],
),
),
);
}
}
效果如下:
可以看出,同樣設置mainAxisAlignment
為spaceAround
的情況下,設置mainAxisSize
為min
與max
的不同,設置max
可以看到按照全螢幕來適配,設置min
則無效果。
mainAxisAlignment
(mainAxisSize
為max
時) 的效果:
代碼如下:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('Column Learn'),
),
body: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAß'),
Text('MainAxisAlignment spaceEvenly'),
Text('MainAxisSize max'),
Text('CrossAxisAlignment start'),
FlutterLogo()
],
),
),
);
}
}
效果如下:
從圖中可以看出當mainAxisSize
為max
時,mainAxisAlignment
各不同取值的顯示效果。
crossAxisAlignment 的效果#
代碼如下:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('Column Learn'),
),
body: Column(
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAß'),
Text('MainAxisSize max'),
Text('CrossAxisAlignment start'),
FlutterLogo()
],
),
),
);
}
}
顯示效果如下:
從上面可以看到,crossAxisAlignment
各不同值的顯示效果。
但是取值 baseline 時,報錯了,錯誤是
══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════ The following assertion was thrown building MyApp(dirty): textBaseline is required if you specify the crossAxisAlignment with CrossAxisAlignment.baseline 'package:flutter/src/widgets/basic.dart': Failed assertion: line 4369 pos 15: 'crossAxisAlignment != CrossAxisAlignment.baseline || textBaseline != null'
什麼原因呢?根據錯誤提示可以推斷出,當設置了rossAxisAlignment.baseline
時,需要設置textBaseline
屬性,故而,把textBaseline
屬性加上即可解決。
代碼如下:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('Column Learn'),
),
body: Column(
textBaseline: TextBaseline.alphabetic,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.baseline,
children: [
Text('AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAß'),
Text('TextBaseline alphabetic'),
Text('MainAxisSize max'),
Text('CrossAxisAlignment baseline'),
FlutterLogo()
],
),
),
);
}
}
效果如下:
textBaseline 的不同取值有什麼不同呢?參考What is the difference between alphabetic and ideographic in Flutter's TextBaseline enum
截圖如下:
官方提示需要注意的點#
需要注意的:一#
當Column
的子視圖中,有Expanded
或者Flexiable
的子視圖,而且這個Column
Widget 又放在了一個Column
Widget 或ListView
或其他不固定高度的 Widget 中,那麼此時就會報錯。
驗證代碼如下:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('Column Learn'),
),
body: Column(
textBaseline: TextBaseline.ideographic,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.baseline,
children: [
Column(
children: [Expanded(child: FlutterLogo())],
),
],
),
),
);
}
}
報錯如下:
══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════ The following assertion was thrown during performLayout(): RenderFlex children have non-zero flex but incoming height constraints are unbounded.
報錯的原因是:
當使用Expanded
時,需要的是父視圖的高度固定,被Expanded
包括的子視圖填充父視圖的區域。而如果嵌套了Column
或者ListView
或其他可滑動視圖時,父視圖的高度是不固定的,此時Expanded
也就無法填充了。
解決辦法:
當外層是Column
時,可以使用Expanded
把內層的Column
包括起來,這樣,相當於告訴內層的Column
Widget,高度時要填充外層的Column
Widget,從而也就相當於確定了高度。
代碼如下:
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: new AppBar(
title: new Text('Column Learn'),
),
body: Column(
textBaseline: TextBaseline.ideographic,
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.baseline,
children: [
Expanded(
child: Column(
children: [Expanded(child: FlutterLogo())],
),
),
],
),
),
);
}
}
而當外層是ListView
或者其他可滑動視圖時,由於可滑動視圖內容的高度是無法確定的,此時就需要考慮為什麼會外層有了不固定高度視圖,Column
Widget 的子視圖還會有Expanded
或Flexible
這種情況出現,通常解決方法是移除內層子視圖的Expanded
或Flexible
。
需要注意的:二#
和Row
類似,當子視圖內容超出了父視圖區域時,Flutter 在 Debug 模式下,會顯示黃色的提示。效果如下:
解決辦法:
考慮使用ListView
來代替Column
,使子視圖內容可滑動。