Flutter有狀態(tài)組件StatefulWidget生命周期詳解
1、StatefulWidget的背后
flutter開發(fā)過程中,我們經(jīng)常會(huì)用到兩個(gè)組件StatelessWidget和StatefulWidget。前者為無狀組件,后者為有狀態(tài)組件,無狀態(tài)組件通常在創(chuàng)建后內(nèi)部的數(shù)據(jù)無法改變,而有狀態(tài)組件可以維持內(nèi)部的數(shù)據(jù)狀態(tài),適合動(dòng)態(tài)組件使用。
/// 無狀態(tài)組件的定義 class MyApp extends StatelessWidget {} /// 有狀態(tài)狀態(tài)組件的定義 class MyApp extends StatefullWidget { @override State<StatefulWidget> createState() => _MyApp(); } class _MyApp extends State<MyApp> {}
定義無狀態(tài)組件相對(duì)簡單,只需要繼承StatelessWidget即可,而有狀態(tài)組件需要兩個(gè)類來實(shí)現(xiàn),首先是繼承StatefullWidget,然后重寫createState方法,返回State的實(shí)現(xiàn)類。
剛始我不明白StatefullWidget為何要通過重寫createState的方式來實(shí)現(xiàn),后來通過對(duì)StatelessWidget的深入,我漸漸的理解了其中的用意。
首先,StatelessWidget和StatefulWidget都的父類來自Widget,而Widget在定義過程中使用了dart的注解@immutable。
@immutable的作用根據(jù)官方的解釋:被@immutable注解標(biāo)明的類或者子類都必須是不可變的。
也就是說,繼承了StatelessWidget和StatefullWidget的組件都必須為常量組件,可以使用const修飾,而它的構(gòu)造函數(shù)和成員屬性需要是常量,不可變的。
class MyApp extends StatelessWidget { /// 常量屬性,不加final會(huì)警告 final String? title; const MyApp({Key? key, this.title}) : super(key: key); } /// or class MyApp extends StatefulWidget { /// 常量屬性,不加final會(huì)警告 final String? title; const MyApp({Key? key, this.title}) : super(key: key); @override State<StatefulWidget> createState() => _MyApp(); } /// 調(diào)用時(shí)可以加const const MyApp()
StatelessWidget自然沒什么問題,本身它就是無狀態(tài)組件,創(chuàng)建出來內(nèi)部數(shù)據(jù)不會(huì)改變,符合@immutable的定義。而StatefullWidget不同,作為有狀態(tài)組件,需要維持自身的成員屬性可變,不能是一個(gè)常量。那如何解決呢?就是通過State來保持狀態(tài),因?yàn)樗⒉焕^承Widget,不受@immutable的影響,內(nèi)部成員可以定義成變量。
還有個(gè)問題,為什么Widget非要使用@immutable來注釋?
這一切的出發(fā)點(diǎn)都是為了減少性能的損耗,提高Widget構(gòu)建效率。因?yàn)槌A慷x的類,不會(huì)重復(fù)構(gòu)建,可以大大提升運(yùn)行速度,只要明白這一點(diǎn),就能解了StatefulWidget這樣設(shè)計(jì)的意義。
2、StatefulWidget的生命周期
StatefulWidget通過State來管理狀態(tài),同時(shí)也提供了相應(yīng)的生命周期函數(shù),如initState、didUpdateWidget、dispose等,我們只需要重寫這些函數(shù),StatefulWidget在執(zhí)行過程中會(huì)在合適的時(shí)機(jī)調(diào)用。
StatefulWidget的生命周期可以分為創(chuàng)建階段、更新階段和銷毀階段,下面我們結(jié)合一個(gè)示例來看下它的執(zhí)行過程。
/// StatefulWidget demo import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class ColorInheritedWidget extends InheritedWidget { final Color color; const ColorInheritedWidget({ Key? key, required this.color, required Widget child, }) : super(key: key, child: child); @override bool updateShouldNotify(covariant ColorInheritedWidget oldWidget) { return oldWidget.color != color; } static ColorInheritedWidget? of(BuildContext context) => context.dependOnInheritedWidgetOfExactType<ColorInheritedWidget>(); } class MyApp extends StatefulWidget { final String? title; const MyApp({Key? key, this.title}) : super(key: key); @override State<StatefulWidget> createState() { print('createState'); return _MyApp(); } } class _MyApp extends State<MyApp> { int value = 0; bool isDetach = false; Color color = Colors.red; @override void initState() { super.initState(); print('initState'); } @override void didChangeDependencies() { super.didChangeDependencies(); print('didChangeDependencies'); } @override void didUpdateWidget(covariant MyApp oldWidget) { super.didUpdateWidget(oldWidget); print('didUpdateWidget'); } @override void deactivate() { super.deactivate(); print('deactivate'); } @override void dispose() { super.dispose(); print('deactivate'); } @override Widget build(BuildContext context) { print('build'); return ColorInheritedWidget( color: color, child: MaterialApp( home: Scaffold( appBar: AppBar( title: const Text('StatefulWidget demo'), ), body: Column( children: [ if (!isDetach) MyAppChild(value: value), MaterialButton( color: Colors.blue, onPressed: () { value++; setState(() {}); }, child: const Text('更新value'), ), MaterialButton( color: Colors.blue, onPressed: () { color = color == Colors.red ? Colors.yellow : Colors.red; setState(() {}); }, child: const Text('更新color'), ), MaterialButton( color: Colors.blue, onPressed: () { isDetach = !isDetach; setState(() {}); }, child: Text(isDetach ? '添加' : '移除'), ) ], ), ), ), ); } } class MyAppChild extends StatefulWidget { final int value; const MyAppChild({Key? key, required this.value}) : super(key: key); @override State<StatefulWidget> createState() { print('child-createState'); return _MyAppChild(); } } class _MyAppChild extends State<MyAppChild> { @override void initState() { super.initState(); print('child-initState'); } @override void didChangeDependencies() { super.didChangeDependencies(); print('child-didChangeDependencies'); } @override void didUpdateWidget(covariant MyAppChild oldWidget) { super.didUpdateWidget(oldWidget); print('child-didUpdateWidget'); } @override void deactivate() { super.deactivate(); print('child-deactivate'); } @override void dispose() { super.dispose(); print('child-dispose'); } @override Widget build(BuildContext context) { print('child-build'); return Container( width: 100, height: 100, color: ColorInheritedWidget.of(context)?.color, child: Text('${widget.value}'), ); } }
2.1創(chuàng)建階段
運(yùn)行demo,首次渲染會(huì)打印出:
flutter: createState
flutter: initState
flutter: didChangeDependencies
flutter: build
flutter: child-createState
flutter: child-initState
flutter: child-didChangeDependencies
flutter: child-build
從這里我們可以看出,StatefulWidget組件執(zhí)行的順序createState -> initState -> didChangeDependencies -> build。
- createState
Widget只是對(duì)組件信息的描述,而Element才是最終對(duì)Widget的實(shí)現(xiàn)。通過Element可以進(jìn)行Widget的掛載和RenderObject的創(chuàng)建,最終實(shí)現(xiàn)UI的渲染。
flutter首幀渲染的過程中,會(huì)不斷向下遍歷Widget樹,通過cteateElememt創(chuàng)建Element實(shí)例,每創(chuàng)建一個(gè)Widget都會(huì)對(duì)應(yīng)創(chuàng)建一個(gè)Element。例如創(chuàng)建StatefulWidget時(shí)會(huì)對(duì)應(yīng)創(chuàng)建StatefulElement。
abstract class StatefulWidget extends Widget { const StatefulWidget({ Key? key }) : super(key: key); @override StatefulElement createElement() => StatefulElement(this); /// ... }
StatefulElement的構(gòu)造函數(shù)中,執(zhí)行了StatefulWidget的createState方法,完成對(duì)State的調(diào)用。
class StatefulElement extends ComponentElement { StatefulElement(StatefulWidget widget) : _state = widget.createState(), super(widget) { state._element = this; state._widget = widget; } /// ... }
- initState
createState通過StatefulElement構(gòu)造函數(shù)來創(chuàng)建,而initState在firstBuild方法中定義,firstBuild是首幀渲染的時(shí)候,通過StatefulElement實(shí)例的mount方法進(jìn)行調(diào)用,firstBuild只會(huì)執(zhí)行一次,也就是說initState只會(huì)在Widget首次創(chuàng)建次調(diào)用。
通常我們會(huì)使用initState進(jìn)行數(shù)據(jù)的初始化,也可以進(jìn)行網(wǎng)絡(luò)請(qǐng)求操作。
/// StatefulElement @override void _firstBuild() { try { /// 調(diào)用initState final Object? debugCheckForReturnedFuture = state.initState() as dynamic; } finally { /// ... } /// 調(diào)用didChangeDependencies state.didChangeDependencies(); /// 調(diào)用父級(jí)ComponentElement類的_firstBuild,再往上到Element的rebuild方法,最后觸發(fā)的是StatefulElement中的performRebuild方法。 super._firstBuild(); } @override void performRebuild() { /// _didChangeDependencies標(biāo)記Element的依賴節(jié)點(diǎn)發(fā)生改變,didChangeDependencies會(huì)再次調(diào)用。 if (_didChangeDependencies) { state.didChangeDependencies(); _didChangeDependencies = false; } /// 調(diào)用ComponentElement中的performRebuild,最終觸發(fā)StatefulElement的build方法 super.performRebuild(); } @override Widget build() => state.build(this);
2.2更新階段
在State類中調(diào)用了setState方法,會(huì)解發(fā)組件update流程,它會(huì)對(duì)比新舊Element,將修改過的組件標(biāo)記為臟元素。如果Widget依賴InheritedWidget的數(shù)據(jù)發(fā)現(xiàn)變化,會(huì)觸發(fā)didChangeDependencies函數(shù),接著會(huì)調(diào)用子組件的update方法,在StatefulElement的update方法中執(zhí)行了didUpdateWidget,最后才執(zhí)行rebuild進(jìn)行build的調(diào)用,完成UI的更新。
/// StatefulElement @protected @pragma('vm:prefer-inline') Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) { /// ... final Element newChild; if (child != null) { bool hasSameSuperclass = true; if (hasSameSuperclass && child.widget == newWidget) { newChild = child; } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) { /// 更新邏輯會(huì)調(diào)用child的update方法 child.update(newWidget); newChild = child; } } return newChild; }
/// StatefulElement @override void update(StatefulWidget newWidget) { super.update(newWidget); try { /// 調(diào)用state.didUpdateWidget final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic; } /// 重新rebuild深入遍歷子組件 rebuild(); }
在demo中,點(diǎn)擊“更新value”按鈕,會(huì)打印出:
flutter: build
flutter: child-didUpdateWidget
flutter: child-build
從這里可以看出父組件setState時(shí),會(huì)先走自身的build再觸發(fā)子組件的didUpdateWidget和build。
2.3銷毀階段
在demo中,點(diǎn)擊“移除”按鈕,會(huì)打印出:
flutter: build
flutter: child-deactivate
flutter: child-dispose
組件移除節(jié)點(diǎn)后會(huì)調(diào)用deactivate,如果該組件被移除節(jié)點(diǎn),然后未被 插入到其他節(jié)點(diǎn)時(shí),則會(huì)繼續(xù)調(diào)用 dispose 永久移除,并釋放組件資源。
總結(jié):
1、StatefulWidget通過State來管理狀態(tài)數(shù)據(jù),目的是為了保持StatefulWidget可常量創(chuàng)建,減少性能的損耗,提高Widget構(gòu)建效率。
2、StatefulWidget創(chuàng)建階段生命周期先執(zhí)行順序createState -> initState -> didChangeDependencies -> build??梢栽趇nitState進(jìn)行數(shù)據(jù)初始化、網(wǎng)絡(luò)請(qǐng)求操作。
3、StatefulWidget更新階段:build -> child didUpdateWidget -> child build。
4、StatefulWidget銷毀階段:build -> child deactivate -> child dispose。
以上就是Flutter有狀態(tài)組件StatefulWidget生命周期詳解的詳細(xì)內(nèi)容,更多關(guān)于Flutter StatefulWidget生命周期的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android實(shí)現(xiàn)志愿者系統(tǒng)詳細(xì)步驟與代碼
這篇文章主要介紹了Android實(shí)現(xiàn)志愿者系統(tǒng),本系統(tǒng)采用MVC架構(gòu)設(shè)計(jì),SQLite數(shù)據(jù)表有用戶表、成員表和活動(dòng)表,有十多個(gè)Activity頁面。打開應(yīng)用,進(jìn)入歡迎界面,3s后跳轉(zhuǎn)登錄界面,用戶先注冊賬號(hào),登錄成功后進(jìn)入主界面2023-02-02淺析Android中build.gradle的實(shí)用技巧
這篇文章主要介紹了淺析Android中build.gradle的實(shí)用技巧,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-03-03Android應(yīng)用開發(fā)中RecyclerView組件使用入門教程
這篇文章主要介紹了Android應(yīng)用開發(fā)中RecyclerView組件使用的入門教程,RecyclerView主要針對(duì)安卓5.0以上的material design開發(fā)提供支持,需要的朋友可以參考下2016-02-02Android框架學(xué)習(xí)之Volley和Glide詳解
這篇文章主要給大家介紹了關(guān)于Android框架學(xué)習(xí)之Volley和Glide的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05Android編程實(shí)現(xiàn)手機(jī)拍照的方法詳解
這篇文章主要介紹了Android編程實(shí)現(xiàn)手機(jī)拍照的方法,結(jié)合實(shí)例形式分析了Android實(shí)現(xiàn)手機(jī)拍照的操作步驟與具體細(xì)節(jié),需要的朋友可以參考下2016-11-11