[toc]
對於現代APP的應用來說,為了更加美觀,通常會需要用到不同影象的堆疊效果,比如在一個APP使用者背景頭像上面新增一個按鈕,表示可以修改使用者資訊等。
要實現這樣的效果,我們需要在一個Image上面堆疊其他的widget物件,flutter為我們提供了這樣的一個非常方便的layout元件叫做Stack,今天和大家一起來聊一聊Stack的使用。
我們先來看下Stack的定義:
class Stack extends MultiChildRenderObjectWidget
Stack繼承自MultiChildRenderObjectWidget,表示在stack中可以render多個child widget物件。
因為Stack中的child是重疊關係,所以需要對child進行定位,根據定位的不同Stack中的child可以分為兩種型別,分別是positioned和non-positioned。
所謂positioned,是指child widget被包裝在Positioned中。什麼是Positioned呢?
Positioned是專門用來定位Stack中的child位置的一個widget。所以Positioned必須用在Stack中,並且Positioned和Stack的路徑之間只能存在StatelessWidget或者StatefulWidget這兩種widget。
如果一個物件被包含在Positioned中,那麼這個物件就是一個Stack中的positioned物件。
Positioned中除了封裝的child之外,還有6個屬性,如下所示:
const Positioned({
Key? key,
this.left,
this.top,
this.right,
this.bottom,
this.width,
this.height,
required Widget child,
})
這六個屬性分別是left,top,right,bottom,width和height。其中left,top,right,bottom分別表示到左,頂,右,底的距離,這個距離是相對stack來說的。而width和height則表示的是Positioned的寬度和高度。
事實上,使用left和right可以定義出width,使用top和bottom可以定義出height。
如果在一個軸方向的三個值都不存在,那麼會使用Stack.alignment來定位子元素。
如果六個值都不存在,那麼這個child就是一個non-positioned的child。
對於non-positioned的child,是通過Stack的alignment來進行佈局的,預設情況下是按top left corners進行佈局的。
我們接下來看一下Stack中有哪些屬性,下面是Stack的建構函式:
Stack({
Key? key,
this.alignment = AlignmentDirectional.topStart,
this.textDirection,
this.fit = StackFit.loose,
@Deprecated(
'Use clipBehavior instead. See the migration guide in flutter.dev/go/clip-behavior. '
'This feature was deprecated after v1.22.0-12.0.pre.',
)
this.overflow = Overflow.clip,
this.clipBehavior = Clip.hardEdge,
List<Widget> children = const <Widget>[],
})
可以看到Stack中有alignment,textDirection,fit,overflow和clipBehavior這幾個屬性。
首先來看alignment,這裡的alignment是一個AlignmentGeometry物件,主要用來佈局non-positioned children。
AlignmentGeometry中有兩個需要設定的屬性,分別是start和y。
start表示的是橫線定位範圍,它的取值比較奇怪,-1表示的是start side的邊緣位置,而1表示的是end side的邊緣位置。如果取值超過了這個範圍,則表示對應的位置超過了邊緣位置。
start的位置跟TextDirection是相關聯的,如果TextDirection的值是ltr,也就是說從左到右排列,那麼start就在最左邊,如果TextDirection的值是rtl,也就是說從右到左排列,那麼start就是在最右邊。
有橫向位置就有縱向位置,這個縱向位置用y來表示,它的正常取值範圍也是-1到1,當然你也可以超出這個範圍。
為了使用者更加方便的使用AlignmentGeometry,AlignmentGeometry提供了一些便捷的方法,如topStart,topCenter,topEnd等,大家可以自行選取。
接下來的屬性是textDirection,textDirection是一個TextDirection物件,它有兩個值,分別是rtl和ltr,在講解alignment的時候,我們已經提到過textDirection,它會影響alignment中橫向的佈局。
接下來是StackFit型別的fit屬性,StackFit有三個值,分別是loose,expand和passthrough。
loose表示的是一個鬆散結構,比如Stack規定的size是300x500,那麼它的child的寬度可以從0-300,child的高度可以從0-500.
expand表示是一個擴充的效果,比如Stack規定的size是300x500,那麼它的child的寬度就是300,child的高度就是500.
passthrough表示傳遞給stack的限制會原封不動的傳遞給他的child,不會進行任何修改。
overflow表示children超出展示部分是否會被剪下。不過這個屬性已經是Deprecated,flutter推薦我們使用clipBehavior這個屬性來代替。
clipBehavior是一個Clip物件,它的預設值是Clip.hardEdge。其他的幾個值還有none,hardEdge,antiAlias和antiAliasWithSaveLayer。
none表示不進行任何裁剪,hardEdge的裁剪速度最快,但是精確度不高。antiAlias速度比hardEdge慢一點,但是有光滑的邊緣。antiAliasWithSaveLayer是最慢的,應該很少被使用。
有了上面的講解,接下來我們看一下Stack的具體使用。
在我們這個例子中,我們在Stack中設定一個背景圖片,然後在圖片上疊加一個文字。
那麼應該怎麼實現呢?
首先我們需要設定Stack的alignment方式,我們希望文字和圖片的中心重合,也就是說把文字放在圖片中間,我們將Stack的alignment設定為Alignment.center。
接下來是一個背景圖片,因為原始圖片是一個正方形的圖片,我們需要對圖片進行裁剪成圓形,這裡使用一個非常方便的類CircleAvatar來建立圓形的圖示:
const CircleAvatar(
backgroundImage: AssetImage('images/head.jpg'),
radius: 100,
),
上面的程式碼能夠建立一個半徑是100的圓。
然後是文字的建立,可以給Text設定文字內容和對應的style:
Text(
'編輯',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
)
然後把Text封裝在Container中,並使用BoxDecoration給他指定一個背景:
Container(
decoration: const BoxDecoration(
color: Colors.green,
),
child: const Text(
...
最後將上面的程式碼組合起來就是我們最後的Stack:
Widget build(BuildContext context) {
return Stack(
alignment: Alignment.center,
children: [
const CircleAvatar(
backgroundImage: AssetImage('images/head.jpg'),
radius: 100,
),
Container(
decoration: const BoxDecoration(
color: Colors.green,
),
child: const Text(
'編輯',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
],
);
執行生成的介面如下:
以上就是Stack的使用,通過堆疊元件,我們可以實現很多炫酷的功能。
本文的例子:https://github.com/ddean2009/learn-flutter.git
更多內容請參考 http://www.flydean.com/11-flutter-ui-layout-stack/
最通俗的解讀,最深刻的乾貨,最簡潔的教學,眾多你不知道的小技巧等你來發現!
歡迎關注我的公眾號:「程式那些事」,懂技術,更懂你!