在上一节中我们讲过通过StackPositioned,我们可以指定一个或多个子元素相对于父元素各个边的精确偏移,并且可以重叠。但如果我们只想简单的调整一个子元素在父元素中的位置的话,使用Align组件会更简单一些。

Align

https://api.flutter.dev/flutter/widgets/Align-class.html
Align 组件可以调整子组件的位置,并且可以根据子组件的宽高来确定自身的的宽高,定义如下:

  1. const Align({
  2. Key key,
  3. this.alignment = Alignment.center,
  4. this.widthFactor,
  5. this.heightFactor,
  6. Widget child,
  7. })
  • alignment : 需要一个AlignmentGeometry类型的值,表示子组件在父组件中的起始位置。AlignmentGeometry 是一个抽象类,它有两个常用的子类:AlignmentFractionalOffset,我们将在下面的示例中详细介绍。

    topLeft、topCenter、topRight 顶部左对齐、顶部居中对齐、顶部右对齐 centerLeft、center、centerRight 垂直居中水平居左对齐、垂直居中水平居中对齐、垂直居中水平居右对齐 bottomLeft、bottomCenter、bottomRight 具体指定,从左至右、从上至下,范围 [-1, 1]。

  • widthFactorheightFactor是用于确定Align 组件本身宽高的属性;它们是两个缩放因子,会分别乘以子元素的宽、高,最终的结果就是Align 组件的宽高。如果值为null,则组件的宽高将会占用尽可能多的空间。

示例1:

对齐与相对定位 Align、Center - 图1

  1. Column(
  2. crossAxisAlignment: CrossAxisAlignment.start,
  3. children: [
  4. Container(
  5. width: 50.0,
  6. height: 50.0,
  7. color: Colors.green,
  8. child: Align(alignment: Alignment.topLeft, child: Text('你')),
  9. //等同于:
  10. //child: Align(alignment: Alignment(-1, -1), child: Text('你')),
  11. ),
  12. SizedBox(height: 10.0),
  13. Container(
  14. color: Colors.green,
  15. child: Align(alignment: Alignment.bottomCenter, child: Text('你')),
  16. ),
  17. SizedBox(height: 10.0),
  18. Container(
  19. color: Colors.green,
  20. child: Align(
  21. alignment: Alignment.bottomCenter,
  22. child: Text('你'),
  23. widthFactor: 2.0,
  24. heightFactor: 3.0,
  25. ),
  26. ),
  27. ],
  28. );

示例2

image.png

  1. Container(
  2. color: Colors.grey,
  3. width: 200,
  4. height: 200,
  5. child: Stack(
  6. children: [
  7. Align(
  8. alignment: Alignment.topLeft, //默认为center
  9. child: Container(width: 50, height: 50, color: Colors.red),
  10. ),
  11. Positioned(
  12. bottom: 10,
  13. right: 10,
  14. child: Container(
  15. color: Colors.yellow,
  16. child: Align(
  17. //通过指定 widthFactor 和 heightFactor 为 2,把Align组件的宽高都放大2倍。
  18. widthFactor: 2.0,
  19. heightFactor: 2.0,
  20. child: Container(width: 25, height: 25, color: Colors.red),
  21. ),
  22. ),
  23. ),
  24. Align(
  25. alignment: Alignment.topRight,
  26. child: FlutterLogo(
  27. //FlutterLogo 是Flutter SDK提供的一个组件,内容就是Flutter的商标。
  28. size: 50,
  29. ),
  30. ),
  31. ],
  32. ),
  33. );

Alignment

Alignment继承自AlignmentGeometry,表示矩形内的一个点,他有两个属性xy,分别表示在水平和垂直方向的偏移,Alignment定义如下:

  1. Alignment(this.x, this.y)

Alignment Widget会以矩形的中心点作为坐标原点,即Alignment(0.0, 0.0)xy的值从-1到1分别代表矩形左边到右边的距离和顶部到底边的距离,因此2个水平(或垂直)单位则等于矩形的宽(或高),如Alignment(-1.0, -1.0) 代表矩形的左侧顶点,而Alignment(1.0, 1.0)代表右侧底部终点,而Alignment(1.0, -1.0) 则正是右侧顶点,即Alignment.topRight

为了使用方便,矩形的原点、四个顶点,以及四条边的终点在Alignment类中都已经定义为了静态常量。

  1. Alignment.topLeft //等同于 Alignment(-1, -1)
  2. Alignment.centerRight //等同于 Alignment(1, 0)

Alignment可以通过其坐标转换公式将其坐标转为子元素的具体偏移坐标:

  1. (Alignment.x*childWidth/2+childWidth/2, Alignment.y*childHeight/2+childHeight/2)

其中childWidth为子元素的宽度,childHeight为子元素高度。(此处有疑)

现在我们再看看上面的示例,我们将Alignment(1.0, -1.0)带入上面公式,可得FlutterLogo的实际偏移坐标正是(60,0)。下面再看一个例子:

  1. Align(
  2. widthFactor: 2,
  3. heightFactor: 2,
  4. alignment: Alignment(2,0.0),
  5. child: FlutterLogo(
  6. size: 60,
  7. ),
  8. )

我们可以先想象一下运行效果:将Alignment(2,0.0)带入上述坐标转换公式,可以得到FlutterLogo的实际偏移坐标为(90,30)。实际运行如图4-12所示:
对齐与相对定位 Align、Center - 图3

FractionalOffset

FractionalOffset 继承自 Alignment,它和 Alignment唯一的区别就是坐标原点不同!FractionalOffset 的坐标原点为矩形的左侧顶点,这和布局系统的一致,所以理解起来会比较容易。FractionalOffset的坐标转换公式为:(此处有疑)

  1. 实际偏移 = (FractionalOffse.x * childWidth, FractionalOffse.y * childHeight)

下面看一个例子:

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

实际运行效果如图4-13所示下:
对齐与相对定位 Align、Center - 图4
我们将FractionalOffset(0.2, 0.6)带入坐标转换公式得FlutterLogo实际偏移为(12,36),和实际运行效果吻合。

Align和Stack对比

可以看到,AlignStack/Positioned都可以用于指定子元素相对于父元素的偏移,但它们还是有两个主要区别:

  1. 定位参考系统不同;Stack/Positioned定位的的参考系可以是父容器矩形的四个顶点;而Align则需要先通过alignment 参数来确定坐标原点,不同的alignment会对应不同原点,最终的偏移是需要通过alignment的转换公式来计算出。
  2. Stack可以有多个子元素,并且子元素可以堆叠,而Align只能有一个子元素,不存在堆叠。

Center

https://api.flutter.dev/flutter/widgets/Center-class.html
垂直居中组件。定义:

  1. Center({ Key key, double widthFactor, double heightFactor, Widget child })
  2. : super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child);

示例:
image.png

  1. Column(
  2. crossAxisAlignment: CrossAxisAlignment.start,
  3. children: [
  4. Container(
  5. color: Colors.green,
  6. child: Center(
  7. widthFactor: null, //为null时,组件的宽高将会占用尽可能多的空间
  8. heightFactor: null,
  9. child: Text('hello flutter'),
  10. ),
  11. ),
  12. SizedBox(height: 5.0),
  13. Container(
  14. width: 120.0,
  15. height: 120.0,
  16. color: Colors.green,
  17. child: Center(
  18. widthFactor: 3.0, //如果父组件限定了宽度,Center的宽高等于父组件的宽高
  19. heightFactor: 2.0,
  20. child: Text('hello flutter'),
  21. ),
  22. ),
  23. SizedBox(height: 5.0),
  24. Container(
  25. color: Colors.green,
  26. child: Center(
  27. //如果父组件没有限定宽度,那么父组件宽高就是其宽度的3.0倍,高度的2.0倍
  28. widthFactor: 3.0,
  29. heightFactor: 2.0,
  30. child: Text('hello flutter'),
  31. ),
  32. ),
  33. ],
  34. );

总结

本节重点介绍了Align组件及两种偏移类AlignmentFractionalOffset,读者需要理解这两种偏移类的区别及各自的坐标转化公式。另外,在此建议读者在需要制定一些精确的偏移时应优先使用FractionalOffset,因为它的坐标原点和布局系统相同,能更容易算出实际偏移。

在后面,我们又介绍了Align组件和Stack/PositionedCenter的关系,读者可以对比理解。
还有,熟悉Web开发的同学可能会发现Align组件的特性和Web开发中相对定位(position: relative)非常像,是的!在大多数时候,我们可以直接使用Align组件来实现Web中相对定位的效果,读者可以类比记忆。