Flutter 不顯示任何內容,返回哪個Widget 合適?

oldbirds發表於2021-03-22

當您不想顯示任何內容時,我們通常做法是返回Container 或者SizedBox,但是這是最佳方案麼?

return Builder(
  builder: (_) {
    if (condition) {
      return const MyWidget();
    } else {
      return const Container();
      // return const SizedBox();
    }
  },
);
複製程式碼

我們都知道 Flutter 的四棵樹:Widget 樹,Element 樹, RenderObject 樹和 Layer 樹。

  1. 當應用啟動時,會遍歷並建立所有的 Widget 形成 Widget Tree。
  2. 同時與 Widget Tree 相對應,通過呼叫 Widget 上的 createElement() 方法建立每個 Element 物件,形成 Element Tree。
  3. 當每個 Element 呼叫 createRenderObject() 時,將建立對應渲染物件,形成一個 Render Tree。
  4. Layer 的組成由 RenderObject 中的 isRepaintBoundary 標誌位決定,當 isRepaintBoundary 為 true 時,那麼該區域就是一個可更新繪製區域,而當這個區域形成時,其實就會新建立一個 Layer,而由 Layer 構成的 Layer Tree 最後會被提交到 Flutter Engine 繪製出畫面。

RenderObject 它才是真正幹活(layout、paint)。所以如果我們想要不顯示任何內容,那麼返回的 Widget 應該不需要在螢幕繪製出來。也就是說當你返回 Container 或者 SizedBox 也不是最佳方案,因為它們也會建立對應的 RenderObject,RenderObject 位於渲染樹中,雖然在螢幕上不顯示任何內容,也對其進行一些計算。

如果有個 Widget 不建立 RenderObject 就好了。

很高興的告訴你,還真有個 Widget 適合這種情況,它不是官方提供的。這個 Widget 就是 nil

nil 的實現非常簡單:

import 'package:flutter/widgets.dart';

/// 一個 [Nil] 例項, 可以用在你的佈局中.
const nil = Nil();

/// 這個 Widget 不做任何事情.
/// 當你要返回個 Widget 但又不能返回 null 的時候,非常適合.
class Nil extends Widget {
  /// Creates a [Nil] widget.
  const Nil({Key? key}) : super(key: key);

  @override
  Element createElement() => _NilElement(this);
}

class _NilElement extends Element {
  _NilElement(Nil widget) : super(widget);

  @override
  bool get debugDoingBuild => false;

  @override
  void performRebuild() {}
}
複製程式碼

Nil 自定義了 _NilElement,在 _NilElement 我們沒有看到 RenderObject 的建立。

那麼如何使用?

nil: ^1.0.1
複製程式碼

我們驗證下是有 RenderObject 建立

class MyPage extends StatelessWidget {
  final bool showText;

  MyPage({this.showText});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Text('hello, oldbirds'),
          showText ? Text('Container') : Container(),
          showText ? Text('nil') : Nil(),
        ],
      ),
    );
  }
}
複製程式碼

我們可以非常清晰的觀察到 Container 還是比較複雜的,也看到了 RenderObject 的建立。與之對比,Nil 就非常的乾淨,啥都沒有。

從四棵樹的理論來說,Nil 在這裡比會比 Container、SizedBox 等更適合。

注意

This widget is not intended to be used in widgets accepting multiple children (e.g. Rows, Columns, etc.). The best option is to not add a widget in the list if you don't want it to be displayed. Moreover using a Nil widget in such a case, can have unexpected results.

所以在 Columns 的地方不建議使用,本例子只是為了更好的對比才這樣寫的。更好的做法:

class MyPage extends StatelessWidget {
  final bool showText;

  MyPage({this.showText});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Text('hello, oldbirds'),
          if(showText) Text('Container'),
          if(showText) Text('nil'),
        ],
      ),
    );
  }
}
複製程式碼

如果想加入Flutter微信交流群,請關注官方微信公眾號 OldBirds

相關文章