老孟导读:Flutter中有这么一类组件,用于定位、装饰、控制子组件,比如 Container (定位、装饰)、Expanded (扩展)、SizedBox (固定尺寸)、AspectRatio (宽高比)、FractionallySizedBox (占父组件比例)。这些组件的使用频率非常高,下面一一介绍,最后给出项目中实际案例熟悉其用法。
【Flutter实战】系列文章地址:http://laomengit.com/guide/introduction/mobile_system.html

Container 是最常用的组件之一,它是单容器类组件,即仅能包含一个子组件,用于装饰和定位子组件,例如设置背景颜色、形状等。

最简单的用法如下:

  1. Container(
  2. child: Text('老孟'),
  3. )

子组件不会发生任何外观上的变化:

设置背景颜色:

  1. Container(
  2. color: Colors.blue,
  3. child: Text('老孟'),
  4. )

设置内边距( padding ) 和 外边距( margin )

  1. Container(
  2. color: Colors.blue,
  3. child: Container(
  4. margin: EdgeInsets.all(10),
  5. padding: EdgeInsets.all(20),
  6. color: Colors.red,
  7. child: Text('老孟'),
  8. ),
  9. )

效果如下:

decoration 属性设置子组件的背景颜色、形状等。设置背景为圆形,颜色为蓝色:

  1. Container(
  2. child: Text('老孟,专注分享Flutter技术及应用'),
  3. decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.blue),
  4. )

默认情况下,圆形的直径等于 Container 窄边长度,相当于在矩形内绘制内切圆。

上面的情况明显不是我们希望看到了,希望背景是圆角矩形:

  1. Container(
  2. child: Text('老孟,专注分享Flutter技术及应用'),
  3. padding: EdgeInsets.symmetric(horizontal: 10),
  4. decoration: BoxDecoration(
  5. shape: BoxShape.rectangle,
  6. borderRadius: BorderRadius.all(Radius.circular(20)),
  7. color: Colors.blue),
  8. )

除了背景我们可以设置边框效果,代码如下:

  1. Container(
  2. child: Text('老孟,专注分享Flutter技术及应用'),
  3. padding: EdgeInsets.symmetric(horizontal: 10),
  4. decoration: BoxDecoration(
  5. borderRadius: BorderRadius.circular(12),
  6. border: Border.all(
  7. color: Colors.blue,
  8. width: 2,
  9. ),
  10. ),
  11. )

创建圆角图片和圆形图片:

  1. Container(
  2. height: 200,
  3. width: 200,
  4. decoration: BoxDecoration(
  5. image: DecorationImage(
  6. image: NetworkImage(
  7. 'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg'),
  8. fit: BoxFit.cover,
  9. ),
  10. border: Border.all(
  11. color: Colors.blue,
  12. width: 2,
  13. ),
  14. borderRadius: BorderRadius.circular(12),
  15. ),
  16. )


修改其形状为圆形,代码如下:

  1. Container(
  2. height: 200,
  3. width: 200,
  4. decoration: BoxDecoration(
  5. image: DecorationImage(
  6. image: NetworkImage(
  7. 'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg'),
  8. fit: BoxFit.cover,
  9. ),
  10. border: Border.all(
  11. color: Colors.blue,
  12. width: 2,
  13. ),
  14. shape: BoxShape.circle,
  15. ),
  16. )

设置对齐方式为居中,背景色为蓝色,代码如下:

  1. Container(
  2. color: Colors.blue,
  3. child: Text('老孟,一个有态度的程序员'),
  4. alignment: Alignment.center,
  5. )

注意:设置对齐方式后,Container将会充满其父控件,相当于Android中 match_parent

Alignment 已经封装了常用的位置,

通过名字就知道其位置,这里要介绍一下其他的位置,比如在距离左上角1/4处:

  1. Container(
  2. alignment: Alignment(-.5,-.5),
  3. child: Text('老孟,专注分享Flutter技术及应用'),
  4. )

所以这里有一个非常重要的坐标系,Alignment 坐标系如下:

组件的中心为坐标原点。

设置固定的宽高属性:

  1. Container(
  2. color: Colors.blue,
  3. child: Text('老孟,专注分享Flutter技术及应用'),
  4. alignment: Alignment.center,
  5. height: 60,
  6. width: 250,
  7. )

通过 constraints 属性设置最大/小宽、高来确定大小,如果不设置,默认最小宽高是0,最大宽高是无限大(double.infinity),约束width代码如下:

  1. Container(
  2. color: Colors.blue,
  3. child: Text('老孟,专注分享Flutter技术及应用'),
  4. alignment: Alignment.center,
  5. constraints: BoxConstraints(
  6. maxHeight: 100,
  7. maxWidth: 300,
  8. minHeight: 100,
  9. minWidth: 100,
  10. ),
  11. )

通过transform可以旋转、平移、缩放Container,旋转代码如下:

  1. Container(
  2. color: Colors.blue,
  3. child: Text('老孟,专注分享Flutter技术及应用'),
  4. alignment: Alignment.center,
  5. height: 60,
  6. width: 250,
  7. transform: Matrix4.rotationZ(0.5),
  8. )

注意:Matrix4.rotationZ()参数的单位是弧度而不是角度

SizedBox 是具有固定宽高的组件,直接指定具体的宽高,用法如下:

  1. SizedBox(
  2. height: 60,
  3. width: 200,
  4. child: Container(
  5. color: Colors.blue,
  6. alignment: Alignment.center,
  7. child: Text('老孟,专注分享Flutter技术及应用'),
  8. ),
  9. )

设置尺寸无限大,如下:

  1. SizedBox(
  2. height: double.infinity,
  3. width: double.infinity,
  4. ...
  5. )

虽然设置了无限大,子控件是否会无限长呢?不,不会,子控件依然会受到父组件的约束,会扩展到父组件的尺寸,还有一个便捷的方式设置此方式:

  1. SizedBox.expand(
  2. child: Text('老孟,专注分享Flutter技术及应用'),
  3. )

SizedBox 可以没有子组件,但仍然会占用空间,所以 SizedBox 非常适合控制2个组件之间的空隙,用法如下:

  1. Column(
  2. children: <Widget>[
  3. Container(height: 30,color: Colors.blue,),
  4. SizedBox(height: 30,),
  5. Container(height: 30,color: Colors.red,),
  6. ],
  7. )

AspectRatio 是固定宽高比的组件,用法如下:

  1. Container(
  2. height: 300,
  3. width: 300,
  4. color: Colors.blue,
  5. alignment: Alignment.center,
  6. child: AspectRatio(
  7. aspectRatio: 2 / 1,
  8. child: Container(color: Colors.red,),
  9. ),
  10. )

aspectRatio 是宽高比,可以直接写成分数的形式,也可以写成小数的形式,但建议写成分数的形式,可读性更高。效果如下:

FractionallySizedBox 是一个相对父组件尺寸的组件,比如占父组件的70%:

  1. Container(
  2. height: 200,
  3. width: 200,
  4. color: Colors.blue,
  5. child: FractionallySizedBox(
  6. widthFactor: .8,
  7. heightFactor: .3,
  8. child: Container(
  9. color: Colors.red,
  10. ),
  11. ),
  12. )

通过 alignment 参数控制子组件显示的位置,默认为居中,用法如下:

  1. FractionallySizedBox(
  2. alignment: Alignment.center,
  3. ...
  4. )

ExpandedFlexibleSpacer 都是具有权重属性的组件,可以控制 Row、Column、Flex 的子控件如何布局的组件。

Flexible 组件可以控制 Row、Column、Flex 的子控件占满父组件,比如,Row 中有3个子组件,两边的宽是100,中间的占满剩余的空间,代码如下:

  1. Row(
  2. children: <Widget>[
  3. Container(
  4. color: Colors.blue,
  5. height: 50,
  6. width: 100,
  7. ),
  8. Flexible(
  9. child: Container(
  10. color: Colors.red,
  11. height: 50,
  12. )
  13. ),
  14. Container(
  15. color: Colors.blue,
  16. height: 50,
  17. width: 100,
  18. ),
  19. ],
  20. )

还是有3个子组件,第一个占1/6,第二个占2/6,第三个占3/6,代码如下:

  1. Column(
  2. children: <Widget>[
  3. Flexible(
  4. flex: 1,
  5. child: Container(
  6. color: Colors.blue,
  7. alignment: Alignment.center,
  8. child: Text('1 Flex/ 6 Total',style: TextStyle(color: Colors.white),),
  9. ),
  10. ),
  11. Flexible(
  12. flex: 2,
  13. child: Container(
  14. color: Colors.red,
  15. alignment: Alignment.center,
  16. child: Text('2 Flex/ 6 Total',style: TextStyle(color: Colors.white),),
  17. ),
  18. ),
  19. Flexible(
  20. flex: 3,
  21. child: Container(
  22. color: Colors.green,
  23. alignment: Alignment.center,
  24. child: Text('3 Flex/ 6 Total',style: TextStyle(color: Colors.white),),
  25. ),
  26. ),
  27. ],
  28. )

子组件占比 = 当前子控件 flex / 所有子组件 flex 之和。

Flexible中 fit 参数表示填满剩余空间的方式,说明如下:

  • tight:必须(强制)填满剩余空间。
  • loose:尽可能大的填满剩余空间,但是可以不填满。

这2个看上去不是很好理解啊,什么叫尽可能大的填满剩余空间?什么时候填满?看下面的例子:

  1. Row(
  2. children: <Widget>[
  3. Container(
  4. color: Colors.blue,
  5. height: 50,
  6. width: 100,
  7. ),
  8. Flexible(
  9. child: Container(
  10. color: Colors.red,
  11. height: 50,
  12. child: Text('Container',style: TextStyle(color: Colors.white),),
  13. )
  14. ),
  15. Container(
  16. color: Colors.blue,
  17. height: 50,
  18. width: 100,
  19. ),
  20. ],
  21. )

这段代码是在最上面代码的基础上给中间的红色Container添加了Text子控件,此时红色Container就不在充满空间,再给Container添加对齐方式,代码如下:

  1. Row(
  2. children: <Widget>[
  3. Container(
  4. color: Colors.blue,
  5. height: 50,
  6. width: 100,
  7. ),
  8. Flexible(
  9. child: Container(
  10. color: Colors.red,
  11. height: 50,
  12. alignment: Alignment.center,
  13. child: Text('Container',style: TextStyle(color: Colors.white),),
  14. )
  15. ),
  16. Container(
  17. color: Colors.blue,
  18. height: 50,
  19. width: 100,
  20. ),
  21. ],
  22. )


此时又填满剩余空间。

大家是否还记得 Container 组件的大小是如何调整的吗?Container 默认是适配子控件大小的,但当设置对齐方式时 Container 将会填满父组件,因此是否填满剩余空间取决于子组件是否需要填满父组件。

如果把 Flexible 中子组件由 Container 改为 OutlineButton,代码如下:

  1. Row(
  2. children: <Widget>[
  3. Container(
  4. color: Colors.blue,
  5. height: 50,
  6. width: 100,
  7. ),
  8. Flexible(
  9. child: OutlineButton(
  10. child: Text('OutlineButton'),
  11. ),
  12. ),
  13. Container(
  14. color: Colors.blue,
  15. height: 50,
  16. width: 100,
  17. ),
  18. ],
  19. )

OutlineButton 正常情况下是不充满父组件的,因此最终的效果应该是不填满剩余空间:

下面再来介绍另一个权重组件 Expanded ,源代码如下:

  1. class Expanded extends Flexible {
  2. /// Creates a widget that expands a child of a [Row], [Column], or [Flex]
  3. /// so that the child fills the available space along the flex widget's
  4. /// main axis.
  5. const Expanded({
  6. Key key,
  7. int flex = 1,
  8. @required Widget child,
  9. }) : super(key: key, flex: flex, fit: FlexFit.tight, child: child);
  10. }

Expanded 继承字 Flexible,fit 参数固定为 FlexFit.tight,也就是说 Expanded 必须(强制)填满剩余空间。上面的 OutlineButton 想要充满剩余空间可以直接使用 Expanded :

  1. Row(
  2. children: <Widget>[
  3. Container(
  4. color: Colors.blue,
  5. height: 50,
  6. width: 100,
  7. ),
  8. Expanded(
  9. child: OutlineButton(
  10. child: Text('OutlineButton'),
  11. ),
  12. ),
  13. Container(
  14. color: Colors.blue,
  15. height: 50,
  16. width: 100,
  17. ),
  18. ],
  19. )

Spacer 也是一个权重组件,源代码如下:

  1. @override
  2. Widget build(BuildContext context) {
  3. return Expanded(
  4. flex: flex,
  5. child: const SizedBox.shrink(),
  6. );
  7. }

Spacer 的本质也是 Expanded 的实现的,和Expanded的区别是:Expanded 可以设置子控件,而 Spacer 的子控件尺寸是0,因此Spacer适用于撑开 Row、Column、Flex 的子控件的空隙,用法如下:

  1. Row(
  2. children: <Widget>[
  3. Container(width: 100,height: 50,color: Colors.green,),
  4. Spacer(flex: 2,),
  5. Container(width: 100,height: 50,color: Colors.blue,),
  6. Spacer(),
  7. Container(width: 100,height: 50,color: Colors.red,),
  8. ],
  9. )

三个权重组建总结如下

  • Spacer 是通过 Expanded 实现的,Expanded继承自Flexible。
  • 填满剩余空间直接使用Expanded更方便。
  • Spacer 用于撑开 Row、Column、Flex 的子组件的空隙。

先看下效果:

拿到效果图先不要慌 (取出手机拍照发个朋友圈

版权声明:本文为mengqd原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/mengqd/p/13179103.html