Flutter 生命週期及渲染原理
這是我參與11月更文挑戰的第12天,活動詳情檢視:2021最後一次更文挑戰
Widget 生命週期
生命週期的基本概念
什麼是生命週期
我們使用一個物件的時候,有時會需要知道物件的一個狀態,什麼時候被建立,什麼時候被銷燬。我們常用的生命週期方法其實本質上就是回撥函式,是 Flutter
封裝好的,在 Widget
的不同狀態設定對應的回撥方法給外部使用。
生命週期的作用
- 初始化資料
- 建立變數、常量
- 傳送網路請求
- 監聽小部件的事件
- 管理記憶體
- 銷燬資料、銷燬監聽者、定時器等
Widget 常見的生命週期函式
Widget
生命週期函式我們可以分為兩種型別來看,無狀態與有狀態型別。
無狀態 Widget (StatelessWidget)
當無狀態 Widget
比渲染的時候會依次呼叫建構函式與 build
函式。
有狀態 Widget (StatefulWidget)
``` class MyHomePage extends StatefulWidget { final String? title; MyHomePage({this.title}) { print('Widget 建構函式被呼叫了'); } @override _MyHomePageState createState() { print('createState 函式被呼叫了'); return _MyHomePageState(); } }
class _MyHomePageState extends State
@override void dispose() { super.dispose(); print('State 的 dispose 函式被呼叫了'); } } ```
有狀態的 Widget
被渲染的時候會依次呼叫 StatefulWidget
的建構函式、createState
函式、State
的構造函、initState
函式、build
函式。當熱過載的時候會呼叫 StatefulWidget
的建構函式跟 State
的 build
函式。當呼叫 setState
方法的時候會呼叫 build
函式。
通過原始碼可以看到 setState
其實就是 _element
呼叫了 markNeedsBuild
函式,只是在此之前做了一些錯誤的判斷,這裡 _element
就是 context
。
資料共享部件 InheritedWidget
``` class MyData extends InheritedWidget { final int data; //需要在子元件中共享的資料
//構造方法 const MyData({required this.data, required Widget child}) : super(child: child);
//定義一個便捷方法,方便子元件中的 widget 獲取共享資料 static MyData? ofContext(BuildContext context) { return context.dependOnInheritedWidgetOfExactType(); }
//該回調決定當前 data 發生變化的時候,是否通知子元件(依賴 data 的子元件) @override bool updateShouldNotify(covariant InheritedWidget oldWidget) { print('呼叫了 updateShouldNotify 函式'); //如果返回 true,子部件中依賴共享資料的 Widget(build 函式中是否使用共享資料) 的 didChangeDependencies 方法會被呼叫 return (oldWidget as MyData).data != data; } }
class InheritedDemo extends StatefulWidget { const InheritedDemo({Key? key}) : super(key: key);
@override _InheritedDemoState createState() => _InheritedDemoState(); }
class _InheritedDemoState extends State
@override Widget build(BuildContext context) { return MyData(data: _count, child: Column( children: [ TextDemo(count: _count), ElevatedButton(onPressed: () { _count++; setState(() {}); }, child: const Icon(Icons.add)), ], )); } }
class TextDemo extends StatefulWidget { final int? count; TextDemo({this.count}); @override _TextDemoState createState() => _TextDemoState(); }
class _TextDemoState extends State
在我們開發的過程中一定會遇到這種場景,子部件的資料需要依賴父部件的資料,當層級比較多的話層層傳遞的方式就會比較麻煩,所以 Flutter
提供一個 InheritedWidget
部件,用來解決這種場景。如上案例中,我們定義了一個負責資料共享的類 MyData
繼承於 InheritedWidget
,MyData
的構造方法中有兩個引數,data
代表需要共享的資料,child
表示依賴於資料共享的 Widget
。並且我們提供了一個 ofContext
方法,供外界獲取資料時候使用。使用的話,我們在 _InheritedDemoState
的 build
方法中初始化 MyData
,在需要獲取共享資料的子部件中通過 MyData.ofContext(context)
來獲取資料,這裡需要注意的是,子元件需要通過 InheritedWidget
來獲取共享資料的話,其根元件必須是繼承於 InheritedWidget
類的 MyData
。當我們執行 _count++
的時候會呼叫 updateShouldNotify
方法,在這裡我們可以通過返回值來判斷是否呼叫子元件的 didChangeDependencies
方法,類似於傳送通知,返回值為 true
的時候就會呼叫,反之就不呼叫,我們可以根據需求在 didChangeDependencies
方法中做一些事情。
Flutter 渲染原理
abstract class Widget extends DiagnosticableTree {
Element createElement();
}
以上只附上了關鍵程式碼,通過原始碼我們可以看到 Widget
類都會實現 createElement
函式。這裡我們對於 Widget
的子類 StatelessWidget
跟 StatefulWidget
的渲染流程分開來看。
StatelessWidget 渲染流程
abstract class StatelessWidget extends Widget {
StatelessElement createElement() => StatelessElement(this);
}
StatelessWidget
中 createElement
方法會建立一個 StatelessElement
物件並加入到 Elment
樹中,並且返回 StatelessElement
物件,建立 StatelessElement
物件的時候 StatelessWidget
自己作為引數傳給 StatelessElement
物件。
class StatelessElement extends ComponentElement {
Widget build() => widget.build(this);
}
abstract class ComponentElement extends Element {
void mount(Element? parent, Object? newSlot) {
super.mount(parent, newSlot);
assert(_child == null);
assert(_lifecycleState == _ElementLifecycle.active);
_firstBuild();
assert(_child != null);
}
abstract class Element extends DiagnosticableTree implements BuildContext {
Element(Widget widget)
: assert(widget != null),
_widget = widget;
通過原始碼追蹤可以看到 StatelessElement
繼承於 ComponentElement
,ComponentElement
繼承於 Element
,在 Element
的構造方法中外部傳入的 widget
會被賦值給 _widget
屬性,在 ComponentElement
中會呼叫 mount
方法, mount
方法中的 _firstBuild
最終會執行 ComponentElement
的 build
方法,並且 StatelessElement
的 build
會執行 widget.build(this)
,並把自己傳給外面。
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {}
}
所以我們外部 StatelessWidget
子類中的 context
就是 Element
物件。
StatefulWidget 渲染流程
abstract class StatefulWidget extends Widget {
@override
StatefulElement createElement() => StatefulElement(this);
}
StatefulWidget
同樣會執行 createElement
,但是返回的物件是 StatefulElement
型別。
``` class StatefulElement extends ComponentElement { StatefulElement(StatefulWidget widget) : _state = widget.createState(), super(widget) { state._element = this; state._widget = widget; }
@override Widget build() => state.build(this); } ```
但是 StatefulElement
多了一步就是執行 createState
函式,並且賦值給 _state
屬性,並且把外部傳入的 widget
賦值給 state._widget
屬性,把 this
指標賦值給 state._element
。這也是我們能在 state
中能獲取到 widget
的原因。這裡 build
方法中執行的是 state.build(this)
。
- Swift - LeetCode - 學生出勤記錄 I
- Swift - LeetCode - 字串中的第一個唯一字元
- Swift - LeetCode - 猜數字大小
- Swift - LeetCode - 翻轉二叉樹
- Swift - LeetCode - 二叉樹的後序遍歷
- Swift - LeetCode - 二叉樹的前序遍歷
- Swift String、Moya 原始碼解析及高階函式
- Flutter 中 key 的原理及作用
- Flutter 生命週期及渲染原理
- Flutter 仿寫微信搜尋頁
- Flutter 網路請求類封裝及搜尋框實現
- Flutter 佈局聊天列表頁及網路資料處理
- Flutter 通訊錄索引條完善及聊天資料配置
- Flutter 仿寫微信通訊錄頁面
- Flutter 仿寫微信發現、我的頁面
- Flutter 專案搭建及工程配置
- 常用 Widget 部件介紹及 Flutter 佈局方式
- Flutter 之 Widget 部件體驗
- Dart 基礎語法
- 記憶體管理-弱引用分析