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
,使子视图内容可滑动。