前言:
這是我參與8月更文挑戰的第 11 天,活動詳情檢視:8月更文挑戰。為應掘金的八月更文挑戰
,我準備在本月挑選 31
個以前沒有介紹過的元件,進行全面分析和屬性介紹。這些文章將來會作為 Flutter 元件集錄
的重要素材。希望可以堅持下去,你的支援將是我最大的動力~
本系列 | 元件文章 | 列表 |
---|---|---|
1.NotificationListener | 2.Dismissible | 3.Switch |
4.Scrollbar | 5.ClipPath | 6.CupertinoActivityIndicator |
7.Opacity | 8.FadeTransition | 9. AnimatedOpacity |
10. FadeInImage | 11. Offstage[本文] |
一、認識 Offstage 元件
大家可能知道 Offstage
元件可以讓 child
元件顯示/隱藏
,但很少用它。畢竟想讓一個元件顯示/隱藏
,我們有其他的手段。比如通過 if
判斷,那 Offstage
元件的價值何在,為什麼要有這個元件,它有哪些特性?帶著這些問題,我們今天就來詳細分析一下 Offstage
元件。
1. Offstage 基本資訊
下面是 Offstage
元件類的定義
和 構造方法
,可以看出它繼承自 SingleChildRenderObjectWidget
。例項化時可以傳入布林型的 offstage
和 child
元件。
2. Offstage 的使用
Offstage
的使用非常簡單,只需給定offstage
的值,就能對 child
元件進行顯示或隱藏。其中 offstage:true
表示不在舞臺上,即隱藏。原始碼註釋中有個例項,我們就以此來認識 Offstage 的使用。
通過點選按鈕切換 _offstage
的狀態,來顯示或隱藏 buildChild
構建的圖示元件。可以看出一點:通過 Offstage
元件隱藏的子元件,不會在螢幕上佔據位置
。
class OffstageDemo extends StatefulWidget {
@override
_OffstageDemoState createState() => _OffstageDemoState();
}
class _OffstageDemoState extends State<OffstageDemo> {
bool _offstage = true;
@override
Widget build(BuildContext context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ElevatedButton(
child: const Text('切換顯隱'),
onPressed: () {
setState(() {
_offstage = !_offstage;
});
},
),
Offstage(
offstage: _offstage,
child: buildChild(),
),
Text('圖示是否隱藏: $_offstage'),
],
);
}
Widget buildChild() => const Padding(
padding: EdgeInsets.all(10),
child: Icon(
Icons.camera_outlined,
color: Colors.green,
size: 50,
),
);
}
複製程式碼
3.Offstage 的特點
上面一個案例就能說明 Offstage
的使用和作用,很多人也就到這裡淺嘗輒止
。但當我們檢視渲染樹時,可以發現被隱藏元件對應的渲染物件依舊在樹上。也就是說,它雖然不可見
,但它還在
。
這樣我們可以通過 GlobalKey
去獲取渲染物件,拿到被隱藏的元件大小。注意:這個操作並不是在說元件尺寸要通過 Offstage
來獲取,而是說 Offstage
可以獲取隱藏元件大小。任何元件都可以通過 GlobalKey
來拿到渲染物件獲取尺寸。這裡只是在說明被 Offstage
隱藏的元件,對應的渲染物件依舊在樹中。
final GlobalKey _key = GlobalKey();
Size _getSize() {
final RenderBox box = _key.currentContext!.findRenderObject()! as RenderBox;
return box.size;
}
Widget buildChild() => Padding(
key: _key, //<---
padding: const EdgeInsets.all(10),
child: const Icon(
Icons.camera_outlined,
color: Colors.green,
size: 50,
),
);
複製程式碼
由於這種特性,對於隱藏具有動畫效果
的元件要格外注意。拿 CupertinoActivityIndicator 舉例,它是通過 CustomPaint
元件進行繪製的,其中會維護一個不停運動
的動畫控制器,用於觸發畫板的重繪。動畫控制器會在 RenderCustomPaint
被監聽,觸發 markNeedsPaint
。
通過除錯可以發現,即使 CupertinoActivityIndicator 被隱藏,但動畫器仍會不斷執行,RenderCustomPaint
監聽動畫器改變,也會不斷觸發 markNeedsPaint
,這顯然是不友好的。所以我們需要在隱藏 CupertinoActivityIndicator
的同時,關掉動畫。那麼問題來了,CupertinoActivityIndicator
元件的動畫器是維護在元件狀態內部的,我們如何控制,這裡先按下不表,在後面的 TickerMode
元件一文中進行探討。
Widget buildChild() => Padding(
key: _key,
padding: const EdgeInsets.all(10),
child:CupertinoActivityIndicator(
radius: 20,
),
);
複製程式碼
二、 Offstage 的原始碼實現
1. Offstage 原始碼分析
它繼承自 SingleChildRenderObjectWidget
就說明,該元件需要維護一個 RenderObject
物件的建立及更新。
在 createRenderObject
方法中,建立 RenderOffstage
,offstage
作為構造入參。在 updateRenderObject
中,對 RenderOffstage
物件進行更新。也就是說,元件的顯示/隱藏是在 RenderAnimatedOpacity
中進行的。
2. RenderOffstage 原始碼
RenderOffstage
作為一個 RenderObject
,負責佈局與繪製。可以看到在原始碼的處理中,計算寬高時,當offstage
為 true
,會返回 0
,這也是為什麼在介面上不會顯示的原因。
在 performLayout
中,如果 offstage
為 true
,子渲染物件會執行 layout
。說明即使是隱藏,子節點也會進行佈局。
3. 點選和繪製
hitTest
用於點選處理,可以看出,如果 offstage
為 true
是不會響應點選的。paint
用於繪製,如果 offstage
為 true
直接返回,不會進行繪製。也就是說 offstage
為 true
,即使 CupertinoActivityIndicator
不停觸發 markNeedsPaint
,也不會執行子渲染物件的繪製操作。
Offstage
的使用方式到這裡就介紹完畢,那本文到這裡就結束了,謝謝觀看,明天見~