網站首頁 編程語言 正文
1、StatefulWidget的背后
flutter開發過程中,我們經常會用到兩個組件StatelessWidget和StatefulWidget。前者為無狀組件,后者為有狀態組件,無狀態組件通常在創建后內部的數據無法改變,而有狀態組件可以維持內部的數據狀態,適合動態組件使用。
/// 無狀態組件的定義
class MyApp extends StatelessWidget {}
/// 有狀態狀態組件的定義
class MyApp extends StatefullWidget {
@override
State<StatefulWidget> createState() => _MyApp();
}
class _MyApp extends State<MyApp> {}
定義無狀態組件相對簡單,只需要繼承StatelessWidget即可,而有狀態組件需要兩個類來實現,首先是繼承StatefullWidget,然后重寫createState方法,返回State的實現類。
剛始我不明白StatefullWidget為何要通過重寫createState的方式來實現,后來通過對StatelessWidget的深入,我漸漸的理解了其中的用意。
首先,StatelessWidget和StatefulWidget都的父類來自Widget,而Widget在定義過程中使用了dart的注解@immutable。
@immutable的作用根據官方的解釋:被@immutable注解標明的類或者子類都必須是不可變的。
也就是說,繼承了StatelessWidget和StatefullWidget的組件都必須為常量組件,可以使用const修飾,而它的構造函數和成員屬性需要是常量,不可變的。
class MyApp extends StatelessWidget {
/// 常量屬性,不加final會警告
final String? title;
const MyApp({Key? key, this.title}) : super(key: key);
}
/// or
class MyApp extends StatefulWidget {
/// 常量屬性,不加final會警告
final String? title;
const MyApp({Key? key, this.title}) : super(key: key);
@override
State<StatefulWidget> createState() => _MyApp();
}
/// 調用時可以加const
const MyApp()
StatelessWidget自然沒什么問題,本身它就是無狀態組件,創建出來內部數據不會改變,符合@immutable的定義。而StatefullWidget不同,作為有狀態組件,需要維持自身的成員屬性可變,不能是一個常量。那如何解決呢?就是通過State來保持狀態,因為它并不繼承Widget,不受@immutable的影響,內部成員可以定義成變量。
還有個問題,為什么Widget非要使用@immutable來注釋?
這一切的出發點都是為了減少性能的損耗,提高Widget構建效率。因為常量定義的類,不會重復構建,可以大大提升運行速度,只要明白這一點,就能解了StatefulWidget這樣設計的意義。
2、StatefulWidget的生命周期
StatefulWidget通過State來管理狀態,同時也提供了相應的生命周期函數,如initState、didUpdateWidget、dispose等,我們只需要重寫這些函數,StatefulWidget在執行過程中會在合適的時機調用。
StatefulWidget的生命周期可以分為創建階段、更新階段和銷毀階段,下面我們結合一個示例來看下它的執行過程。
/// 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創建階段
運行demo,首次渲染會打印出:
flutter: createState
flutter: initState
flutter: didChangeDependencies
flutter: build
flutter: child-createState
flutter: child-initState
flutter: child-didChangeDependencies
flutter: child-build
從這里我們可以看出,StatefulWidget組件執行的順序createState -> initState -> didChangeDependencies -> build。
- createState
Widget只是對組件信息的描述,而Element才是最終對Widget的實現。通過Element可以進行Widget的掛載和RenderObject的創建,最終實現UI的渲染。
flutter首幀渲染的過程中,會不斷向下遍歷Widget樹,通過cteateElememt創建Element實例,每創建一個Widget都會對應創建一個Element。例如創建StatefulWidget時會對應創建StatefulElement。
abstract class StatefulWidget extends Widget {
const StatefulWidget({ Key? key }) : super(key: key);
@override
StatefulElement createElement() => StatefulElement(this);
/// ...
}
StatefulElement的構造函數中,執行了StatefulWidget的createState方法,完成對State的調用。
class StatefulElement extends ComponentElement {
StatefulElement(StatefulWidget widget)
: _state = widget.createState(),
super(widget) {
state._element = this;
state._widget = widget;
}
/// ...
}
- initState
createState通過StatefulElement構造函數來創建,而initState在firstBuild方法中定義,firstBuild是首幀渲染的時候,通過StatefulElement實例的mount方法進行調用,firstBuild只會執行一次,也就是說initState只會在Widget首次創建次調用。
通常我們會使用initState進行數據的初始化,也可以進行網絡請求操作。
/// StatefulElement
@override
void _firstBuild() {
try {
/// 調用initState
final Object? debugCheckForReturnedFuture = state.initState() as dynamic;
} finally {
/// ...
}
/// 調用didChangeDependencies
state.didChangeDependencies();
/// 調用父級ComponentElement類的_firstBuild,再往上到Element的rebuild方法,最后觸發的是StatefulElement中的performRebuild方法。
super._firstBuild();
}
@override
void performRebuild() {
/// _didChangeDependencies標記Element的依賴節點發生改變,didChangeDependencies會再次調用。
if (_didChangeDependencies) {
state.didChangeDependencies();
_didChangeDependencies = false;
}
/// 調用ComponentElement中的performRebuild,最終觸發StatefulElement的build方法
super.performRebuild();
}
@override
Widget build() => state.build(this);
2.2更新階段
在State類中調用了setState方法,會解發組件update流程,它會對比新舊Element,將修改過的組件標記為臟元素。如果Widget依賴InheritedWidget的數據發現變化,會觸發didChangeDependencies函數,接著會調用子組件的update方法,在StatefulElement的update方法中執行了didUpdateWidget,最后才執行rebuild進行build的調用,完成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)) {
/// 更新邏輯會調用child的update方法
child.update(newWidget);
newChild = child;
}
}
return newChild;
}
/// StatefulElement
@override
void update(StatefulWidget newWidget) {
super.update(newWidget);
try {
/// 調用state.didUpdateWidget
final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic;
}
/// 重新rebuild深入遍歷子組件
rebuild();
}
在demo中,點擊“更新value”按鈕,會打印出:
flutter: build
flutter: child-didUpdateWidget
flutter: child-build
從這里可以看出父組件setState時,會先走自身的build再觸發子組件的didUpdateWidget和build。
2.3銷毀階段
在demo中,點擊“移除”按鈕,會打印出:
flutter: build
flutter: child-deactivate
flutter: child-dispose
組件移除節點后會調用deactivate,如果該組件被移除節點,然后未被 插入到其他節點時,則會繼續調用 dispose 永久移除,并釋放組件資源。
總結:
1、StatefulWidget通過State來管理狀態數據,目的是為了保持StatefulWidget可常量創建,減少性能的損耗,提高Widget構建效率。
2、StatefulWidget創建階段生命周期先執行順序createState -> initState -> didChangeDependencies -> build。可以在initState進行數據初始化、網絡請求操作。
3、StatefulWidget更新階段:build -> child didUpdateWidget -> child build。
4、StatefulWidget銷毀階段:build -> child deactivate -> child dispose。
原文鏈接:https://juejin.cn/post/7187607597426409533
相關推薦
- 2022-12-15 C++?Boost?Lockfree超詳細講解使用方法_C 語言
- 2022-01-14 path.join()和path.resolve()區別
- 2022-11-03 C++編譯期循環獲取變量類型詳情_C 語言
- 2023-03-16 Redis設置key的過期時間_Redis
- 2022-07-22 React警告:Can‘t perform a React state update on an u
- 2022-05-08 使用Visual?Studio編寫單元測試_實用技巧
- 2022-01-18 正則——16進制顏色
- 2023-10-09 grid網格布局
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支