Flutter 中 Stack 的使用詳解(內含對比圖) | Flutter Widgets

這是我參與更文挑戰的第12天,活動詳情查看: 更文挑戰html

前言

前面幾篇介紹了ListViewGridViewWrapColumnRow 等這些在平面列表佈局,今天咱們要聊的是 Stack ,能夠叫他棧佈局也叫層級佈局,主要來顯示平面重疊佈局,能夠更加靈活的控制子項的位置。git

簡單使用

這裏咱們添加 3 個大小不一樣子項,看看他們是怎麼排列的。github

Stack(
  children: [
    getItem(3, width: 120, height: 120, color: Colors.purple),
    getItem(2, width: 80, height: 80, color: Colors.blue),
    getItem(1),
  ],
)
複製代碼

getItem

這裏除 index 外,其餘參數使用了選擇參數api

/// 獲取子項目(這裏使用了選擇參數)
Widget getItem(int index,
               {double? width = 60, double? height = 60, Color color = Colors.orange}) {
  return Container(
    // 寬高設置 60
    width: width,
    height: height,
    // 設置背景色
    color: color,
    // 設置間隙
    margin: EdgeInsets.all(2),
    // 設置子項居中
    alignment: Alignment.center,
    // 設置子項
    child: Text('$index'),
  );
}
複製代碼

看效果

image.png

alignment (對齊方式)

若是你看看源碼能夠看到這個參數的默認值是 AlignmentDirectional.topStart ,可是這裏咱們依然可使用咱們熟悉的 Alignment.topLeft 來進行參數設置,這是爲何呢?咱們看看來看看 AlignmentDirectional 源碼吧,Alignment 源碼在前面的章節咱們看過了markdown

Stack(
  // 居中對齊
  alignment: Alignment.topLeft,
  children: [
    getItem(3, width: 120, height: 120, color: Colors.purple),
    getItem(2, width: 80, height: 80, color: Colors.blue),
    getItem(1),
  ],
)
複製代碼

Alignment 和 AlignmentDirectional 源碼

// Alignment
class Alignment extends AlignmentGeometry {
  /// Creates an alignment.
  ///
  /// The [x] and [y] arguments must not be null.
  const Alignment(this.x, this.y)
    : assert(x != null),
      assert(y != null);
  static const Alignment topLeft = Alignment(-1.0, -1.0);
  static const Alignment center = Alignment(0.0, 0.0);
  ...
}

// AlignmentDirectional
class AlignmentDirectional extends AlignmentGeometry {
  const AlignmentDirectional(this.start, this.y)
    : assert(start != null),
      assert(y != null);
  static const AlignmentDirectional topStart = AlignmentDirectional(-1.0, -1.0);
  // 考慮使用 Alignment.center 代替
  static const AlignmentDirectional center = AlignmentDirectional(0.0, 0.0);
 	...
}
複製代碼

上面咱們貼出了核心源碼,能夠看出他倆都是繼承自 AlignmentGeometry ,而後實現也是差很少,並且源碼中的註釋也是推薦咱們使用 Alignment 代替oop

各類對齊方式效果

這裏咱們就使用 Alignment 了源碼分析

Alignment.topLeft Alignment.topCenter Alignment.topRight
image.png image.png image.png
Alignment.centerLeft Alignment.center Alignment.centerRight
image.png image.png image.png
Alignment.bottomLeft Alignment.bottomCenter Alignment.bottomRight
image.png image.png image.png

學點技巧

當咱們輸入 **Alignment.bottomRight** 這類參數時,由於單次比較長若是所有輸入就太麻煩了,咱們能夠輸入部分前綴+常量的首字母,以下佈局

  • Alignment.bottomRight => alibr

image.png

  • Alignment.center => alic

image.png

閱讀我得文章你會發現,不光有源碼分析、結構化梳理還有各類實用的技巧,記得關注我哦post

fit(填充方式)

爲了展現效果,咱們先添加一個背景 BgContainer (以前的篇章封裝的通用組件)ui

BgContainer(
  child: Stack(
    // 居中
    alignment: Alignment.center,
    // 設置默認值 loose
    fit: StackFit.loose,
    children: [
      getItem(3, width: 120, height: 120, color: Colors.purple),
      getItem(2, width: 80, height: 80, color: Colors.blue),
      getItem(1),
    ],
  ),
)
複製代碼

image.png

StackFit.loose (疏鬆的) - 默認

這裏爲了顯示效果咱們使用了 DevTools 中的 Widget Inspector 來調試佈局,以後可能會聊 Flutter DevTools 的使用技巧(這裏不作承諾,仍是可能回聊)
image.png
經過上圖能夠看到此時 Stack 的大小取決於子項中最大的(也就是紫色 120 寬高的子項)

StackFit.expand(展開的)

image.png
這裏彷佛不是很符合預期,理論上不該該是擴展 Stack 到最大便可嗎?

目前咱們的子項都是沒有設置定位的,因此此時全部的子類約束都會擴展到與Stack 最大值一致

添加定位

咱們此時作一點點改變看看效果,先看代碼

BgContainer(
  child: Stack(
    alignment: Alignment.center,
    fit: StackFit.expand,
    children: [
      getItem(3, width: 120, height: 120, color: Colors.purple),
      getItem(2, width: 80, height: 80, color: Colors.blue),
      // 這添加了定位
      Positioned(
        // 距左邊 10 
        left: 10,
        // 距上邊 10 
        top: 10,
        child: getItem(1),
      ),
    ],
  ),
)
複製代碼

image.png
此時看到效果了,1 黃色 子項遵循了本身的約束,由於他添加了定位,其餘 二、3 由於沒有添加定位因此和 Stack 同樣大,約束被傳遞。

這裏咱們暫時不講定位的使用,下篇咱們系統的聊

StackFit.passthrough(直穿的)

咱們先看看效果
image.png
這裏彷佛和 loose 沒有啥區別啊?咱們能夠看看源碼後改變成如下的示例代碼再看看

BgContainer(
  // 添加了一個橫向佈局
  child: Row(
    children: [
      // 添加了展開組件,前面講過能夠去專欄看看
      Expanded(
        child: Stack(
          alignment: Alignment.center,
          // 設置填充方式爲 passthrough
          fit: StackFit.passthrough,
          children: [
            getItem(3, width: 120, height: 120, color: Colors.purple),
            getItem(2, width: 80, height: 80, color: Colors.blue),
            Positioned(
              left: 10,
              top: 10,
              child: getItem(1),
            ),
          ],
        ),
      )
    ],
  ),
),
)
複製代碼

image.png
這裏咱們看到與 expand 還有些不一樣,這裏咱們只展開了寬度,高度仍是子項的高度。由於這裏 Stack 的寬度約束是展開的屏幕寬度,直接傳遞給了沒有添加定位的子組件,因此看到 二、3 子組件跟着變了,而 1 子組件的大小沒有發生變化。

若是是 loose 會怎樣?

image.png

對於非定位組件會繼續保持對齊方式和子項的約束

小總結

image.png

clipBehavior(剪裁行爲)

爲啥不聊 overflow 呢,由於本文是基於 Flutter 2.2.1 版本的,overflow 已經被棄用了,不少地方的剪裁行爲之後都統一爲 clipBehavior ,這參數咱們以前在剪裁篇《Flutter 中各類剪裁 Widget 的使用》聊過,建議回看

當咱們設置的子項定位後大小超過了 Stack 佈局的時候,咱們指望怎樣剪裁渲染呢?是剪裁仍是顯示,是帶抗鋸齒仍是不帶呢?

BgContainer(
  child: Stack(
    alignment: Alignment.center,
    fit: StackFit.passthrough,
    // 設置剪裁行爲默認 hardEdge
    clipBehavior: Clip.hardEdge,
    children: [
      getItem(3, width: 120, height: 120, color: Colors.purple),
      getItem(2, width: 80, height: 80, color: Colors.blue),
      // 這裏設置定位左上角 -20
      Positioned(
        left: -20,
        top: -20,
        child: getItem(1),
      ),
    ],
  ),
)
複製代碼

看效果

Clip.none(不剪裁) Clip.hardEdge antiAlias、antiAliasWithSaveLayer
image.png image.png image.png

通常是默認和不剪裁進行設置,其餘的能夠回看剪裁篇,介紹了不一樣。

到這裏 Stack 核心內容就聊完了,下篇咱們結合聊定位組件「Positioned、Align」聊聊,記得關注個人專欄哦

源碼倉庫

基於 Flutter 🔥 最新版本

參考連接

關注專欄

  • 此文章已收錄到下面👇 的專欄,能夠直接關注
  • 更多文章繼續閱讀|系列文章持續更新

👏 歡迎點贊➕收藏➕關注,有任何問題隨時在下面👇評論,我會第一時間回覆哦

相關文章
相關標籤/搜索