Flutter -容器布局知多少

881 阅读5分钟

线性布局 Column Row

image.png

Row

Row 布局组件类似于 Android 中的 LinearLayout 线性布局,它用来做水平横向布局使用,里面的 children 子元素按照水平方向进行排列。

Row({
    Key key,
  	
  	// * 子元素集合
    List<Widget> children = const <Widget>[],
  
    // 主轴方向上的对齐方式(Row的主轴是横向轴)
    MainAxisAlignment mainAxisAlignment = MainAxisAlignment.start,
    // 在主轴方向(Row的主轴是横向轴)占有空间的值,默认是max
    MainAxisSize mainAxisSize = MainAxisSize.max,
    // 在交叉轴方向(Row是纵向轴)的对齐方式,Row的高度等于子元素中最高的子元素高度
    CrossAxisAlignment crossAxisAlignment = CrossAxisAlignment.center,
    
 		// 水平方向子元素的排列方向:从左到右排列还是反向
    TextDirection textDirection,
    // 表示纵轴(垂直)的对齐排列方向,默认是VerticalDirection.down,表示从上到下。这个参数一般用于Column组件里
    VerticalDirection verticalDirection = VerticalDirection.down,
    // 字符对齐基线方式
    TextBaseline textBaseline,

  })
  • MainAxisAlignment 主轴属性:主轴方向上的对齐方式,Row 是横向轴为主轴
enum MainAxisAlignment {
  // 按照主轴起点对齐,例如:按照靠近最左侧子元素对齐
  start,

  // 将子元素放置在主轴的末尾,按照末尾对齐
  end,

  // 子元素放置在主轴中心对齐
  center,

  // 将主轴方向上的空白区域均分,使得子元素之间的空白区域相等,首尾子元素都靠近首尾,没有间隙。有点类似于两端对齐
  spaceBetween,

  // 将主轴方向上的空白区域均分,使得子元素之间的空白区域相等,但是首尾子元素的空白区域为1/2
  spaceAround,

  // 将主轴方向上的空白区域均分,使得子元素之间的空白区域相等,包括首尾子元素
  spaceEvenly,
}
  • CrossAxisAlignment 交叉属性:在交叉轴方向的对齐方式,Row 是纵向轴。Row 的高度等于子元素中最高的子元素高度。
enum CrossAxisAlignment {
  // 子元素在交叉轴上起点处展示
  start,

  // 子元素在交叉轴上末尾处展示
  end,

  // 子元素在交叉轴上居中展示
  center,

  // 让子元素填满交叉轴方向
  stretch,

  // 在交叉轴方向,使得子元素按照baseline对齐
  baseline,
}
  • MainAxisSize 在主轴方向子元素占有空间的方式,Row 的主轴是横向轴。默认是 max
enum MainAxisSize {
  // 根据传入的布局约束条件,最大化主轴方向占用可用空间,也就是尽可能充满可用宽度
  max,

  // 与max相反,是最小化占用主轴方向的可用空间
  min,
}

Column

Column 是纵向排列子元素,属性和Row基本一致,只是方向有点不同而已。

Box Model 盒子模型

image.png

不同部分的说明:

  • Margin(外边距)  - 边框意外的距离。
  • Border(边框)  - 围绕在内边距和内容外的边框。
  • Padding(内边距)  - 边框内部到内容的距离。
  • Content(内容)  - 盒子的内容,显示文本和图像。

Container

Container 是一个组合类容器,它本身不对应具体的 RenderObject,它是 DecoratedBox、ConstrainedBox、Transform、Padding、Align 等组件组合的一个多功能容器,所以我们只需通过一个 Container 组件可以实现同时需要装饰、变换、限制的场景。

Container({
    Key key,
    // 容器子Widget对齐方式
    this.alignment,
    // 容器内部padding
    this.padding,
    // 背景色
    Color color,
    // 背景装饰
    Decoration decoration,
    // 前景装饰
    this.foregroundDecoration,
    // 容器的宽度
    double width,
    // 容器的高度
    double height,
    // 容器大小的限制条件
    BoxConstraints constraints,
    // 容器外部margin
    this.margin,
    // 变换,如旋转
    this.transform,
    // 容器内子Widget
    this.child,
  })
BoxDecoration 装饰
const BoxDecoration({
  // 背景色
  this.color,
  // 背景图片
  this.image,
  // 边框样式
  this.border,
  // 边框圆角
  this.borderRadius,
  // 阴影
  this.boxShadow,
  // 渐变
  this.gradient,
  // 背景混合模式
  this.backgroundBlendMode,
  // 形状
  this.shape = BoxShape.rectangle,
})

弹性布局Flex

弹性布局允许子组件按照一定比例来分配父容器空间。

我们可以发现 Column Row 组件都是继承与 Flex,功能非常强大,通常我们直接用 Column Row 即可。

Expanded

Expanded 只能放在 Flex、Column、Row 中使用

  • 把包裹的元素撑开
 @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
          Expanded(
            child: Container(
              color: Colors.amber,
            ),
          ),
          const FlutterLogo(
            size: 32,
          ),
        ],
      ),
    );
  }
  • Flex 属性调整比例
children: [
    Expanded(
      flex: 1,
      child: Container(
        color: Colors.amber,
      ),
    ),
    const Expanded(
      flex: 2,
      child: FlutterLogo(
        size: 32,
      ),
    ),
  ],

Spacer

留白撑开,很适合用在标题按钮的场景中

children: [
    Container(
      width: 50,
      color: Colors.amber,
    ),
    const Spacer(),
    const FlutterLogo(
      size: 32,
    ),
  ],

层叠布局Stack

Stack 允许子组件堆叠

Stack({

	Key key,
	
	// 对齐方式,默认是左上角(topStart)
	this.alignment = AlignmentDirectional.topStart,
	
	// 对齐方向
	this.textDirection,
	
	// 定义如何设置无定位子元素尺寸,默认为loose
	this.fit = StackFit.loose,
  
  // 对超出 Stack 显示空间的部分如何剪裁
  this.clipBehavior = Clip.hardEdge,
	
	// 子元素
	List<Widget> children = const <Widget>[],

})

Positioned

根据 Stack 的四个角来确定子组件的位置

const Positioned({

	Key key,
	
	this.left, // 上下左右位置
	this.top,
	this.right,
	this.bottom,
	
	this.width, // 宽高
	this.height,
	
	@required Widget child,
  
})

流式布局 Wrap

用 Row 的时候可以发现子元素不会自动换行,这时候就需要 Wrap 了。

Wrap({
  this.direction = Axis.horizontal,
  // 主轴方向的对齐方式
  this.alignment = WrapAlignment.start,
  // 主轴方向子widget的间距
  this.spacing = 0.0,
  // 纵轴方向的对齐方式
  this.runAlignment = WrapAlignment.start,
  // 纵轴方向的间距
  this.runSpacing = 0.0,
  // 交叉轴对齐方式
  this.crossAxisAlignment = WrapCrossAlignment.start,
  this.textDirection,
  this.verticalDirection = VerticalDirection.down,
  List<Widget> children = const <Widget>[],
})

对齐定位 Align

调整子元素在父元素的位置

Align({
  Key key,
  
  // 需要一个AlignmentGeometry类型的值
  // AlignmentGeometry 是一个抽象类,
  // 它有两个常用的子类:Alignment和 FractionalOffset
  this.alignment = Alignment.center,
  
  // 两个缩放因子
  // 会分别乘以子元素的宽、高,最终的结果就是 Align 组件的宽高
  this.widthFactor,
  this.heightFactor,
  Widget child,
})
Alignment

Alignment 是从 Align 的中心点出发

/// The top left corner.
  static const Alignment topLeft = Alignment(-1.0, -1.0);

  /// The center point along the top edge.
  static const Alignment topCenter = Alignment(0.0, -1.0);

  /// The top right corner.
  static const Alignment topRight = Alignment(1.0, -1.0);

  /// The center point along the left edge.
  static const Alignment centerLeft = Alignment(-1.0, 0.0);

  /// The center point, both horizontally and vertically.
  static const Alignment center = Alignment(0.0, 0.0);

  /// The center point along the right edge.
  static const Alignment centerRight = Alignment(1.0, 0.0);

  /// The bottom left corner.
  static const Alignment bottomLeft = Alignment(-1.0, 1.0);

  /// The center point along the bottom edge.
  static const Alignment bottomCenter = Alignment(0.0, 1.0);

  /// The bottom right corner.
  static const Alignment bottomRight = Alignment(1.0, 1.0);

Alignment(-1.0, -1.0) 标识从中心点出发,左上角

FractionalOffset

这种方式是固定从左上角出发

 @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Align(
        alignment: FractionalOffset(0.5, 0.1),
        child: FlutterLogo(
          size: 50,
        ),
      ),
    );
  }

用 FractionalOffset 对象,输入 0~1 的比例值

Center

Center 是集成了 Align 对象,默认 alignment=Alignment.center

Center 定义, 少了一个 alignment 参数

class Center extends Align {
  /// Creates a widget that centers its child.
  const Center({ Key? key, double? widthFactor, double? heightFactor, Widget? child })
    : super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child);
}

然后 Align 定义, 默认了 this.alignment = Alignment.center

class Align extends SingleChildRenderObjectWidget {
  /// Creates an alignment widget.
  ///
  /// The alignment defaults to [Alignment.center].
  const Align({
    Key? key,
    this.alignment = Alignment.center,
    this.widthFactor,
    this.heightFactor,
    Widget? child,
  }) : assert(alignment != null),
       assert(widthFactor == null || widthFactor >= 0.0),
       assert(heightFactor == null || heightFactor >= 0.0),
       super(key: key, child: child);

我正在参与掘金技术社区创作者签约计划招募活动,点击链接报名投稿