接前一篇文章 Flutter狀態管理Provider(一) 簡易使用
過程分析
State之setState
我們debug上面示例的程式碼。進入setState
void setState(VoidCallback fn) {
final dynamic result = fn() as dynamic;
_element.markNeedsBuild();///這一步Element
}
複製程式碼
我們進入Element
void markNeedsBuild() {
if (!_active) return;
if (dirty) return;
_dirty = true;
owner.scheduleBuildFor(this); ///BuildOwner
}
複製程式碼
BuildOwner.scheduleBuildFor: 把當前element加入到dirty列表。drawFrame重新構建dirty element
/// Adds an element to the dirty elements list
/// so that it will be rebuilt
/// when [WidgetsBinding.drawFrame] calls [buildScope].
void scheduleBuildFor(Element element) {
if (element._inDirtyList) {
return true;
}());
if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
_scheduledFlushDirtyElements = true;
onBuildScheduled();
}
_dirtyElements.add(element);
element._inDirtyList = true;
}
複製程式碼
重新構建的呼叫棧是這樣的。
--> onBuildScheduled() (非必須)
···
--> SchedulerBinding.scheduleFrame()
···
--> WidgetsBinding.drawFrame()
--> BuildOwner.buildScope()
--> Element.rebuild()
--> ComponentElement.performBuild()
--> StatefulElement.build()/updateChild(_child, built, slot)
--> State.build()
複製程式碼
更多細節可以參考 文章
InheritedWidget
setState 通過 Element.markNeedsBuild(),實現了無腦rebuild一切。接下來是InheritedWidget,關注兩個問題:
- 如何共享資料?
- 如何控制rebuild?
首先要從InheritedWidget的使用入手
////CountProvider繼承InheritedWidget,儲存我們的資料
class CountProvider extends InheritedWidget {
final int count;
CountProvider({Key key, this.count, Widget child})
: super(key: key, child: child);
@override
bool updateShouldNotify(CountProvider old) {
return true;
}
}
//拿到CountProvider例項
CountProvider provider1 = BuildContext.getElementForInheritedWidgetOfExactType<CountProvider>().widget;
var count=provider1.count;
複製程式碼
BuildContext物件開始有段註釋說明
[BuildContext] objects are actually [Element] objects. The [BuildContext] interface is used to discourage direct manipulation of [Element] objects.
BuildContext物件 其實就是Element物件。BuildContext介面是用來阻止對Element物件的直接訪問。顯而易見,Element是實現細節,系統不希望我們直接操作Element。
InheritedWidget教會我們,有兩種方式獲取InheritedWidget。1.12.13 替換了1.6的老方法,更加明確了這兩個方法的字面意思。
///第一種,拿到InheritedElement獲取它的InheritedWidget
InheritedElement Element.getElementForInheritedWidgetOfExactType<T extends InheritedWidget>()
///第二種
T Element.dependOnInheritedWidgetOfExactType<T extends InheritedWidget>();
複製程式碼
InheritedElement是啥?我們進入到InheritedWidget。
InheritedWidget告訴我們兩個事情:
- 對應Element是InheritedElement。那重點就在InheritedElement裡面了。
- 解釋updateShouldNotify,大概意思呢,當InheritedWidget rebuilt時,我們並不總是需要rebuilt 依賴它的widget。需要或者不需要,這取決於你的updateShouldNotify方法。
abstract class InheritedWidget extends ProxyWidget {
const InheritedWidget({ Key key, Widget child })
: super(key: key, child: child);
@override
InheritedElement createElement() => InheritedElement(this);
@protected
bool updateShouldNotify(covariant InheritedWidget oldWidget);
}
複製程式碼
接下來我們進入第一種
class Element{
Map<Type, InheritedElement> _inheritedWidgets;
@override
InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
///key為T的Type,從_inheritedWidgets中獲取 InheritedElement
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
return ancestor;
}
}
///我們發現_updateInheritance() 修改了_inheritedWidgets
void _updateInheritance() {
assert(_active);
_inheritedWidgets = _parent?._inheritedWidgets;
}
///而_updateInheritance()是在mount()或者active()中呼叫
///mount()是首次把element新增到element Tree,然後 element是active狀態
///active()是把deactivate element ,重新標記為active狀態
///總之是從無到有或者從不可見到可見。
@mustCallSuper
void mount(Element parent, dynamic newSlot) {
_parent = parent;
_slot = newSlot;
_depth = _parent != null ? _parent.depth + 1 : 1;
_active = true;
_updateInheritance();
}
@mustCallSuper
void activate() {
_active = true;
_dependencies?.clear();
_hadUnsatisfiedDependencies = false;
_updateInheritance();
}
複製程式碼
InheritedWidget對應的是 InheritedElement。它過載了_updateInheritance方法,把自身加了進去,也是可以想象到的操作。
class InheritedElement{
@override
void _updateInheritance() {
assert(_active);
final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets;
if (incomingWidgets != null)
_inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
else
_inheritedWidgets = HashMap<Type, InheritedElement>();
_inheritedWidgets[widget.runtimeType] = this;
}
}
複製程式碼
回過頭來看,其實也挺簡單的。當前Element會繼承(儲存)parent的InheritedElement。所以不管Element 與InheritedElement相隔多少層,只要是childElement,都是可以傳遞過來的。
我們回答了第一個問題--如何共享資料。
現在我們進入到dependOnInheritedWidgetOfExactType
class Element {
@override
T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
///這段操作與getElementForInheritedWidgetOfExactType一模一樣。
final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
///新增依賴。
if (ancestor != null) {
assert(ancestor is InheritedElement);
return dependOnInheritedElement(ancestor, aspect: aspect);
}
_hadUnsatisfiedDependencies = true;
return null;
}
@override
InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
assert(ancestor != null);
_dependencies ??= HashSet<InheritedElement>();
_dependencies.add(ancestor);
ancestor.updateDependencies(this, aspect);
return ancestor.widget;
}
}
複製程式碼
我們再進入ancestor.updateDependencies,幹了哪些事情。
class InheritedElement{
@protected
void updateDependencies(Element dependent, Object aspect) {
setDependencies(dependent, null);
}
@protected
void setDependencies(Element dependent, Object value) {
_dependents[dependent] = value;
}
}
複製程式碼
總結下來就是當前Element和InheritedElement互相儲存下彼此的依賴關係。
我們回憶下上篇中自定義的ChangeNotifierProvider。
class ChangeNotifierProvider extends StatefulWidget {
final Widget child;
final CountModel model;
...
}
class _ChangeNotifierProviderState extends State<ChangeNotifierProvider> {
_ChangeNotifierProviderState();
_update() {
setState(() => {});
}
@override
void initState() {
super.initState();
widget.model.addListener(_update);
}
....
///在此處build內debug
@override
Widget build(BuildContext context) {
return CountProvider(
model: widget.model,
child: widget.child,
);
}
}
複製程式碼
但點選increment按鈕時,
--> ChangeNotifier._update()
--> ChangeNotifierProvider.setState()
--> ...
--> Element.rebuild()
--> ComponentElement.performBuild()
--> StatefulElement.build()/updateChild
--> ChangeNotifierProvider.build(BuildContext)
複製程式碼
我們進入ComponentElement
class ComponentElement{
///ChangeNotifierProvider對應的ComponentElement performRebuild
@override
void performRebuild() {
///呼叫_ChangeNotifierProviderState.build(),生成新的CountProvider
Widget built= build();
_child = updateChild(_child, built, slot);
}
}
///此時 Element是InheritedElement,newWidget是我們新構建CountProvider
@protected
Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
///newWidget為null,則讓child失活不可見,並不會清除。
if (newWidget == null) {
if (child != null)
deactivateChild(child);
return null;
}
if (child != null) {
///新老CountProvider是同一個物件,處理下slot,及可見性
///這個地方也告訴我們CountProvider的child每次都new一個物件,也會全部rebuilt
if (child.widget == newWidget) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
return child;
}
if (Widget.canUpdate(child.widget, newWidget)) {
if (child.slot != newSlot)
updateSlotForChild(child, newSlot);
///*只有這裡才會進入我們的InheritedElement
child.update(newWidget);
return child;
}
deactivateChild(child);
}
return inflateWidget(newWidget, newSlot);
}
複製程式碼
接著上述的child.update(newWidget),先進入了ProxyElement
class ProxyElement{
@override
void update(ProxyWidget newWidget) {
final ProxyWidget oldWidget = widget;
super.update(newWidget);
assert(widget == newWidget);
updated(oldWidget);
_dirty = true;
rebuild();
}
@protected
void updated(covariant ProxyWidget oldWidget) {
notifyClients(oldWidget);
}
}
複製程式碼
InheritedElement過載了updated()方法。同時實現了notifyClients()我們發現這個地方留了一手, 正是需要我們實現的updateShouldNotify
class InheritedElement{
@override
void updated(InheritedWidget oldWidget) {
if (widget.updateShouldNotify(oldWidget))
super.updated(oldWidget);
}
@override
void notifyClients(InheritedWidget oldWidget) {
///_dependents中儲存的都是
///通過dependOnInheritedWidgetOfExactType產生依賴的子孫Element
for (Element dependent in _dependents.keys) {
notifyDependent(oldWidget, dependent);
}
}
@protected
void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
///最終呼叫dependent的markNeedsBuild();
dependent.didChangeDependencies();
}
}
複製程式碼
到這裡我們算是把方法的呼叫棧說明白了。也對InheritedWidget的內部原理大概有了認識。 其實就是觀察者模式:註冊監聽,通知更新的邏輯。還有更多細節還在繼續學習中。
這些內容,足夠讓我們進入到Provider的使用過程中。
Provider
這個地方我們挑常用的ChangeNotifierProvider分析 回顧程式碼
class CountModel extends ChangeNotifier {
int count;
CountModel(this.count);
void increment() {
count++;
notifyListeners();
}
}
ChangeNotifierProvider.value(
value: _countModel,
child: Column(
children: <Widget>[
Consumer<CountModel>(
builder: (contextC, model, child) {
return Text("計數:${model.count}(有依賴情況)");
},
),
Builder(builder: (context1) {
return Text(
"計數:${Provider.of<CountModel>(context1, listen: false).count}(無依賴情況)");
}),
RaisedButton(
child: Text("increment"),
onPressed: () => _countModel.increment()),
],
)
複製程式碼