前言
閱讀此文的彥祖,亦菲們,附送一枚Provider模板程式碼生成外掛!
我為啥要寫這個外掛呢?
此事說來話短,我這不準備寫解析Provider原始碼的文章,肯定要寫這框架的使用樣例啊,然後再嗶嗶原始碼呀!在寫demo樣例的時候,新建那倆三個檔案、資料夾和必寫的模板程式碼,這讓我感到很方啊,這不耽誤我時間嘛!然後就擼了這個外掛,相對而言,多花了幾百倍的時間。。。
希望這個外掛,能減輕使用Provider小夥們的一點工作量;外掛裡面的模板程式碼是經過我深思熟慮過的,如果各位靚仔有更好的模板程式碼,請在評論裡貼出來,我覺得合理的話,會加入到外掛裡。
關於Provider的原始碼,如果對設計模式或面向介面程式設計不熟悉的話,看起來是相當懵逼的,基本就是:懵逼樹上懵逼果,懵逼樹下你和我;Provider原始碼使用了大量的抽象類,呼叫父類建構函式,繼承實現斷言,很多關鍵的函式呼叫,點進去都是抽象類,必須返回好幾層去看看這個抽象類的實現類是什麼,看的十分頭大!這裡面有很多設計模式的痕跡:觀察者模式、策略模式、外觀模式、命令模式、訪問者模式、模板模式、迭代器模式、、、
我會竭盡所能的將總體流程說清楚,相關晦澀流程會結合圖文,並給出相應小demo演示
ε=(´ο`*)))唉,這篇文章寫完,我感覺整個人都被掏空了。。。
不管你用或不用Provider,我相信在你讀完本文的重新整理機制欄目,大概率會對該框架中閃耀的智慧,感到由衷的讚歎!
使用
老規矩,說原理之前,先來看下使用
Provider的使用,和我前倆篇寫的Handler和ThreadLocal使用有一些區別
Provider是一個狀態管理框架,寫它的使用可能會佔較多篇幅,所以文章整體篇幅也會較長,請見諒。。。
我實在不想分篇幅水贊啊,而且也是為了方便大家可以在一篇文章裡面查閱相關知識(請結合掘金旁邊的大綱食用),也方便我隨時修改優化文章內容。。。
外掛
-
外掛github:provider_template
- 使用中碰見什麼bug,希望大家能及時給我提issue
-
外掛可以進入Android Studio的Setting裡面,選擇Plugins,然後搜尋flutter provider,第一個,看圖上紅框標定的就是了,點選install安裝即可
- 來下看使用效果圖
- 如果你不喜歡這種命名方式,這裡提供修改入口;也支援了持久化
- 大家按需修改吧
初始寫法
- 在寫Provider的demo例項的時候,是按照下面這種寫法的,畢竟下面這種寫法,是非常
正統且常見
的一種寫法
class ProEasyCounterPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => ProEasyCounterProvider(),
child: _buildPage(context),
);
}
Widget _buildPage(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('Provider-Easy範例')),
body: Center(
child: Consumer<ProEasyCounterProvider>(
builder: (context, provider, child) {
return Text('點選了 ${provider.count} 次',
style: TextStyle(fontSize: 30.0));
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => Provider.of<ProEasyCounterProvider>(context, listen: false).increment(),
child: Icon(Icons.add),
),
);
}
}
class ProEasyCounterProvider extends ChangeNotifier {
int count = 0;
void increment() {
count++;
notifyListeners();
}
}
複製程式碼
這地方有個讓我很難受的地方,就是Provider.of這個實在是太長了,但是我如果不使用Provider.of,就需要把Scaffold整體包裹在Consumer裡面,這樣可以直接拿到provider變數使用,,,但是這樣的話,Consumer包裹的模組就有點太大了。。。
而且Provider.of這地方還只是使用了模組內Provider,還不是獲取全域性的Provider,使用頻率肯定很高,都這麼寫而且這麼長,想想就頭皮發麻,我方了呀。。。
優化寫法
上面那個Provider.of寫法,讓我巨難受:走在回去的路上想,有什麼方法可以優化呢?洗澡的時候想,有什麼方法可以優化呢?
我轉念一想,我這地方只是寫個使用demo,我特麼有必要這麼糾結嗎?!
但是,我就是糾結的一批啊,一定有什麼方法可以優化!(魔改框架? ...石樂志吧我)
突然靈光一閃!我!看到了光!蓋亞!
既然ChangeNotifierProvider裡面create引數,是接受了我例項化的ChangeNotifier物件,然後它內部存了起來,然後在Consume裡面的builder方法裡面分發給我,那我自己是不是也可把ChangeNotifier物件存起來!
- 突然間醍醐灌頂,思路就突破了,然後就可以愉快的在這上面玩耍了
class ProEasyCounterPage extends StatelessWidget {
final provider = ProEasyCounterProvider();
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => provider,
child: _buildPage(),
);
}
Widget _buildPage() {
return Scaffold(
appBar: AppBar(title: Text('Provider-Easy範例')),
body: Center(
child: Consumer<ProEasyCounterProvider>(
builder: (context, provider, child) {
return Text('點選了 ${provider.count} 次',
style: TextStyle(fontSize: 30.0));
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => provider.increment(),
child: Icon(Icons.add),
),
);
}
}
class ProEasyCounterProvider extends ChangeNotifier {
int count = 0;
void increment() {
count++;
notifyListeners();
}
}
複製程式碼
Provider.of(context, listen: false).increment() 直接變成 provider.increment()
一個模組裡面,會有很多地方用到provider,這樣一改,瞬間輕鬆很多,而且還不需要傳context了。。。
在這上面我們還能騷!還能簡化!
- 因為這裡我們直接使用我們自己儲存起來provider,所以可以進一步簡化
- Consumer進行了簡化,builder方法裡面引數,大部分情況不需要了
- 我甚至都想把泛型去掉;看了下原始碼,應該很難去掉,泛型在框架內部起到了至關重要的作用
//原版
Consumer<ProEasyCounterProvider>(builder: (context, provider, child) {
return Text(
'點選了 ${provider.count} 次',
style: TextStyle(fontSize: 30.0),
);
}),
//簡化
Consumer<ProEasyCounterProvider>(builder: (_, __, ___) {
return Text(
'點選了 ${provider.count} 次',
style: TextStyle(fontSize: 30.0),
);
}),
複製程式碼
瀏覽了Provider內部的原始碼後,發現:按照上面這樣寫是完全沒問題!會一定程度上提升效率!
凎!可以把外掛和demo程式碼全改了!搞起!
外掛生成程式碼
外掛生成程式碼分為倆個模式:Default和High
預設模式有倆個檔案(Default):view、provider
高階模式有三個檔案(High):view、provider、state
大家都是用Flutter的老手,對這種結構應該非常瞭解,state層是把資料層獨立出來維護
在非常複雜的提交介面,state層我甚至還會分出:跳轉(jump)、提交(submit)、展示(show)這三種結構;沒辦法,一個模組搞了上百個變數,不這樣分,太難維護了
default:預設模式下的模板程式碼
- view
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'provider.dart';
class CounterPage extends StatelessWidget {
final provider = CounterProvider();
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => provider,
child: Container(),
);
}
}
複製程式碼
- provider
import 'package:flutter/material.dart';
class CounterProvider extends ChangeNotifier {
}
複製程式碼
High:高階模式下的模板程式碼
- view
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'provider.dart';
class CounterPage extends StatelessWidget {
final provider = CounterProvider();
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => provider,
child: Container(),
);
}
}
複製程式碼
- provider
import 'package:flutter/material.dart';
import 'state.dart';
class CounterProvider extends ChangeNotifier {
final state = CounterState();
}
複製程式碼
- state
class CounterState {
CounterState() {
// init some variables
}
}
複製程式碼
前置知識
下面就是Provider的原始碼分析內容了,如果大家趕時間,可以點個贊(方便日後查閱,滑稽.jpg),回頭等有時間,再靜下心來慢慢看;我怕你快餐式閱讀,讀到重新整理機制那塊,會直接罵街,這寫的啥玩意???
Provider的重新整理機制,相關流程相當之繞,我已經竭盡全力,精簡了無數我們不需要關注的程式碼,然後一步步帶著你的思路去走一遍正確的流程,相關類還給了很多說明,但是架不住原始碼流程山路十八彎,繞的一比啊!你如果不用心去看,去體會,會相當煩躁。。。
我已經幫大家熬過最蛋筒的部分,相關繞的流程畫了詳細的圖示,我已經努力了;如果你想知道Provider內部運轉機制,現在就需要你努力了!
ChangeNotifier的單獨使用
ValueListenableBuilder和ValueNotifier可以配套使用,ValueListenableBuilder內部也是一個StatefulWidget,程式碼很簡單,感興趣的可以自己檢視
這個暫且不表,這邊就搞最原始的ChangeNotifier的使用
大家肯定在Provider都寫過繼承ChangeNotifier的程式碼,而且寫的非常多,但是大家知道怎麼單獨使用ChangeNotifier,以達到控制介面變化的效果嗎?
我搜了很多怎麼單獨使用ChangeNotifier的文章,但是基本都是寫配合ChangeNotifierProvider在Provider中使用的,我佛了呀,搜到寥寥無幾的文章,也沒說清楚,怎麼單獨使用;我想這玩意是不是有個單獨XxxWidgetBuild配合使用?但是!我怎麼都找不到,氣抖冷!
我突然想到,TextField控制元件中的TextEditingController用到了ChangeNotifier,總不可能TextField還用Provider吧!我在原始碼裡面一通翻,各種super,abstract,私有變數,看的頭皮發麻,最後終於找到了關鍵程式碼,搞清楚TextField是怎麼使用ChangeNotifier的了,為什麼每次改變TextEditingController的text值,然後在TextField資料框裡的資料也及時改變了,其實最後還是用到setState。
TextField中的流程程式碼不貼了,如果貼出來,會相當佔篇幅:我下面會寫一個顆粒度最小ChangeNotifier的單獨使用demo
- TextEditingController實際是繼承了ValueNotifier,來看下ValueNotifier
class ValueNotifier<T> extends ChangeNotifier implements ValueListenable<T> {
ValueNotifier(this._value);
@override
T get value => _value;
T _value;
set value(T newValue) {
if (_value == newValue)
return;
_value = newValue;
notifyListeners();
}
@override
String toString() => '${describeIdentity(this)}($value)';
}
複製程式碼
ValueNotifier實際是對ChangeNotifier的封裝
這裡影響不大,我們還是使用ChangeNotifier,來寫一個類似TextField中的控制器效果,每當控制器中的數值改變,其控制元件內容就自動更新
- 先使用ChangeNotifier搞一個控制器
class TestNotifierController extends ChangeNotifier {
String _value = '0';
String get value => _value;
set value(String newValue) {
if (_value == newValue) return;
_value = newValue;
notifyListeners();
}
}
複製程式碼
- 搭配這個控制器的Widget
- OK,這樣就搞定了,改變控制器的資料,Widget也會自動重新整理
- 我把功能顆粒度壓縮的非常小,希望大家閱讀會比較輕鬆
class TestNotifierWidget extends StatefulWidget {
const TestNotifierWidget({
Key? key,
this.controller,
}) : super(key: key);
final TestNotifierController? controller;
@override
_TestNotifierState createState() => _TestNotifierState();
}
class _TestNotifierState extends State<TestNotifierWidget> {
@override
void initState() {
///新增回撥 value改變時,自動觸發回撥內容
widget.controller?.addListener(_change);
super.initState();
}
@override
Widget build(BuildContext context) {
return Text(
widget.controller?.value ?? '初始值為空',
style: TextStyle(fontSize: 30.0),
);
}
///被觸發的回撥
void _change() {
setState(() {});
}
}
複製程式碼
- 來看下怎麼使用這個控制元件
- 使用程式碼已經非常簡單了:onPressed改變了控制器數值內容,TestNotifierWidget控制元件會自動重新整理
class TestNotifierPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final controller = TestNotifierController();
var count = 0;
return Scaffold(
appBar: AppBar(title: Text('ChangeNotifier使用演示')),
body: Center(
child: TestNotifierWidget(controller: controller),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
controller.value = '數值變化:${(++count).toString()}';
},
child: Icon(Icons.add),
),
);
}
}
複製程式碼
- 來看下效果圖
Function Call()
這裡說個小知識點,原始碼裡面大量使用了這個技巧,網上搜了下,很少提到這個的,這邊記一筆
每個Function都有個Call()方法
- 下面倆種方式呼叫是等同的,都能呼叫test方法
void main(){
test();
test.call();
}
void test(){
print('test');
}
複製程式碼
你可能想,這有什麼用,我還多寫一個 .call ?
來看下一個小范例,就知道這個東西能幫我們簡化很多程式碼
- 平時封裝帶有CallBack回撥Widget
- 這邊寫了倆個自定義的點選回撥判斷操作
- 如果不做判空操作,外部未實現這個Function,點選事件會報空異常
class TestWidget extends StatelessWidget {
const TestWidget({
Key? key,
this.onTap,
this.onBack,
}) : super(key: key);
final VoidCallback? onTap;
final VoidCallback? onBack;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
if (onTap != null) {
onTap!();
}
if (onBack != null) {
onBack!();
}
},
child: Container(),
);
}
}
複製程式碼
- 使用 .call() 後,可以怎麼寫呢?
- 可以幹掉麻煩的if判空操作了!
class TestWidget extends StatelessWidget {
const TestWidget({
Key? key,
this.onTap,
this.onBack,
}) : super(key: key);
final VoidCallback? onTap;
final VoidCallback? onBack;
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
onTap?.call();
onBack?.call();
},
child: Container(),
);
}
}
複製程式碼
重新整理機制
Provider的重新整理機制是非常重要的,只要把Provider的重新整理機制搞清楚,這個框架在你面前,將不在神祕!
實際上,大家只要看到ChangeNotifier的應用,那肯定知道,這就是個觀察者模式,但是問題是:它的監聽在何處新增?新增的監聽邏輯是否有完整的初始化鏈路?監聽邏輯是什麼?為什麼觸發監聽邏輯,能導致相應控制元件重新整理?
- 上面初始化的完整鏈路看的真是有點蛋痛
- 原始碼東一榔錘西一棒的,而且還用了大量了抽象類,想直接定位邏輯,那是不可能的,你必須找到實現類賦值的地方,才能明白內部運轉
- 不搞清楚完整初始化鏈路,內心就相當於膈應,明知道他肯定初始化了,卻不知道他在哪初始化的,就很難受
- 我下面將相關流程理了一遍,希望對大家有所幫助
- 要讀懂Provider,必須要有個前提,明白什麼觀察者模式:觀察者模式其實很簡單,簡單描述下
- 定義個List型別,泛型為一個抽象類,初始化這個List
- 然後給這個List,add這個抽象類的實現類例項
- 某個合適時候,遍歷這個List所有例項,觸發所有例項的某個方法
- 如果將這個思想和反射註解結合在一起,就能大大拓寬它的使用面,例如android裡的EventBus。。。
總流程
繼承ChangeNotifier的類,是通過ChangeNotifierProvider傳入到Provider內部,很明顯ChangeNotifierProvider這個類很重要,基本可以算是框架的主入口
這邊梳理下ChangeNotifierProvider 回溯的總流程,其它的旁枝末節,暫時不貼程式碼,這個往上回溯的過程,例項了一個很重要的上下文類,很多關鍵的類初始化都和這個上下文類有關係,先來回溯下這個重要的流程!
- ChangeNotifierProvider
- 這地方有個_dispose回撥,是定義好的,內部邏輯是回收ChangeNotifier例項
- 這裡將該方法賦值給了他的父類ListenableProvider,然後一層層往上回溯
class ChangeNotifierProvider<T extends ChangeNotifier?> extends ListenableProvider<T> {
ChangeNotifierProvider({
Key? key,
required Create<T> create,
bool? lazy,
TransitionBuilder? builder,
Widget? child,
}) : super(
key: key,
create: create,
dispose: _dispose,
lazy: lazy,
builder: builder,
child: child,
);
...
static void _dispose(BuildContext context, ChangeNotifier? notifier) {
notifier?.dispose();
}
}
複製程式碼
- ListenableProvider
- 這地方有個_startListening回撥,這個方法極其重要
class ListenableProvider<T extends Listenable?> extends InheritedProvider<T> {
ListenableProvider({
Key? key,
required Create<T> create,
Dispose<T>? dispose,
bool? lazy,
TransitionBuilder? builder,
Widget? child,
}) : super(
key: key,
startListening: _startListening,
create: create,
dispose: dispose,
lazy: lazy,
builder: builder,
child: child,
);
...
static VoidCallback _startListening(InheritedContext e, Listenable? value,) {
value?.addListener(e.markNeedsNotifyDependents);
return () => value?.removeListener(e.markNeedsNotifyDependents);
}
}
複製程式碼
- InheritedProvider
- 這個類就是邏輯的糾纏點了:我省略了大量和主流程無關的程式碼,不然會十分影響你的關注點,會很難受
- 這裡就不需要看他的父類了,他的父類是SingleChildStatelessWidget,這個類是對StatelessWidget類的一個封裝,能稍微優化下巢狀問題,無關緊要
- 需要看下buildWithChild(看成StatelessWidget的build方法就行了)方法裡面的_InheritedProviderScope類,來看下他的原始碼
class InheritedProvider<T> extends SingleChildStatelessWidget {
InheritedProvider({
Key? key,
Create<T>? create,
T Function(BuildContext context, T? value)? update,
UpdateShouldNotify<T>? updateShouldNotify,
void Function(T value)? debugCheckInvalidValueType,
StartListening<T>? startListening,
Dispose<T>? dispose,
this.builder,
bool? lazy,
Widget? child,
}) : _lazy = lazy,
_delegate = _CreateInheritedProvider(
create: create,
update: update,
updateShouldNotify: updateShouldNotify,
debugCheckInvalidValueType: debugCheckInvalidValueType,
startListening: startListening,
dispose: dispose,
),
super(key: key, child: child);
...
final _Delegate<T> _delegate;
final bool? _lazy;
final TransitionBuilder? builder;
...
@override
Widget buildWithChild(BuildContext context, Widget? child) {
...
return _InheritedProviderScope<T>(
owner: this,
debugType: kDebugMode ? '$runtimeType' : '',
child: builder != null
? Builder(
builder: (context) => builder!(context, child),
)
: child!,
);
}
}
複製程式碼
- _InheritedProviderScope
- 這裡是繼承了InheritedWidget,裡面重寫createElement方法,在構建Widget的時候,這個方法是肯定會被呼叫的!
- 馬上就要到最重要的類了,就是createElement中例項化的_InheritedProviderScopeElement類!
class _InheritedProviderScope<T> extends InheritedWidget {
const _InheritedProviderScope({
required this.owner,
required this.debugType,
required Widget child,
}) : super(child: child);
final InheritedProvider<T> owner;
final String debugType;
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
return false;
}
@override
_InheritedProviderScopeElement<T> createElement() {
return _InheritedProviderScopeElement<T>(this);
}
}
複製程式碼
- _InheritedProviderScopeElement:實現方法裡面的邏輯全省略了,邏輯太多,看著頭暈
- 先說明下,這個類是極其極其重要的!大家可以看下他實現了一個什麼抽象類:InheritedContext!
- InheritedContext繼承了BuildContext,也就是說,這裡作者實現了BuildContext所有抽象方法
- 是的,BuildContext也是個抽象類,我們可以去實現多個不同實現類
- 內部系統只需要特定的週期去觸發相應方法,就可以了
- 你可以在相應的方法裡面實現自己的邏輯,大大的擴充套件了邏輯,怎麼說呢?有點策略模式味道,可以動態替換實現類
- _InheritedProviderScopeElement算是實現了:InheritedContext和BuildContext;BuildContext中有很多方法是和控制元件生命週期掛鉤的,例如熱過載觸發(reassemble),setState觸發(build、performRebuild)、以及很有意思的強制依賴項元件重新整理(markNeedsNotifyDependents:這是Provider作者在InheritedContext中抽象的方法)。。。
abstract class InheritedContext<T> extends BuildContext {
T get value;
void markNeedsNotifyDependents();
bool get hasValue;
}
class _InheritedProviderScopeElement<T> extends InheritedElement implements InheritedContext<T> {
_InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
: super(widget);
...
@override
void mount(Element? parent, dynamic newSlot) {
...
}
@override
_InheritedProviderScope<T> get widget => super.widget as _InheritedProviderScope<T>;
@override
void reassemble() {
...
}
@override
void updateDependencies(Element dependent, Object? aspect) {
...
}
@override
void notifyDependent(InheritedWidget oldWidget, Element dependent) {
...
}
@override
void performRebuild() {
...
}
@override
void update(_InheritedProviderScope<T> newWidget) {
...
}
@override
void updated(InheritedWidget oldWidget) {
...
}
@override
void didChangeDependencies() {
...
}
@override
Widget build() {
...
}
@override
void unmount() {
...
}
@override
bool get hasValue => _delegateState.hasValue;
@override
void markNeedsNotifyDependents() {
...
}
bool _debugSetInheritedLock(bool value) {
...
}
@override
T get value => _delegateState.value;
@override
InheritedWidget dependOnInheritedElement(
InheritedElement ancestor, {
Object? aspect,
}) {
...
}
@override
void debugFillProperties(DiagnosticPropertiesBuilder properties) {
...
}
}
複製程式碼
上面進行了五步的回溯流程,如果不仔細看清楚相關類裡面的邏輯,很可能就迷失在super方法裡。。。
通過上面的五步回溯,我們可以斷定一個事實:_InheritedProviderScopeElement(實現BuildContext) 被例項化了,而且他在初始化的時候被呼叫了,對應的,其內部相應的週期也能被正常觸發!這樣之前看原始碼困擾我的很多問題,就迎刃而解了!
- 圖示
- 上面回溯的層級過多,還有很多的繼承和實現
- 看了後,腦中可能沒啥印象,所以此處畫了流程圖,可以參照對比
新增監聽
整個重新整理機制裡面有個相當重要的一環,我們從Create中傳入的類,它內部是怎麼處理的?
class ProEasyCounterPage extends StatelessWidget {
final provider = ProEasyCounterProvider();
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => provider,
child: Container(),
);
}
}
複製程式碼
就算沒看原始碼,我也能斷定傳入的XxxProvider例項,肯定使用了其本身的addListener方法!
但是找這個addListener方法,實在讓我找自閉了,之前因為沒梳理總流程,對其初始化鏈路不明晰,找到了addListener方法,我都十分懷疑,是不是找對了、其它地方是不是還有addListener方法;後來沒辦法,就把Provider原始碼下載下來(之前直接專案裡面點Provider外掛原始碼看的),全域性搜尋addListener方法,排除所有的測試類中使用的,然後斷定我找對了,整個新增監聽的鏈路是通順的!
下面來整體的帶大家過一遍原始碼
靚仔們,我要開始繞了!!!
流轉
- ChangeNotifierProvider
- 明確下Create是一個Function,返回繼承ChangeNotifier類的例項
- 這裡一定要記住create這個變數的走向,其中的T就是繼承ChangeNotifier類的關鍵類
- 增加了_dispose方法,傳給了父類
- create這裡super給其父類,回溯下父類
typedef Create<T> = T Function(BuildContext context);
class ChangeNotifierProvider<T extends ChangeNotifier?> extends ListenableProvider<T> {
ChangeNotifierProvider({
Key? key,
required Create<T> create,
bool? lazy,
TransitionBuilder? builder,
Widget? child,
}) : super(
key: key,
create: create,
dispose: _dispose,
lazy: lazy,
builder: builder,
child: child,
);
...
static void _dispose(BuildContext context, ChangeNotifier? notifier) {
notifier?.dispose();
}
}
複製程式碼
- ListenableProvider
- 此處將create例項super給了父類
- 還增加一個_startListening方法,也同樣給了父類
class ListenableProvider<T extends Listenable?> extends InheritedProvider<T> {
ListenableProvider({
Key? key,
required Create<T> create,
Dispose<T>? dispose,
bool? lazy,
TransitionBuilder? builder,
Widget? child,
}) : super(
key: key,
startListening: _startListening,
create: create,
dispose: dispose,
lazy: lazy,
builder: builder,
child: child,
);
...
static VoidCallback _startListening(InheritedContext e, Listenable? value,) {
value?.addListener(e.markNeedsNotifyDependents);
return () => value?.removeListener(e.markNeedsNotifyDependents);
}
}
複製程式碼
- InheritedProvider
- 這地方和上面總流程不太一樣了
- create、dispose、startListening傳給了_CreateInheritedProvider
- 需要看下_CreateInheritedProvider
class InheritedProvider<T> extends SingleChildStatelessWidget {
InheritedProvider({
Key? key,
Create<T>? create,
T Function(BuildContext context, T? value)? update,
UpdateShouldNotify<T>? updateShouldNotify,
void Function(T value)? debugCheckInvalidValueType,
StartListening<T>? startListening,
Dispose<T>? dispose,
this.builder,
bool? lazy,
Widget? child,
}) : _lazy = lazy,
_delegate = _CreateInheritedProvider(
create: create,
update: update,
updateShouldNotify: updateShouldNotify,
debugCheckInvalidValueType: debugCheckInvalidValueType,
startListening: startListening,
dispose: dispose,
),
super(key: key, child: child);
...
}
複製程式碼
- 流程圖示
_CreateInheritedProvider
這地方會進行一個很重要的回溯流程,回溯到_InheritedProviderScopeElement
下次再有需要用到這個類,就直接拿這個類來講了
- _CreateInheritedProvider說明
- _CreateInheritedProvider繼承了抽象類 _Delegate,實現了其createState抽象方法
- 按理說,主要邏輯肯定在createState方法中**_CreateInheritedProviderState**例項中
- 必須要看下_CreateInheritedProvider例項,在何處呼叫 createState方法,然後才能繼續看 _CreateInheritedProviderState的邏輯
@immutable
abstract class _Delegate<T> {
_DelegateState<T, _Delegate<T>> createState();
void debugFillProperties(DiagnosticPropertiesBuilder properties) {}
}
class _CreateInheritedProvider<T> extends _Delegate<T> {
_CreateInheritedProvider({
this.create,
this.update,
UpdateShouldNotify<T>? updateShouldNotify,
this.debugCheckInvalidValueType,
this.startListening,
this.dispose,
}) : assert(create != null || update != null),
_updateShouldNotify = updateShouldNotify;
final Create<T>? create;
final T Function(BuildContext context, T? value)? update;
final UpdateShouldNotify<T>? _updateShouldNotify;
final void Function(T value)? debugCheckInvalidValueType;
final StartListening<T>? startListening;
final Dispose<T>? dispose;
@override
_CreateInheritedProviderState<T> createState() =>
_CreateInheritedProviderState();
}
複製程式碼
- 這裡需要重新回顧下InheritedProvider類
- 這地方做了一個很重要的操作,將_CreateInheritedProvider例項賦值給 _delegate
- buildWithChild方法中_InheritedProviderScope的owner接受了InheritedProvider本身的例項
- 結合這倆個就有戲了,再來看下_InheritedProviderScope類
class InheritedProvider<T> extends SingleChildStatelessWidget {
InheritedProvider({
Key? key,
Create<T>? create,
T Function(BuildContext context, T? value)? update,
UpdateShouldNotify<T>? updateShouldNotify,
void Function(T value)? debugCheckInvalidValueType,
StartListening<T>? startListening,
Dispose<T>? dispose,
this.builder,
bool? lazy,
Widget? child,
}) : _lazy = lazy,
_delegate = _CreateInheritedProvider(
create: create,
update: update,
updateShouldNotify: updateShouldNotify,
debugCheckInvalidValueType: debugCheckInvalidValueType,
startListening: startListening,
dispose: dispose,
),
super(key: key, child: child);
final _Delegate<T> _delegate;
final bool? _lazy;
...
@override
Widget buildWithChild(BuildContext context, Widget? child) {
,,,
return _InheritedProviderScope<T>(
owner: this,
debugType: kDebugMode ? '$runtimeType' : '',
child: builder != null
? Builder(
builder: (context) => builder!(context, child),
)
: child!,
);
}
}
複製程式碼
- _InheritedProviderScope
- createElement方法傳入_InheritedProviderScope本身的例項
- 關鍵的在_InheritedProviderScopeElement類中
class _InheritedProviderScope<T> extends InheritedWidget {
const _InheritedProviderScope({
required this.owner,
required this.debugType,
required Widget child,
}) : super(child: child);
final InheritedProvider<T> owner;
final String debugType;
@override
bool updateShouldNotify(InheritedWidget oldWidget) {
return false;
}
@override
_InheritedProviderScopeElement<T> createElement() {
return _InheritedProviderScopeElement<T>(this);
}
}
複製程式碼
- _InheritedProviderScopeElement類,我就直接精簡到關鍵程式碼了
- 有沒有感覺InheritedWidget很像StatefulWidget,實際他倆最終都是繼承Widget,未對Widget的建造者模式那層封裝,所以有倆層結構;而StatelessWidget將建造者模式那層進行了封裝,所以只有一層結構
- 下面的關鍵程式碼看到沒!**widget.owner._delegate.createState() ... ** 這地方呼叫了_CreateInheritedProvider類的createState() 方法,安心了
- performRebuild:該回撥會在setState或者build的時候會觸發;此處做了一個判斷,只會在第一次build的時候觸發
- 這裡可以確定_CreateInheritedProvider類中的createState方法一定會被呼叫;接下來看看其方法裡面呼叫的 _CreateInheritedProviderState類
class _InheritedProviderScopeElement<T> extends InheritedElement
implements InheritedContext<T> {
_InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
: super(widget);
...
@override
void performRebuild() {
if (_firstBuild) {
_firstBuild = false;
_delegateState = widget.owner._delegate.createState()..element = this;
}
super.performRebuild();
}
...
}
複製程式碼
- 流程圖示
_InheritedProviderScopeElement
- _CreateInheritedProviderState:這個類做了很多事情,很多的主體邏輯的都在此處理
- 該類程式碼很多,此處只留下我們需要關注的程式碼,因為省略了很多程式碼,從下面的主體程式碼來看,流程就清楚了:create、startListening、dispose 都有
- 但是這些變數是依附在delegate上的,這個delegate是個啥?需要看下繼承的抽象類 _DelegateState
class _CreateInheritedProviderState<T> extends _DelegateState<T, _CreateInheritedProvider<T>> {
VoidCallback? _removeListener;
bool _didInitValue = false;
T? _value;
_CreateInheritedProvider<T>? _previousWidget;
@override
T get value {
...
if (!_didInitValue) {
_didInitValue = true;
if (delegate.create != null) {
assert(debugSetInheritedLock(true));
try {
...
_value = delegate.create!(element!);
} finally {
...
}
...
}
...
}
element!._isNotifyDependentsEnabled = false;
_removeListener ??= delegate.startListening?.call(element!, _value as T);
element!._isNotifyDependentsEnabled = true;
assert(delegate.startListening == null || _removeListener != null);
return _value as T;
}
@override
void dispose() {
super.dispose();
_removeListener?.call();
if (_didInitValue) {
delegate.dispose?.call(element!, _value as T);
}
}
...
}
複製程式碼
- _DelegateState
- delegate是通過 _InheritedProviderScopeElement的例項獲取到了owner然後獲取到了 _delegate變數
- _delegate這個變數是在InheritedProvider類中的例項化 _CreateInheritedProvider賦值給他的,不信的話,可以返回去看看
- 好吉爾繞!!!
abstract class _DelegateState<T, D extends _Delegate<T>> {
_InheritedProviderScopeElement<T>? element;
T get value;
D get delegate => element!.widget.owner._delegate as D;
bool get hasValue;
bool debugSetInheritedLock(bool value) {
return element!._debugSetInheritedLock(value);
}
bool willUpdateDelegate(D newDelegate) => false;
void dispose() {}
void debugFillProperties(DiagnosticPropertiesBuilder properties) {}
void build({required bool isBuildFromExternalSources}) {}
}
複製程式碼
- element
- 現在還有個問題,element這個變數在哪例項化的?怎麼大家這麼隨便用它!就不怕它為空嗎?
- 直接帶大家來_InheritedProviderScopeElement裡面看了,上面已經回顧了到這個必定例項化這個上下文類的流程
- performRebuild回撥中,在呼叫createState()方法的時候,給element賦值了,element = this
- 所以在_CreateInheritedProviderState類中,可以隨便使用element 這個變數,他的值肯定不為空!
class _InheritedProviderScopeElement<T> extends InheritedElement
implements InheritedContext<T> {
_InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
: super(widget);
...
@override
void performRebuild() {
if (_firstBuild) {
_firstBuild = false;
_delegateState = widget.owner._delegate.createState()..element = this;
}
super.performRebuild();
}
...
}
複製程式碼
不知道大家對這流程有沒有個清晰的印象
- 來看看這山路十八彎的初始化鏈路圖
_CreateInheritedProviderState
有了上面分析出的element和_delegate不為空的,且 _delegate能直接訪問 _CreateInheritedProvider這個例項基礎,再來看下 _CreateInheritedProviderState程式碼
- get 流程
- 我們傳入的create會直接賦值給 _value,現在這個 _value,就是我們在外面傳進來的那個XxxProvider例項了!
- 底下也呼叫了 startListening,說明從外面傳進來的這個回撥也呼叫了,將 上下文例項 和 傳進來的XxxProvider例項 作為入參傳進了這個回撥中,此處傳進來的回撥也通過 .call 被呼叫了!
- dispose 流程
- 呼叫startListening方法時,該方法會返回一個移除監聽Function
- 移除監聽的Function在dispose時被呼叫,移除給XxxProvider新增的監聽
- 從外部傳入的dispose方法,也在此處被執行
- OK!回收資源的操作在此處都搞定了!
class _CreateInheritedProviderState<T> extends _DelegateState<T, _CreateInheritedProvider<T>> {
VoidCallback? _removeListener;
bool _didInitValue = false;
T? _value;
_CreateInheritedProvider<T>? _previousWidget;
@override
T get value {
...
if (!_didInitValue) {
_didInitValue = true;
if (delegate.create != null) {
assert(debugSetInheritedLock(true));
try {
...
_value = delegate.create!(element!);
} finally {
...
}
...
}
...
}
element!._isNotifyDependentsEnabled = false;
_removeListener ??= delegate.startListening?.call(element!, _value as T);
element!._isNotifyDependentsEnabled = true;
assert(delegate.startListening == null || _removeListener != null);
return _value as T;
}
@override
void dispose() {
super.dispose();
_removeListener?.call();
if (_didInitValue) {
delegate.dispose?.call(element!, _value as T);
}
}
...
}
複製程式碼
- 關鍵的就是startListening回撥了,來看下他的邏輯
- _startListening在此處 addListener 了!ChangeNotifier 是 Listenable 實現類,姑且把它當成訪問者模式也可,所以這個value就是我們從外面傳進來的 XxxProvider
- 返回了一個VoidCallback的Function,裡面是移除監聽邏輯
class ListenableProvider<T extends Listenable?> extends InheritedProvider<T> {
ListenableProvider({
Key? key,
required Create<T> create,
Dispose<T>? dispose,
bool? lazy,
TransitionBuilder? builder,
Widget? child,
}) : super(
key: key,
startListening: _startListening,
create: create,
dispose: dispose,
lazy: lazy,
builder: builder,
child: child,
);
...
static VoidCallback _startListening(InheritedContext e, Listenable? value,) {
value?.addListener(e.markNeedsNotifyDependents);
return () => value?.removeListener(e.markNeedsNotifyDependents);
}
}
複製程式碼
還有最後一個問題!!!
需要呼叫_startListening方法,必須呼叫 _CreateInheritedProviderState類裡面的 get value
在哪個初始化入口,使用這個 get value 呢?
- 這裡直接給出結論了,還是在 _InheritedProviderScopeElement這個上下文類裡面
- reassemble:全域性狀態的初始化邏輯或熱過載的時候被呼叫
- _delegateState首先在performRebuild回撥中會賦初值
- 在reassemble回撥中,_delegateState呼叫了value( _delegateState.value )
- 所以 get value 肯定會在初始化的時候被呼叫,上面流程是通順的
class _InheritedProviderScopeElement<T> extends InheritedElement
implements InheritedContext<T> {
_InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
: super(widget);
late _DelegateState<T, _Delegate<T>> _delegateState;
...
@override
void performRebuild() {
if (_firstBuild) {
_firstBuild = false;
_delegateState = widget.owner._delegate.createState()..element = this;
}
super.performRebuild();
}
@override
void reassemble() {
super.reassemble();
final value = _delegateState.hasValue ? _delegateState.value : null;
if (value is ReassembleHandler) {
value.reassemble();
}
}
...
}
複製程式碼
總結
上面分析完了新增監聽,以及相關的初始化鏈路和呼叫鏈路
- 可以把流程圖整全了,來看看
重新整理邏輯
重新整理邏輯也是相當之繞啊;本菜比,各種debug,在framework裡面各種打斷點,終於把流程理通了!我突然感覺自己打通了任督二脈!
作者為了實現這個重新整理邏輯,和系統api做了大量的互動,相當的精彩!
我會盡力將這個精彩紛呈的操作,展現給大家!
觸發
- ListenableProvider
- 這地方邏輯很簡單,新增了InheritedContext這個上下文類中的markNeedsNotifyDependents方法
- 說明,我們在外部使用notifyListeners() 的時候,一定會觸發InheritedContext實現類中的markNeedsNotifyDependents方法
class ListenableProvider<T extends Listenable?> extends InheritedProvider<T> {
ListenableProvider({
Key? key,
required Create<T> create,
Dispose<T>? dispose,
bool? lazy,
TransitionBuilder? builder,
Widget? child,
}) : super(
key: key,
startListening: _startListening,
create: create,
dispose: dispose,
lazy: lazy,
builder: builder,
child: child,
);
...
static VoidCallback _startListening(InheritedContext e, Listenable? value,) {
value?.addListener(e.markNeedsNotifyDependents);
return () => value?.removeListener(e.markNeedsNotifyDependents);
}
}
複製程式碼
- _InheritedProviderScopeElement: _InheritedProviderScopeElement是InheritedContext的實現類
- 還是要來這個類看看,只保留了和markNeedsNotifyDependents有關的程式碼
- markNeedsNotifyDependents回撥作用,總的來說:會將強制依賴於T視窗小部件進行重建
- 說的這麼籠統沒啥用,下面會全面分析,他是怎麼做到讓依賴於T視窗小部件進行重建的! 我想了下,還是觀察者模式的應用。。。
class _InheritedProviderScopeElement<T> extends InheritedElement implements InheritedContext<T> {
_InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
: super(widget);
...
@override
void markNeedsNotifyDependents() {
if (!_isNotifyDependentsEnabled) {
return;
}
markNeedsBuild();
_shouldNotifyDependents = true;
}
...
}
複製程式碼
重新整理流程
我們們現在來理一下重新整理的流程!
- markNeedsNotifyDependents
- 當我們使用 notifyListeners(),就會觸發,這個回撥
- 此處呼叫了 markNeedsBuild(),然後給 _shouldNotifyDependents 設定為true
- 必備操作,來看下 markNeedsBuild() 作用
class _InheritedProviderScopeElement<T> extends InheritedElement implements InheritedContext<T> {
_InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
: super(widget);
bool _shouldNotifyDependents = false;
...
@override
void markNeedsNotifyDependents() {
if (!_isNotifyDependentsEnabled) {
return;
}
markNeedsBuild();
_shouldNotifyDependents = true;
}
...
}
複製程式碼
- markNeedsBuild
- _InheritedProviderScopeElement最終繼承的還是Element抽象類,markNeedsBuild()方法是Element中的
- Element類是一個實現了BuildContext抽象類中抽象方法的抽象類,該類十分重要
- 這個方法花裡胡哨的程式碼寫了一大堆,他最主要的功能:就是會呼叫Element的performRebuild()方法,然後觸發ComponentElement的build()方法,最終觸發_InheritedProviderScopeElement的build方法
- _InheritedProviderScopeElement extends InheritedElement extends ProxyElement extends ComponentElement extends Element
abstract class Element extends DiagnosticableTree implements BuildContext {
...
void markNeedsBuild() {
assert(_lifecycleState != _ElementLifecycle.defunct);
if (_lifecycleState != _ElementLifecycle.active)
return;
assert(owner != null);
assert(_lifecycleState == _ElementLifecycle.active);
assert(() {
if (owner!._debugBuilding) {
assert(owner!._debugCurrentBuildTarget != null);
assert(owner!._debugStateLocked);
if (_debugIsInScope(owner!._debugCurrentBuildTarget!))
return true;
if (!_debugAllowIgnoredCallsToMarkNeedsBuild) {
final List<DiagnosticsNode> information = <DiagnosticsNode>[
ErrorSummary('setState() or markNeedsBuild() called during build.'),
ErrorDescription(
'This ${widget.runtimeType} widget cannot be marked as needing to build because the framework '
'is already in the process of building widgets. A widget can be marked as '
'needing to be built during the build phase only if one of its ancestors '
'is currently building. This exception is allowed because the framework '
'builds parent widgets before children, which means a dirty descendant '
'will always be built. Otherwise, the framework might not visit this '
'widget during this build phase.',
),
describeElement(
'The widget on which setState() or markNeedsBuild() was called was',
),
];
if (owner!._debugCurrentBuildTarget != null)
information.add(owner!._debugCurrentBuildTarget!.describeWidget('The widget which was currently being built when the offending call was made was'));
throw FlutterError.fromParts(information);
}
assert(dirty); // can only get here if we're not in scope, but ignored calls are allowed, and our call would somehow be ignored (since we're already dirty)
} else if (owner!._debugStateLocked) {
assert(!_debugAllowIgnoredCallsToMarkNeedsBuild);
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() or markNeedsBuild() called when widget tree was locked.'),
ErrorDescription(
'This ${widget.runtimeType} widget cannot be marked as needing to build '
'because the framework is locked.',
),
describeElement('The widget on which setState() or markNeedsBuild() was called was'),
]);
}
return true;
}());
if (dirty)
return;
_dirty = true;
owner!.scheduleBuildFor(this);
}
...
}
複製程式碼
- build
- 這裡說明下,這個子類呼叫父類方法,然後父類呼叫自身方法,是先觸發這個子類的重寫方法,然後可以通過 super. 的方式去執行父類邏輯
- 上面給_shouldNotifyDependents設定為true,所以build內部邏輯會執行notifyClients(widget)方法
- 接下來看下notifyClients(widget)方法
class _InheritedProviderScopeElement<T> extends InheritedElement implements InheritedContext<T> {
_InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
: super(widget);
bool _shouldNotifyDependents = false;
...
@override
Widget build() {
if (widget.owner._lazy == false) {
value; // this will force the value to be computed.
}
_delegateState.build(
isBuildFromExternalSources: _isBuildFromExternalSources,
);
_isBuildFromExternalSources = false;
if (_shouldNotifyDependents) {
_shouldNotifyDependents = false;
notifyClients(widget);
}
return super.build();
}
...
}
複製程式碼
- notifyClients:notifyClients()是InheritedElement類中的,notifyClients()方法是ProxyElement類中的一個抽象方法,InheritedElement在此處做了一個實現
- notifyClients()是一個非常非常重要的方法,它內部有個for迴圈,遍歷了_dependents這個HashMap型別的所有key值, _dependents的key是Element型別
- 什麼是Element?它可以表示為Widget在樹中特定位置的例項,一個Element可以形成一棵樹(想想每個Container都有Element,然後其child再套其它的widget,這樣就形成了一顆樹)
- Element在此處將其理解為:本身Widget和其子節點形成的樹,Element是這棵樹的頭結點,這特定位置的節點是例項化的,對這個特定位置的例項節點操作,會影響到他的子節點
- Widget的createElement()方法會例項化Element
- 這地方遍歷_dependents的key取Element,可以猜測:他肯定是想取某個元素或者說某個Widget
- 取到相關Element例項後,她會傳入notifyDependent(oldWidget, dependent)方法中
- 接下來,需要看看notifyDependent(oldWidget, dependent)方法邏輯了
- notifyClients()是一個非常非常重要的方法,它內部有個for迴圈,遍歷了_dependents這個HashMap型別的所有key值, _dependents的key是Element型別
class InheritedElement extends ProxyElement {
final Map<Element, Object?> _dependents = HashMap<Element, Object?>();
...
@override
void notifyClients(InheritedWidget oldWidget) {
assert(_debugCheckOwnerBuildTargetExists('notifyClients'));
for (final Element dependent in _dependents.keys) {
assert(() {
// check that it really is our descendant
Element? ancestor = dependent._parent;
while (ancestor != this && ancestor != null)
ancestor = ancestor._parent;
return ancestor == this;
}());
// check that it really depends on us
assert(dependent._dependencies!.contains(this));
notifyDependent(oldWidget, dependent);
}
}
}
複製程式碼
- notifyDependent
- if (dependencies is _Dependency) 這判斷的邏輯題裡面還有很多邏輯,是作者在BuildContext上面搞了一個select擴充套件方法(判斷是否需要重新整理),但和現在講了重新整理流程無關,我在裡面繞了好久,凎!
- 去掉上面的邏輯就簡單了,shouldNotify賦值為true,最後呼叫dependent.didChangeDependencies()
- dependent還記得是啥嗎?是父類裡面迴圈取得的Element例項
- 這地方直接去掉super操作,這也是系統建議的,我們可以重寫notifyDependent方法,自定義相關邏輯;因為有時我們需要可選擇性的呼叫dependent.didChangeDependencies()!
class _InheritedProviderScopeElement<T> extends InheritedElement implements InheritedContext<T> {
_InheritedProviderScopeElement(_InheritedProviderScope<T> widget)
: super(widget);
...
@override
void notifyDependent(InheritedWidget oldWidget, Element dependent) {
final dependencies = getDependencies(dependent);
if (kDebugMode) {
ProviderBinding.debugInstance.providerDidChange(_debugId);
}
var shouldNotify = false;
if (dependencies != null) {
if (dependencies is _Dependency<T>) {
...
} else {
shouldNotify = true;
}
}
if (shouldNotify) {
dependent.didChangeDependencies();
}
}
...
}
複製程式碼
- didChangeDependencies
- didChangeDependencies邏輯就很簡單了,會呼叫markNeedsBuild()
- 可以理解為:最終會呼叫該Widget的build方法
- markNeedsBuild()就不講了,內部涉及邏輯太多了,還涉及bind類,還會涉及到繪製流程,我嘞個去。。。
abstract class Element extends DiagnosticableTree implements BuildContext {
...
@mustCallSuper
void didChangeDependencies() {
assert(_lifecycleState == _ElementLifecycle.active); // otherwise markNeedsBuild is a no-op
assert(_debugCheckOwnerBuildTargetExists('didChangeDependencies'));
markNeedsBuild();
}
...
}
複製程式碼
現在有個超糾結的事情,這個點關乎整個重新整理流程的樞紐!
InheritedElement中的_dependents這個map的key是Element,這個Element是什麼?上面所有流程都是為了呼叫 _dependents這個Map中key(Element)的markNeedsBuild()方法,最終是為了呼叫這個Element的Widget的build方法!
大家明白了嗎?我們就算大膽去蒙,去猜,去賭,這個Widget十有八九就是Consumer這類重新整理Widget啊!
但是!但是!他到底是怎麼將這類重新整理Widget新增到InheritedElement的 _dependents變數中的呢 !?
- 上述流程圖示
BuildContext
插播一個小知識點,這個知識和下述內容相關,這邊先介紹一下
BuildContext是什麼?
- BuildContext
- 每個抽象方法上面註釋超級多,我刪掉了(佔篇幅),有興趣的可以自己去原始碼裡看看
- BuildContext就是抽象類,是約定好的一個抽象類,相關方法的功能已經被約定,你如果想實現這個抽象類類,相關方法功能實現可以有出入,但不應該偏離抽象方法註釋所描述的功能範圍
abstract class BuildContext {
Widget get widget;
BuildOwner? get owner;
bool get debugDoingBuild;
RenderObject? findRenderObject();
Size? get size;
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect });
T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object? aspect });
InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>();
T? findAncestorWidgetOfExactType<T extends Widget>();
T? findAncestorStateOfType<T extends State>();
T? findRootAncestorStateOfType<T extends State>();
T? findAncestorRenderObjectOfType<T extends RenderObject>();
void visitAncestorElements(bool Function(Element element) visitor);
void visitChildElements(ElementVisitor visitor);
DiagnosticsNode describeElement(String name, {DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty});
DiagnosticsNode describeWidget(String name, {DiagnosticsTreeStyle style = DiagnosticsTreeStyle.errorProperty});
List<DiagnosticsNode> describeMissingAncestor({ required Type expectedAncestorType });
DiagnosticsNode describeOwnershipChain(String name);
}
複製程式碼
- StatelessWidget:看下StatelessWidget對BuildContext的實現(StatefulWidget同理,不貼了)
- 程式碼超級簡單,StatelessWidget抽象了build方法,入參為BuildContext
- createElement()方法例項了StatelessElement類,並將StatelessWidget本身例項傳入
- StatelessElement裡面實現了ComponentElement的build方法:該方法呼叫了widget裡面的build方法,並將本身的例項傳入,流程通了,此處呼叫StatelessWidget的build方法,並傳入了BuildContext的實現類
- ComponentElement的父類中肯定有實現BuildContext,往上看看
abstract class StatelessWidget extends Widget {
const StatelessWidget({ Key? key }) : super(key: key);
@override
StatelessElement createElement() => StatelessElement(this);
@protected
Widget build(BuildContext context);
}
class StatelessElement extends ComponentElement {
StatelessElement(StatelessWidget widget) : super(widget);
@override
StatelessWidget get widget => super.widget as StatelessWidget;
@override
Widget build() => widget.build(this);
@override
void update(StatelessWidget newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_dirty = true;
rebuild();
}
}
複製程式碼
- ComponentElement
- ComponentElement繼承Element,它抽象了一個build方法,StatelessElement實現了這個方法,沒毛病
- 來看看Element
abstract class ComponentElement extends Element {
...
@protected
Widget build();
...
}
複製程式碼
- Element
- Element此處實現了BuildContext,所以繼承他的子類,直接將本身例項傳給BuildContext就OK了
- 如果沒做什麼騷操作,BuildContext可以理解為:每個Widget都有對應的Element( 通過createElement()生成 ),Element是BuildContext實現類
abstract class Element extends DiagnosticableTree implements BuildContext {
...
}
複製程式碼
- Widget
- Widget抽象了一個createElement()方法
- 每個Widget的子類,理應都有自己對應的Element
@immutable
abstract class Widget extends DiagnosticableTree {
const Widget({ this.key });
final Key? key;
@protected
@factory
Element createElement();
...
}
複製程式碼
- 圖示
- 關於Widget和Element再多說倆句
知道為什麼好多文章說Widget對Element是一對多嗎?
首先Widget是Element的一個配置描述,我們通過類似
StatelessElement createElement() => StatelessElement(this)
,將widget本身的配置資訊例項傳入XxxElemen(this)中,然後XxxElement可以通過傳入的Widget配置資訊去生成對應的Element例項大家發現沒?每一個Widget都有對應的Element例項!
假設寫了下面這個Widget
Widget _myWidget({Widget child}){ return Container(width:30, height:30, child:child); } 複製程式碼
- 我們們這樣用
_myWidget( child: Container( child: _myWidget(), ) ) 複製程式碼
這不就對了嘛,只有一份Widget配置資訊,但是會生成倆個Element!
但是還是會有倆個Widget例項,但從配置資訊層次上看,倆個Widget例項的配置資訊都是一樣的,所以是一份配置資訊。。。
所以就有了Widget對Element是一對多的說法;反正我是這樣理解的,僅供參考。。。
可能大佬們寫文章,這些簡單例項腦子自然生成,但是對這些沒啥概念的靚仔,這或許就成了:一條定理或者既定概念
神奇的Provider.of()
為了將上面的流程連線起來,需要一位神奇的魔術師登場,下面就要請上我們的王炸:Provider.of() !
將重新整理元件新增到了InheritedElement中的_dependents變數裡,他到底是怎麼做到的呢?
- Provider.of() :下面就是該方法所有的邏輯,程式碼很少,實現的功能卻很強!
- of方法中,會通過 _inheritedElementOf(context)方法獲取到,和當前Widget距離最近的(往父節點遍歷)繼承InheritedElement的XxxElement
- 上面是通過 _inheritedElementOf(context)方法中的 context.getElementForInheritedWidgetOfExactType()方法去獲取的;繼承InheritedElement的Widget的子節點,是可以通過這個方法去拿到距離他最近的繼承InheritedElement的Widget的XxxElement例項,同樣的,也可以獲取其中儲存的資料
- 你可能想,我拿到 繼承InheritedElement的XxxElement的例項有啥?我們好好想想:我們拿到這個XxxElement例項後,我們不就可以往它的父類InheritedElement裡面的 _dependents的map變數塞值了嗎?狂喜...
- 它是怎麼做到的呢?就是通過這個:context.dependOnInheritedElement(inheritedElement)
static T of<T>(BuildContext context, {bool listen = true}) {
...
final inheritedElement = _inheritedElementOf<T>(context);
if (listen) {
context.dependOnInheritedElement(inheritedElement);
}
return inheritedElement.value;
}
static _InheritedProviderScopeElement<T> _inheritedElementOf<T>(BuildContext context) {
...
_InheritedProviderScopeElement<T>? inheritedElement;
if (context.widget is _InheritedProviderScope<T>) {
context.visitAncestorElements((parent) {
inheritedElement = parent.getElementForInheritedWidgetOfExactType<
_InheritedProviderScope<T>>() as _InheritedProviderScopeElement<T>?;
return false;
});
} else {
inheritedElement = context.getElementForInheritedWidgetOfExactType<
_InheritedProviderScope<T>>() as _InheritedProviderScopeElement<T>?;
}
if (inheritedElement == null) {
throw ProviderNotFoundException(T, context.widget.runtimeType);
}
return inheritedElement!;
}
複製程式碼
- dependOnInheritedElement
- BuildContext中的dependOnInheritedElement方法點進去是個抽象方法,畢竟BuildContext是個純抽象類,方法都沒有邏輯
- 關於BuildContext上面已經說過了,我們直接去Element類裡面找dependOnInheritedElement方法,看看他的實現邏輯
- 直接看最重要的程式碼 ancestor.updateDependencies(this, aspect):我們傳入的繼承了InheritedElement的XxxElement,被傳入了updateDependencies方法,然後他還將當前Widget的Element例項傳入了updateDependencies方法中
abstract class Element extends DiagnosticableTree implements BuildContext {
...
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object? aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies!.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
...
}
複製程式碼
- updateDependencies:流程終於完整的跑通了!
- updateDependencies方法呼叫了setDependencies方法
- setDependencies方法,將子Widget的Element例項賦值給了繼承InheritedElement的類的 _dependents 變數
class InheritedElement extends ProxyElement {
...
@protected
void setDependencies(Element dependent, Object? value) {
_dependents[dependent] = value;
}
@protected
void updateDependencies(Element dependent, Object? aspect) {
setDependencies(dependent, null);
}
...
}
複製程式碼
- 看下圖示:這圖調了好久,不規劃下,線很容易交叉,吐血...
自定義Builder
通過上面的分析,Provider的widget定點重新整理,已經不再神祕了...
學以致用,我們們來整一個自定義Builder!
- 自定義的EasyBuilder控制元件能起到和Consumer一樣的重新整理作用
class EasyBuilder<T> extends StatelessWidget {
const EasyBuilder(
this.builder, {
Key? key,
}) : super(key: key);
final Widget Function() builder;
@override
Widget build(BuildContext context) {
Provider.of<T>(context);
return builder();
}
}
複製程式碼
寫下完整的使用
- view
class CustomBuilderPage extends StatelessWidget {
final provider = CustomBuilderProvider();
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider(
create: (BuildContext context) => provider,
child: _buildPage(),
);
}
Widget _buildPage() {
return Scaffold(
appBar: AppBar(title: Text('Provider-自定義Builder範例')),
body: Center(
child: EasyBuilder<CustomBuilderProvider>(
() => Text(
'點選了 ${provider.count} 次',
style: TextStyle(fontSize: 30.0),
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => provider.increment(),
child: Icon(Icons.add),
),
);
}
}
///自定義Builder
class EasyBuilder<T> extends StatelessWidget {
const EasyBuilder(
this.builder, {
Key? key,
}) : super(key: key);
final Widget Function() builder;
@override
Widget build(BuildContext context) {
Provider.of<T>(context);
return builder();
}
}
複製程式碼
- provider
class CustomBuilderProvider extends ChangeNotifier {
int count = 0;
void increment() {
count++;
notifyListeners();
}
}
複製程式碼
- 效果圖
總結
以上,就將Provider的重新整理機制完整的說完了~~
撒花 ✿✿ヽ(°▽°)ノ✿
如果那裡寫的欠妥,請各位大佬不吝賜教 ~ . ~
手搓一個狀態管理框架
看完Provider的原理後,大家是不是感覺胸中萬千溝壑,腹中萬千才華無法釋放!我們們就來將自己想法統統釋放出來吧!
學以致用,我們們就來按照Provider重新整理機制,手搓一個狀態管理框架。。。
手搓框架就叫:EasyP(後面應該還會接著寫Bloc和GetX;依次叫EasyC,EasyX,省事...),取Provider的頭字母
手搓狀態框架
這個手搓框架做了很多簡化,但是絕對保留了原汁原味的Provider重新整理機制!
- ChangeNotifierEasyP:類比Provider的ChangeNotifierProvider
- 程式碼做了大量的精簡,只保留了provider的重新整理機制的精髓
- 程式碼我就不解釋了,上面的重新整理機制如果看懂了,下面的程式碼很容易理解;如果沒看懂,我解釋下面程式碼也沒用啊。。。
class ChangeNotifierEasyP<T extends ChangeNotifier> extends InheritedWidget {
ChangeNotifierEasyP({
Key? key,
Widget? child,
required this.create,
}) : super(key: key, child: child ?? Container());
final T Function(BuildContext context) create;
@override
bool updateShouldNotify(InheritedWidget oldWidget) => false;
@override
InheritedElement createElement() => EasyPInheritedElement(this);
}
class EasyPInheritedElement<T extends ChangeNotifier> extends InheritedElement {
EasyPInheritedElement(ChangeNotifierEasyP<T> widget) : super(widget);
bool _firstBuild = true;
bool _shouldNotify = false;
late T _value;
late void Function() callBack;
T get value => _value;
@override
void performRebuild() {
if (_firstBuild) {
_firstBuild = false;
_value = (widget as ChangeNotifierEasyP<T>).create(this);
_value.addListener(callBack = () {
// 處理重新整理邏輯,此處無法直接呼叫notifyClients
// 會導致owner!._debugCurrentBuildTarget為null,觸發斷言條件,無法向後執行
_shouldNotify = true;
markNeedsBuild();
});
}
super.performRebuild();
}
@override
Widget build() {
if (_shouldNotify) {
_shouldNotify = false;
notifyClients(widget);
}
return super.build();
}
@override
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
//此處就直接重新整理新增的監聽子Element了,不各種super了
dependent.markNeedsBuild();
// super.notifyDependent(oldWidget, dependent);
}
@override
void unmount() {
_value.removeListener(callBack);
_value.dispose();
super.unmount();
}
}
複製程式碼
- EasyP:類比Provider的Provider類
class EasyP {
/// 獲取EasyP例項
/// 獲取例項的時候,listener引數老是寫錯,這邊直接用倆個方法區分了
static T of<T extends ChangeNotifier>(BuildContext context) {
return _getInheritedElement<T>(context).value;
}
/// 註冊監聽控制元件
static T register<T extends ChangeNotifier>(BuildContext context) {
var element = _getInheritedElement<T>(context);
context.dependOnInheritedElement(element);
return element.value;
}
/// 獲取距離當前Element最近繼承InheritedElement<T>的元件
static EasyPInheritedElement<T>
_getInheritedElement<T extends ChangeNotifier>(BuildContext context) {
var inheritedElement = context
.getElementForInheritedWidgetOfExactType<ChangeNotifierEasyP<T>>()
as EasyPInheritedElement<T>?;
if (inheritedElement == null) {
throw EasyPNotFoundException(T);
}
return inheritedElement;
}
}
class EasyPNotFoundException implements Exception {
EasyPNotFoundException(this.valueType);
final Type valueType;
@override
String toString() => 'Error: Could not find the EasyP<$valueType>';
}
複製程式碼
- build:最後整一個Build類就行了
class EasyPBuilder<T extends ChangeNotifier> extends StatelessWidget {
const EasyPBuilder(
this.builder, {
Key? key,
}) : super(key: key);
final Widget Function() builder;
@override
Widget build(BuildContext context) {
EasyP.register<T>(context);
return builder();
}
}
複製程式碼
大功告成,上面這三個類,就能起到和Provider一樣的區域性重新整理功能!
重新整理機制一模一樣,絕對沒有吹牛皮!
下面來看看怎麼使用吧!
使用
用法基本和Provider一摸一樣...
- view
class CounterEasyPPage extends StatelessWidget {
final easyP = CounterEasyP();
@override
Widget build(BuildContext context) {
return ChangeNotifierEasyP(
create: (BuildContext context) => easyP,
child: _buildPage(),
);
}
Widget _buildPage() {
return Scaffold(
appBar: AppBar(title: Text('自定義狀態管理框架-EasyP範例')),
body: Center(
child: EasyPBuilder<CounterEasyP>(() {
return Text(
'點選了 ${easyP.count} 次',
style: TextStyle(fontSize: 30.0),
);
}),
),
floatingActionButton: FloatingActionButton(
onPressed: () => easyP.increment(),
child: Icon(Icons.add),
),
);
}
}
複製程式碼
- easyP
class CounterEasyP extends ChangeNotifier {
int count = 0;
void increment() {
count++;
notifyListeners();
}
}
複製程式碼
- 效果圖:體驗一下
- 如果網頁打不開,可能需要你清下瀏覽器快取
全域性EasyP
- 全域性也是可以的,直接把ChangeNotifierEasyP類套在主入口,程式碼就不貼了,給大家看下效果圖
總結
如果有靚仔的公司,不想使用第三方狀態管理框架,完全可以參照Provider的重新整理機制,擼一個狀態管理框架出來!我上面已經擼了一個極簡版,畫龍畫虎難畫骨,上面我大致把他的骨架整好了;如果有需要的話,發揮你的聰明才智,copy過去給他填充血肉吧。。。
如果大家看懂了Provider的重新整理機制,就會發現Provider狀態框架,對系統資源佔用極低,它僅僅只使用了ChangeNotifier,這僅僅是最基礎的Callback回撥,這會佔用多少資源?重新整理邏輯全是呼叫Flutte的framework層自帶的那些api(獲取InheritedElement的內部操作很簡單,有興趣可以看看)。。。所以完全不用擔心,他會佔用多少資源,幾乎忽略不計!
最後
一本祕籍
寫完整篇文章,我突然感覺自己掌握一本武功祕籍!知道了怎麼去寫出高階大氣上檔次且深奧的專案!
我現在就來傳授給大家...
- 首先一定要善用面向介面程式設計的思想!
- 如果要想非常深奧,深奧的自己都難以看懂,那直接濫用這種思想就穩了!
- 多用各種設計模式,別和我扯什麼簡單易用,老夫寫程式碼,就是設計模式一把梭,不管合適不合適,全懟上面
- 一定要多用命令模式和訪問者模式,就是要讓自己的函式入參超高度可擴充套件,難以被別人和自己讀懂
- if else內部邏輯直接拋棄,全用策略模式往上懟
- 不管內部狀態閉不閉環,狀態模式直接強行閉環
- for要少用,多用List遍歷,防止別人不懂你的良苦用心,一定在旁註釋:迭代器模式
- 外觀模式,一般都是做一層外觀吧,我們們直接搞倆層,三層外觀類!代理模式五層代理類起步!
- 物件或變數不管是不是隻用一次,我們們全都快取起來,將享元模式的思想貫徹到底
- 變換莫測的就是橋接模式了,一般倆個維度橋接,我們們直接9個維度,俗話說的好,九九八十一難嘛,不是把你繞進去,就是把自己繞起來!頭髮和命,只有一個能活!
- 所有的類與類絕不強耦合,一定要有中介類橋接,別人要噴你;你就自信的往後一仰,淡淡的說:“迪米特法則,瞭解一下。”
- 最重要的,要多用Framework層的回撥
- 不管那個系統回撥我們們懂不懂,都在裡面整點程式碼,假裝很懂
- 最關鍵的時候,系統抽象類要繼承,多寫點自己的抽象方法,千萬不能寫註釋,不然以後自己看懂了,咋辦?
以上純屬調侃
切勿對號入座進Provider,Provider相關思想用的張弛有度,他所抽象的類,實際在多處實現了不同的實現類,大大的增加了擴充套件;而且他所繼承的系統上下文類裡,所抽象的方法,給了非常詳盡的註釋。
從Provider的原始碼上看,能看出Provider的作者絕對是個高手,必須對framework層有足夠了解,才能寫出那樣精彩的重新整理機制!
這是一個很優秀的框架!
我為啥寫上面這些調侃?ε=(´ο`*)))唉,前人練手,後人抓頭。。。
相關地址
- 文章中Demo的Github地址:flutter_use
- Web效果:cnad666.github.io/flutter_use…
- 如果provider相關功能按鈕沒看到,可能需要你清下瀏覽器快取
- Windows:Windows平臺安裝包
- 密碼:xdd666
系列文章