【Flutter实战】对齐与相对定位(Align)

4.6 对齐与相对定位(Align)

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

4.6.1 Align

Align 组件可以调整子组件的位置,并且可以根据子组件的宽高来确定自身的的宽高,定义如下:

1Align({ 2 Key key, 3 this.alignment = Alignment.center, 4 this.widthFactor, 5 this.heightFactor, 6 Widget child, 7})
  • alignment : 需要一个AlignmentGeometry类型的值,表示子组件在父组件中的起始位置。AlignmentGeometry 是一个抽象类,它有两个常用的子类:AlignmentFractionalOffset,我们将在下面的示例中详细介绍。
  • widthFactorheightFactor是用于确定Align 组件本身宽高的属性;它们是两个缩放因子,会分别乘以子元素的宽、高,最终的结果就是Align 组件的宽高。如果值为null,则组件的宽高将会占用尽可能多的空间。

示例

我们先来看一个简单的例子:

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

FlutterLogo 是Flutter SDK提供的一个组件,内容就是Flutter的商标。在上面的例子中,我们显式指定了Container的宽、高都为120。如果我们不显式指定宽高,而通过同时指定widthFactorheightFactor 为2也是可以达到同样的效果:

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

因为FlutterLogo的宽高为60,则Align的最终宽高都为2*60=120

另外,我们通过Alignment.topRightFlutterLogo定位在Container的右上角。那Alignment.topRight是什么呢?通过源码我们可以看到其定义如下:

1//右上角 2static const Alignment topRight = Alignment(1.0, -1.0);

可以看到它只是Alignment的一个实例,下面我们介绍一下Alignment

Alignment

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

1Alignment(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类中都已经定义为了静态常量。

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

(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)。

FractionalOffset

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

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

下面看一个例子:

1Container( 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)

我们将FractionalOffset(0.2, 0.6)带入坐标转换公式得FlutterLogo实际偏移为(12,36),和实际运行效果吻合。

4.6.2 Align和Stack对比

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

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

4.6.3 Center组件

我们在前面章节的例子中已经使用过Center组件来居中子元素了,现在我们正式来介绍一下它。通过查找SDK源码,我们看到Center组件定义如下:

1class Center extends Align { 2 const Center({ Key key, double widthFactor, double heightFactor, Widget child }) 3 : super(key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: child); 4}

可以看到Center继承自Align,它比Align只少了一个alignment 参数;由于Align的构造函数中alignment 值为Alignment.center,所以,我们可以认为Center组件其实是对齐方式确定(Alignment.center)了的Align

上面我们讲过当widthFactorheightFactornull时组件的宽高将会占用尽可能多的空间,这一点需要特别注意,我们通过一个示例说明:

1...//省略无关代码 2DecoratedBox( 3 decoration: BoxDecoration(color: Colors.red), 4 child: Center( 5 child: Text("xxx"), 6 ), 7), 8DecoratedBox( 9 decoration: BoxDecoration(color: Colors.red), 10 child: Center( 11 widthFactor: 1, 12 heightFactor: 1, 13 child: Text("xxx"), 14 ), 15)

总结

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

在后面,我们又介绍了Align组件和Stack/PositionedCenter的关系,读者可以对比理解。

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

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

【Flutter实战】层叠布局(Stack、Positioned)

4.5层叠布局Stack、Positioned层叠布局和Web中的绝对定位、Android中的Frame布局是相似的,子组件可以根据距父容器四个角的位置来确定自身的位置。绝对定位允许子组件堆叠起来(按照代码中声明的顺序)。Flutter中使用Stack和Positioned这两个组件来配合实现绝对定位。Stack允许子组件堆叠,而Pos

IOS之地图和定位应用开发

11.1iOS定位服务iOS中有三个定位服务组件:  Wifi定位,通过查询一个Wifi路由器的地理位置的信息。比较省电,iPodtouch和iPad也可以采用。  蜂窝基站定位,通过移动运用商基站定位。也适合有3G版本的iPodtouch和iPad。  GPS卫星定位,通过34颗GPS定位位置

Python之time模块的时间戳、时间字符串格式化与转换

Python处理时间和时间戳的内置模块就有time,和datetime两个,本文先说time模块。关于时间戳的几个概念时间戳,根据1970年1月1日00:00:00开始按秒计算的偏移量。时间元组(struct_time),包含9个元素。 time.struct_time(tm_y