Flutter基础——布局一

拥有多个子元素的布局widget

Posted by lingjye on July 18, 2019

iOS中常常借助frame,VFL,AutoLayout,UIStackView或者Masonry,SnapKit等来实现子控件怎么布局。Flutter中的布局需要使用Widget,不同的Widget可以实现不同的效果。本文主要介绍几个基本的布局组件,及其用法。

###

Container

Container一个拥有绘制、定位、调整大小的 widget。

布局行为

如果Container没有子窗口,没有height,没有width,没有约束,并且父窗口提供无限制约束,则Container会尝试尽可能小。

如果Container没有子widget,没有alignment,而是一个height,width或constraints提供,则Container试图给出这些限制和父widget的约束相结合,以尽可能小。

如果Container没有子widget,没有height,没有width,没有constraints,没有alignment,但是父widget提供了有界约束,那么Container会扩展以适应父widget提供的约束。

如果Container具有alignment,并且widget提供无限制约束,则Container会尝试围绕子widget调整自身大小。

如果Container具有alignment,并且父widget提供有界约束,则Container会尝试展开以适合父widget,然后根据对齐方式将子widget置于其自身内部。

如果Container有一个子widget,但没有height,没有width,没有constraints,没有alignment,Container将约束从父级传递给子级,并调整自身大小以匹配子级。

另外margin和padding属性也会影响布局,decoration也会隐式的增加padding。

总体来说,Container会尝试对齐(alignment),根据子widget的大小调整自身大小,采用width,height以及约束(constraints),改变自身以适应父widget,并且要尽可能的小。

构造函数:

1
Container({Key key, AlignmentGeometry alignment, EdgeInsetsGeometry padding, Color color, Decoration decoration, Decoration foregroundDecoration, double width, double height, BoxConstraints constraints, EdgeInsetsGeometry margin, Matrix4 transform, Widget child })

参数:

  • alignment → AlignmentGeometry,子widget的对齐方式;
  • child → Widget,应该添加的子widget;
  • constraints → BoxConstraints,为子widget添加的约束;
  • decoration → Decoration,在子widget背后添加的装饰,例如边框等,不能与color同时使用;
  • foregroundDecoration → Decoration,在子widget前添加装饰;
  • margin → EdgeInsetsGeometry,子widget和decoration周围空出空间;
  • padding → EdgeInsetsGeometry,decoration里面的空出的空间,如果有的话将子widget放到padding内;
  • transform → Matrix4,在绘制容器前应用的转换矩阵;
  • hashCode → int,此对象的哈希码,只读;
  • key → Key,控制一个widget如何替换树中的另一个widget;
  • runtimeType → Type,表示对象的运行时类型。

方法:

  • build(BuildContext context) → Widget,描述此widget表示的用户界面部分;
  • debugFillProperties(DiagnosticPropertiesBuilder properties) → void,添加与节点关联的其他属性;
  • createElement() → StatelessElement,创建StatelessElement以管理该widget在树中的位置;
  • debugDescribeChildren() → List,返回描述此节点的子节点的DiagnosticsNode对象列表;
  • noSuchMethod(Invocation invocation) → dynamic,访问不存在的方法或属性时调用;
  • toDiagnosticsNode({String name, DiagnosticsTreeStyle style }) → DiagnosticsNode,返回调试工具和DiagnosticsNode.toStringDeep使用的对象的调试表示形式;
  • toString({DiagnosticLevel minLevel: DiagnosticLevel.debug }) → String,返回此对象的字符串表示形式;
  • toStringDeep({String prefixLineOne: ‘’, String prefixOtherLines, DiagnosticLevel minLevel: DiagnosticLevel.debug }) → String,返回此节点及其后代的字符串表示形式;
  • toStringShallow({String joiner: ‘, ‘, DiagnosticLevel minLevel: DiagnosticLevel.debug }) → String,返回对象的单行详细描述;
  • toStringShort() → String,该widget的简短文字描述

简单示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@override
Widget build(BuildContext context) {
	return Scaffold(
	  appBar: AppBar(
	    title: Text('Container'),
	  ),
	  body: Center(
	    child: Container(
	      margin: EdgeInsets.all(20.0), // 与父widget的边距为20
	      color: Colors.amber[600],
	      width: 500,// 当宽度超过屏幕宽度,看到的效果实际上是与屏幕保持20的边距
	      height: 500,
	    ),
	  ),
	);
}

设置约束并添加变换:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@override
Widget build(BuildContext context) {
	return Scaffold(
	  appBar: AppBar(
	    title: Text('Container'),
	  ),
	  body: Center(
	    child: Container(
	      constraints: BoxConstraints.expand(
	        height: Theme.of(context).textTheme.display1.fontSize * 1.1 + 200,
	      ),
	      padding: const EdgeInsets.all(8.0),
	      color: Colors.blue,
	      alignment: Alignment.center,
	      child: Text('Hello world', style: Theme.of(context).textTheme.display1.copyWith(color: Colors.white),),
	      transform: Matrix4.rotationZ(0.1),
	    ),
	  ),
	);
}

Padding

一个给其子widget添加指定的填充widget。

将布局约束传递给其子级时,Padding会按给定的填充缩小约束,从而导致子级以较小的大小进行布局。Padding然后根据其子widget的大小调整自身大小,通过填充物膨胀,有效地在子widget周围创造出空的空间。

Padding和Container.padding没有区别。Container不直接实现其属性。相反,Container 将许多更简单的小部件组合到一个方便的包中。Flutter中使用组合(而非继承)是构建widgets的主要机制。

构造函数:

1
Padding({Key key, @required EdgeInsetsGeometry padding, Widget child })

Center

将其子widget居中显示在自身内部的widget。

如果它的尺寸受到约束且widthFactorheightFactor为null,则此widget将尽可能大。如果维度不受约束且相应的大小因子为null,则此widget将匹配其在该维度中的子项大小。如果大小因子不为null,则此widget的相应维度将是子维度和大小因子的乘积。例如,如果widthFactor为2.0,则此widget的宽度将始终是子widget宽度的两倍。

构造函数:

1
Center({Key key, double widthFactor, double heightFactor, Widget child })

参数:

  • heightFactor → double,如果为非null,则将其高度设置为子widget高度乘以此系数;
  • widthFactor → double,如果为非null,则将其宽度设置为子widget宽度乘以此系数;

Align

一个可以将其子widget对齐,并可以根据子widget的大小自动调整大小的widget。

如果它的尺寸受到约束且widthFactor和heightFactor为null,则此widget将尽可能大 。如果维度不受约束且相应的大小因子为null,则widget将匹配其在该维度中的子项大小。如果大小因子不为null,则此widget的相应维度将是子维度和大小因子的乘积。例如,如果widthFactor为2.0,则此widget的宽度将始终是子widget宽度的两倍。

构造函数:

1
Align({Key key, AlignmentGeometry alignment: Alignment.center, double widthFactor, double heightFactor, Widget child })

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
Center(
  child: Container(
    height: 120.0,
    width: 120.0,
    color: Colors.blue[50],
    child: Align(
      alignment: Alignment.topRight,
      child: FlutterLogo(
        size: 60,
      ),
    ),
  ),
)

FittedBox

按自己的大小调整其子widget的大小和位置的widget。

AspectRatio

一个试图将子widget的大小指定为某个特定的长宽比的widget。

ConstrainedBox

对其子项施加附加约束的widget。

Baseline

根据子项的基线对它们的位置进行定位的widget。

FractionallySizedBox

一个widget,它把它的子项放在可用空间的一小部分。

IntrinsicHeight

一个将它的子widget的高度调整其本身实际的高度的widget。

IntrinsicWidth

将它的子widget的宽度调整其本身实际的宽度的widget。

LimitedBox

一个当其自身不受约束时才限制其大小的盒子。

Offstage

一个布局widget,可以控制其子widget的显示和隐藏。

OverflowBox

对其子项施加不同约束的widget,它可能允许子项溢出父级。

SizedBox

一个特定大小的盒子。这个widget强制它的孩子有一个特定的宽度和高度。如果宽度或高度为NULL,则此widget将调整自身大小以匹配该维度中的孩子的大小。

SizedOverflowBox

一个特定大小的widget,但是会将它的原始约束传递给它的孩子,它可能会溢出。

Transform

在绘制子widget之前应用转换的widget。

与在布局之前应用旋转的RotatedBox不同,此对象在绘制之前应用其转换,这意味着在计算此widget的孩子(以及此widget)消耗的空间量时不会考虑转换。

旋转并倾斜文本widget,右上角固定到其原始位置示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class _TransformSampleAppPageState extends State<TransformSampleAppPage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
       appBar: AppBar(
         title: Text('Scaffold'),
       ),
       body: Container(
         color: Colors.black,
         child: Transform(
           alignment: Alignment.topLeft,
           transform: Matrix4.skewY(0.3)..rotateZ(-pi / 12.0),
           child: Container(
             padding: const EdgeInsets.all(50.0),
             margin: const EdgeInsets.all(50),
             color: Colors.red[100],
             child: const Text('Apartment for rent!'),
           ),
         ),
       ),
    );
  }
}

构造函数

1
Transform({Key key, @required Matrix4 transform, Offset origin, AlignmentGeometry alignment, bool transformHitTests: true, Widget child })

参数

  • alignment → AlignmentGeometry,原点的对齐方式,相对于方框的大小。
  • origin → Offset,坐标系的原点(相对于此渲染对象的左上角)在其中应用矩阵。
  • transform → Matrix4,在绘画过程中改变孩子的矩阵。
  • transformHitTests → bool,是否在执行命中测试时应用转换。
  • child → Widget,子widget

CustomSingleChildLayout

一个自定义的拥有单个子widget的布局widget

本文Demo