【Flutter 元件集錄】Visibility| 8月更文挑戰

張風捷特烈發表於2021-08-13
前言:

這是我參與8月更文挑戰的第 13 天,活動詳情檢視:8月更文挑戰。為應掘金的八月更文挑戰,我準備在本月挑選 31 個以前沒有介紹過的元件,進行全面分析和屬性介紹。這些文章將來會作為 Flutter 元件集錄 的重要素材。希望可以堅持下去,你的支援將是我最大的動力~

本系列元件文章列表
1.NotificationListener2.Dismissible3.Switch
4.Scrollbar5.ClipPath6.CupertinoActivityIndicator
7.Opacity8.FadeTransition9. AnimatedOpacity
10. FadeInImage11. Offstage12. TickerMode
13. Visibility[本文]

一、認識 Visibility 元件

前面介紹了 Offstage 元件可以控制 child顯隱,與它相比較的往往是 Visibility 元件。Offstage 原始碼中有對 Visibility 的一句介紹:它可以更高效地隱藏元件(儘管不那麼微妙)。

現在仔細想想,OpacityOffstageVisibility 都可以控制元件的顯示/隱藏 。那它們之間有什麼樣的區別和聯絡呢?通過本文,我將從 Visibility 原始碼中為你揭曉答案。


1.Visibility 基本資訊

下面是 Visibility 元件類的定義構造方法,可以看出它繼承自 StatelessWidget。例項化時必須傳入和 child 元件,另外還有一堆選填的 bool 屬性和一個 replacement 元件。


2.Visibility 的簡單使用

下面是 Visibility 元件的預設效果,中間的圖示是待顯隱元件,通過點選按鈕切換現有狀態。可以看出,直接使用 Visibility 時,元件隱藏時不會佔位

class OffstageDemo extends StatefulWidget {
  @override
  _OffstageDemoState createState() => _OffstageDemoState();
}

class _OffstageDemoState extends State<OffstageDemo> {
  bool _visible = true;

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        ElevatedButton(
          child: const Text('切換顯隱'),
          onPressed: _toggleVisible,
        ),
        Visibility(
          visible: _visible,
          child: buildChild(),
        ),
        Text('預設情況'),
      ],
    );
  }

  void _toggleVisible() {
    setState(() {
      _visible = !_visible;
    });
  }

  Widget buildChild() => const Padding(
        padding: const EdgeInsets.all(10),
        child: Icon(
          Icons.camera_outlined,
          color: Colors.green,
          size: 50,
        ),
      );
}
複製程式碼

3.Visibility 的佔位元件

Visibility 中有一個 replacement 的可選屬性,型別是 Widget ,用於佔位。

this.replacement = const SizedBox.shrink(),

final Widget replacement;
複製程式碼

預設 replacementSizedBox.shrink() ,即寬高為 0SizedBox

我們可以通過 Flutter Inspector 檢視樹的詳細資訊,可以看出在隱藏時。渲染樹中確實有通過 SizedBox 物件的渲染物件節點,且寬高為 0 。


下面是設定 replacement 的效果,可以看出在隱藏時,會顯示 replacement 對應的元件。

Visibility(
  visible: _visible,
  child: buildChild(),
  replacement: buildPlaceholder(),
),

Widget buildPlaceholder() => Container(
      width: 50,
      height: 50,
      padding: const EdgeInsets.all(10),
      child: Placeholder(),
    );
複製程式碼

4. 五個 maintainXXX

下面就剩下來五個 maintainXXX 的布林屬性。

我們先來看一下,構造中的斷言處理:

先看一下 maintainSize ,表示隱藏時是否保持尺寸。預設為 false 。通過斷言 2 可以看出:

maintainSize: true 時,必須 maintainAnimation: true ,否則斷言失敗。
複製程式碼

通過斷言 1 可以看出:

maintainAnimation: true 時,必須 maintainState: true ,否則斷言失敗。
複製程式碼

也就是說,如果想要保持尺寸,必須 maintainAnimationmaintainState 。如下是 maintainSize 的效果,這樣隱藏時,原尺寸區域不會消失。

Visibility(
  visible: _visible,
  child: buildChild(),
  maintainSize: true,
  maintainAnimation: true,
  maintainState: true,
),
複製程式碼

看一下結構,有一個透明度為 0Opacity 元件,看懂的應該能會心一笑。


maintainInteractivity 的作用也非常簡單,它可以控制在隱藏時是否響應事件。

另外 maintainAnimationmaintainState 作用是什麼,就讓原始碼來告訴你吧。


二、 Visibility 的原始碼實現

1. 尺寸和點選事件的保持

Visibility 元件作為一個 StatelessWidget,就決定它本身並不掌握"核心科技" ,只是在 build 方法中組合使用其他元件而已。在原始碼 build 方法中,可以看出:
如果 maintainSizetrue ,會通過 Opacity 元件進行包裹,根據 visible 控制透明度。 maintainInteractivityfalse , 會通過 IgnorePointer 元件進行包裹,忽略點選事件。


2. 保持狀態和動畫

如果 maintainStatetrue ,會通過 Offstage 元件進行包裹,根據 visible 控制是否顯示,對於 Offstage 元件介紹,詳見此文。如果 maintainAnimationfalse , 會通過 TickerMode 元件進行包裹,在隱藏時禁用子元件動畫。 TickerMode 元件介紹,詳見此文


3.為什麼說 Visibility 是高效的?

如果五個 maintainXXX 是預設情況,那麼 Visibility 元件直接通過 visible 屬性決定返回元件,不需要通過 OpacityOffstage元件完成顯隱功能。 另外,maintainStatemaintainAnimation 必須同時為 true ,這也能給使用者一個保障,避免使用 Offstage 是忘記子元件禁止動畫。

到這裡可以看出,Visibility 元件 可以說是顯隱功能的合集。能控制四個屬性:是否接受事件是否保持尺寸是否保持狀態是否停止動畫,分別由 IgnorePointerOpacityOffstageTickerMode 實現。是一個上層的簡單封裝,目的是方便使用者使用,儘可能少犯錯誤。所以如果想顯隱元件,又不知道用什麼好,Visibility 可以無腦翻牌。

Visibility 的使用方式到這裡就介紹完畢,那本文到這裡就結束了,謝謝觀看,明天見~

相關文章