在上一節我們講過,通過Stack
和Positioned
來指定一個或多個子元素相對於父元素各個邊的精確偏移,並且可以重疊。但如果我們只想簡單的調整一個子元素在父元素中的位置的話,使用Align
元件會更簡單一些。
Align
Align
元件可以調整子元件的位置,並且可以根據子元件的寬高來確定自身的寬高,定義如下:
Align({
Key key,
this.alignment = Alignment.center,
this.widthFactor,
this.heightFactor,
Widget child,
})
複製程式碼
alignment
:需要一個AlignmentGeometry
型別的值,表示子元件在父元件中的起始位置。AlignmentGeometry
是一個抽象類,它有兩個常用的子類:Alignment
和FractionalOffset
,我們將在下面的示例中詳細介紹。widthFactor
和heightFactor
是用於確定Align
元件本身寬高的屬性;它們是兩個縮放因子,會分別乘以子元素的寬、高,最終的結果就是Align
元件的寬高。如果值為null
,則Align
元件的寬高將會佔用盡可能多的空間。
示例
我們先來看一個簡單的示例:
Container(
height: 120.0,
width: 120.0,
color: Colors.blue[50],
child: Align(
alignment: Alignment.topRight,
child: FlutterLogo(
size: 60,
),
),
)
複製程式碼
執行效果如下:
FlutterLogo
是Flutter SDK提供的一個元件,內容就是Flutter的商標。在上面的例子中,我們顯式指定了Container
的寬、高都為120。如果我們不顯式指定寬高,而通過同時指定widthFactor
和heightFactor
的值為2,也可以達到同樣的效果,程式碼如下:
Align(
widthFactor: 2,
heightFactor: 2,
alignment: Alignment.topRight,
child: FlutterLogo(
size: 60,
),
),
複製程式碼
因為FlutterLogo
的寬高為60,則Align
的最終寬高都為2*60=120
。
另外,我們通過Alignment.topRight
將FlutterLogo
定位在Container
的右上角。那Alignment.topRight
是什麼呢?通過查詢SDK原始碼我們可以看到其定義如下:
//右上角
static const Alignment topRight = Alignment(1.0, -1.0);
複製程式碼
可以看到它只是Alignment
的一個例項,下面我們介紹一下Alignment
。
Aligment
Alignment
繼承自AlignmentGeometry
,表示矩形內的一個點,它有兩個屬性x
,y
,分別表示在水平和垂直方向的偏移,Alignment
定義如下:
Alignment(this.x, this.y)
複製程式碼
Alignment
元件會以矩形的中心點作為座標原點,即Alignment(0.0, 0.0)
。x
的值從-1到1表示矩形左邊到右邊的距離,y
的值從-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)
,下面我們再看一個例子:
Align(
widthFactor: 2,
heightFactor: 2,
alignment: Alignment(2.0, 0.0),
child: FlutterLogo(
size: 60,
),
)
複製程式碼
現在我們再看看上面的示例,將Alignment(2.0, 0.0)
帶入上述座標轉換公式中,可以得到FlutterLogo
的實際偏移座標為(90, 30)
,實際執行效果如下:
FractionalOffset
FractionalOffset
繼承自Alignment
,它和Alignment
唯一的區別就是座標原點不同!FractionalOffset
的座標原點為矩形的左側頂點,這和佈局系統的一致,所以理解起來會比較容易。FractionalOffset
的座標轉換公式為:
(FractionalOffset.x * childWidth, FractionalOffset.y * childHeight)
複製程式碼
示例:
Container(
height: 120.0,
width: 120.0,
color: Colors.blue[50],
child: Align(
alignment: FractionalOffset(0.2, 0.6),
child: FlutterLogo(
size: 60,
),
),
)
複製程式碼
實際執行效果如下:
我們將FractionalOffset(0.2, 0.6)
帶入座標轉換公式中,可以得到FlutterLogo
實際偏移為(12, 36)
和實際執行效果吻合。
Align和Stack對比
現在我們都知道,Align
和Stack/Positioned
都可以用於指定子元素相對於父元素的偏移,但它們還是有兩個主要的區別:
- 定位參考系統不同;
Stack/Positioned
定位的參考系可以是父容器矩形的四個頂點;而Align
則需要先通過alignment
引數來確定座標原點,不同的alignment
(如Alignment
或FractionalOffset
)會對應不同原點,最終的偏移是需要通過alignment
的轉換公式來計算出來的。 Stack
可以有多個子元素,並且子元素可以堆疊,而Align
只能有一個子元素,不存在堆疊。
Align元件與Center元件的關係
我們在前面章節的例子中已經使用過Center
元件來居中子元素了,現在我們正式來介紹一下它。通過查詢SDK原始碼,我們看到Center
元件定義如下:
class Center extends Align {
const Center({
Key key,
double widthFactor,
double heightFactor,
Widget child
}): super(
key: key,
widthFactor: widthFactor,
heightFactor: heightFactor,
child: child
);
}
複製程式碼
可以看到Center
繼承自Align
,它比Align
只少了一個alignment
引數;由於Align
的建構函式中alignment
值為Alignment.center
,所以,我們可以認為Center
元件其實是對齊方式確定為Alignment.center
的Align
。
上面我們講過當widthFactor
或heightFactor
為null
時Align
元件的寬高將會佔用盡可能多的空間,這一點需要特別注意,下面我們通過一個示例來說明:
...//省略無關程式碼
DecoratedBox(
decoration: BoxDecoration(color: Colors.red),
child: Center(
child: Text("xxx"),
),
),
DecoratedBox(
decoration: BoxDecoration(color: Colors.red),
child: Center(
widthFactor: 1,
heightFactor: 1,
child: Text("xxx"),
),
)
複製程式碼
執行效果如下:
總結
本節重點介紹了Align
元件及兩種偏移類Alignment
和FractionalOffset
,讀者需要理解這兩種偏移類的區別及各自的座標轉化公式。另外,建議讀者在需要制定一些精確的偏移時應優先使用FractionalOffset
,因為它的座標原點和佈局系統相同,能更容易算出實際偏移。
緊接著,我們又介紹了Align
元件和Stack/Positioned
元件的對比,以及與Center
的關係,讀者可以對比理解。
最後,熟悉Web開發的同學可能會發現Align
元件的特性和Web開發中相對定位(position: relative)
非常相似。是的!在大多數時候,我們可以直接使用Align
元件來實現Web中相對定位的效果,讀者可以類比記憶。