--- title: 'State 生命週期' disqus: kyleAlien --- State 生命週期 === ## OverView of Content [TOC] ## State 現在來看看 StatefulWidget#createState 方法,它會回傳一個 State,而這個 State 又如何與 Element 產生關係的 > ![](https://i.imgur.com/WMvVdjy.png) ### BuildContext & Element 關係 * BuildContext 實際上就是 Element 對象,它會將 Element 保護起來不讓使用者直接使用 ```java= abstract class Element extends DiagnosticableTree implements BuildContext { ... 省略 Element(Widget widget) : assert(widget != null), _widget = widget; Element? _parent; } abstract class BuildContext { /// The current configuration of the [Element] that is this [BuildContext]. Widget get widget; /// The [BuildOwner] for this context. The [BuildOwner] is in charge of /// managing the rendering pipeline for this context. BuildOwner? get owner; ... 省略 // 數據共享的方法 T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object? aspect }); } ``` > ![](https://i.imgur.com/rXEph1u.png) ### State & Element 關係 * 在 StatefulWidget 被 inflateWidget 調用時會呼叫 createElement,創建對應的 Element 物件 (**也就是 StatefulElement**) ```java= // framework abstract class StatefulWidget extends Widget { const StatefulWidget({ Key? key }) : super(key: key); // 返回 StatefulElement 物件 @override StatefulElement createElement() => StatefulElement(this); @factory State createState(); } ``` * 接下來關注 StatefulElement 物件,看它的建構函數就會看到,它會讓 ^1^ **state 持有 element**、^2^ **state 持有 Widget** ```java= // framework class StatefulElement extends ComponentElement { final State<StatefulWidget> state; StatefulElement(StatefulWidget widget) : state = widget.createState(), super(widget) { ... 斷言 state._element = this; // state 持有 element ... 斷言 state._widget = widget; // state 持有 Widget } ... } ``` > ![](https://i.imgur.com/hl7kbRF.png) ### State 生命週期 * 在 Flutter 框架中 State 類暴露了 7 種生命週期函數 | 函數名 | 調用時機 | 其他 | | -------- | -------- | -------- | | initState | 建構 | initialzed | | didChangeDependencies | 建構 | initialzed | | build | **建構 & 熱重載** | ready | | reassemble | **熱重載** | ready,release 並不會被調用 | | didUpdateWidget | **熱重載** | ready | | deactivate | 非激活 | ready | | dispose | 死亡 | defunct 不再有 build 能力 | * 當 StatefulWidget 掛載成功後 (執行 mount 方法,**並且 mount 方法只會執行一次**),就會接著執行 \_firstBuild 方法,這些方法就會使用到 initState、didChangeDependencies ```java= // framework class StatefulElement extends ComponentElement { @override void _firstBuild() { assert(state._debugLifecycleState == _StateLifecycle.created); try { _debugSetAllowIgnoredCallsToMarkNeedsBuild(true); // 調用生命週期 initState final Object? debugCheckForReturnedFuture = state.initState() as dynamic; ... 斷言 } finally { _debugSetAllowIgnoredCallsToMarkNeedsBuild(false); } ... 斷言 state.didChangeDependencies(); // 調用 生命週期 didChangeDependencies super._firstBuild(); // 執行父類方法,並且最終會調用到 生命週期 build 方法 } } abstract class State<T extends StatefulWidget> with Diagnosticable { @protected @mustCallSuper void initState() { assert(_debugLifecycleState == _StateLifecycle.created); } @protected @mustCallSuper void didChangeDependencies() { } } abstract class ComponentElement extends Element { @override void mount(Element? parent, Object? newSlot) { super.mount(parent, newSlot); assert(_child == null); assert(_lifecycleState == _ElementLifecycle.active); _firstBuild(); assert(_child != null); } // StatefulElement 會 Override 這個方法 void _firstBuild() { rebuild(); } } ``` > ![](https://i.imgur.com/UCR4b96.png) * **updateChild 函數最終會調用 didUpdateWidget 函數** ```java= // framework abstract class Element extends DiagnosticableTree implements BuildContext { Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) { ... 省略部分 if (child != null) { if (hasSameSuperclass && child.widget == newWidget) { ... 省略 } else if(hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) { // 執行 StatefulElement#update 方法 child.update(newWidget); // 該 update 方法就會執行 didUpdateChild } else { deactivateChild(child); // 移除 View newChild = inflateWidget(newWidget, newSlot); // 重新加載 } } ... 省略部分 } } abstract class Widget extends DiagnosticableTree { static bool canUpdate(Widget oldWidget, Widget newWidget) { return oldWidget.runtimeType == newWidget.runtimeType && oldWidget.key == newWidget.key; } } class StatefulElement extends ComponentElement { @override void update(StatefulWidget newWidget) { super.update(newWidget); assert(widget == newWidget); final StatefulWidget oldWidget = state._widget!; _dirty = true; state._widget = widget as StatefulWidget; try { _debugSetAllowIgnoredCallsToMarkNeedsBuild(true); // 調用生命週期 didUpdateWidget !!! final Object? debugCheckForReturnedFuture = state.didUpdateWidget(oldWidget) as dynamic; ... 斷言 } finally { _debugSetAllowIgnoredCallsToMarkNeedsBuild(false); } rebuild(); // 最終會在觸發 build 方法 } } ``` > ![](https://i.imgur.com/vSKhxtn.png) ### StatefulWidget 嵌套 StatefulWidget * 觀察 **兩個 State 之間的相互作用**,把範例程式的 FloatingActionButton 抽出來,另外寫成一個 StatefulWidget ```java= class FloatingBtn extends StatefulWidget { @override State<StatefulWidget> createState() => _FloatingBtnState(); } class _FloatingBtnState extends State<FloatingBtn> { int _counter = 0; void _incrementCounter() { setState(() { _counter++; }); } @override Widget build(BuildContext context) { return FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ); } // 省略其他生命週期函數 } ``` * **初始 - 建構順序**: MyHomePage 先建構,之後在建構 FloatingBtn > ![](https://i.imgur.com/4lJlHDp.png) * **熱重載 (ctrl + s)**: MyHomePage(父) 會先觸發 build 方法,接著才會觸發 FloatingBtn#didUpdateWidget 方法 > ![](https://i.imgur.com/uVdL7xg.png) ```java= // framework abstract class ComponentElement extends Element { ComponentElement(Widget widget) : super(widget); Element? _child; @override void performRebuild() { Widget? built; try { built = build(); // MyApp 返回 MaterialApp,透過這個來切換下一個 Widget ... } // 省略 catch、finally try { // 更新 Child _child = updateChild(_child, built, slot); assert(_child != null); } catch (e, stack) { ... } ... } } abstract class Element extends DiagnosticableTree implements BuildContext { // 有四種可能性,當前情況就是 有 child,接這就看是否 canUpdate Element? updateChild(Element? child, Widget? newWidget, Object? newSlot) { ... final Element newChild; if (child != null) { // 4. 該元素的 Child 不為空 bool hasSameSuperclass = true; ... 斷言 if (hasSameSuperclass && child.widget == newWidget) // 創建出來的 Widget 和自身的 Child 相同 if (child.slot != newSlot) updateSlotForChild(child, newSlot); newChild = child; // 返回自己的 Child (省去加載) } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) { // cnaUpdate 重點 if (child.slot != newSlot) updateSlotForChild(child, newSlot); // 以這個案例來說就是走這裡 child.update(newWidget); ... 斷言 newChild = child; } else { deactivateChild(child); ... 斷言 newChild = inflateWidget(newWidget, newSlot); } } else { newChild = inflateWidget(newWidget, newSlot); // 3. inflateWidget } ... 斷言 return newChild; } } abstract class Widget extends DiagnosticableTree { /// Initializes [key] for subclasses. const Widget({ this.key }); // 檢查兩項條件 static bool canUpdate(Widget oldWidget, Widget newWidget) { return oldWidget.runtimeType == newWidget.runtimeType && oldWidget.key == newWidget.key; } } ``` ## State 切換 & 跳轉 - 生命週期 1. 切換: 使用 Tab view 在同一個頁面跳轉 2. 跳轉: 使用 Navigator.push 跳轉頁面 ### 切換 Tab & 熱重載 * 寫一個簡單的 TabView,觀察當切換不同 Tab 時,StatefulWidget 的生命週期有何變化 ```java= import 'package:flutter/material.dart'; class SimpleTab extends StatelessWidget { static const TABS = ["GREEN", "YELLOW"]; @override Widget build(BuildContext context) { var tabBar = TabBar( isScrollable: true, labelColor: Colors.white, unselectedLabelColor: Colors.grey, tabs: TABS.map((e) => Container( child: Text(e), )).toList() ); var tabBarView = TabBarView( children: TABS.map((e) => _buildContext(TABS.indexOf(e))).toList() ); return MaterialApp( home: DefaultTabController( length: TABS.length, child: Scaffold( appBar: AppBar( title: Text('State lifecycle'), bottom: tabBar, ), body: tabBarView, ), ), ); } Widget _buildContext(int index) { Color color = Colors.white; Widget context = Container(); switch(index) { case 0: color = Colors.green; context = GreenPage(); break; case 1: color = Colors.yellow; context = YellowPage(); break; } Widget view = Container( padding: EdgeInsets.only(top: 10), alignment: Alignment.center, color: color, child: context, ); return view; } } class YellowPage extends StatefulWidget { @override State<StatefulWidget> createState() => _YellowPageState(); } class _YellowPageState extends State<YellowPage> { @override Widget build(BuildContext context) { print('YellowPage --- build'); return Text('Yellow Page'); } @override void initState() { super.initState(); print('YellowPage --- initState'); } @override void didChangeDependencies() { super.didChangeDependencies(); print('YellowPage --- didChangeDependencies'); } @override void didUpdateWidget(YellowPage oldWidget) { super.didUpdateWidget(oldWidget); print('YellowPage --- didUpdateWidget'); } @override void reassemble() { super.reassemble(); print('YellowPage --- reassemble'); } @override void deactivate() { super.deactivate(); print('YellowPage --- deactivate'); } @override void dispose() { super.dispose(); print('YellowPage --- dispose'); } } class GreenPage extends StatefulWidget { @override State<StatefulWidget> createState() => _GreenPageState(); } class _GreenPageState extends State<GreenPage> { @override Widget build(BuildContext context) { print('GreenPage --- build'); return Text('Green Page'); } @override void initState() { super.initState(); print('GreenPage --- initState'); } @override void didChangeDependencies() { super.didChangeDependencies(); print('GreenPage --- didChangeDependencies'); } @override void didUpdateWidget(GreenPage oldWidget) { super.didUpdateWidget(oldWidget); print('GreenPage --- didUpdateWidget'); } @override void reassemble() { super.reassemble(); print('GreenPage --- reassemble'); } @override void deactivate() { super.deactivate(); print('GreenPage --- deactivate'); } @override void dispose() { super.dispose(); print('GreenPage --- dispose'); } } ``` 1. 初始化會先建構第一個畫面 (GreenPage) ,^1^ initState、^2^ didChangeDependencies、^3^ build > ![](https://i.imgur.com/96N55nI.png) 2. GreenPage -> YellowPage: 會先初始化 & 建構 Yellow 頁面,之後才會釋放 Green 頁面 > ![](https://i.imgur.com/uNvtYB5.png) 3. 整體切換的生命週期 > ![](https://i.imgur.com/bGKm2Aq.png) ### 跳轉 Page - Navigator * 使用上面的 Simple Tab 頁面,當點擊 Change Page 時就透過 Navigator 轉跳到該頁面 ```java= class MyHomePage extends StatefulWidget { MyHomePage({Key? key, required this.title}) : super(key: key); final String title; @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int _counter = 0; void _incrementCounter() { // 更新畫面 setState(() { _counter++; }); } @override Widget build(BuildContext context) { print('MyHomePage --- build'); return Scaffold( appBar: AppBar( title: Text(widget.title), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ Text( 'You have pushed the button this many times:', ), Text( '$_counter', ), TextButton( // 跳轉到 SimpleTab 頁面 onPressed: () { Navigator.push(context, MaterialPageRoute(builder: (context) => SimpleTab())); }, child: Text('Change Page'), ), ], ), ), floatingActionButton: FloatingActionButton( onPressed: _incrementCounter, tooltip: 'Increment', child: Icon(Icons.add), ), ); } @override void initState() { print('MyHomePage --- initState'); super.initState(); } @override void didChangeDependencies() { print('MyHomePage --- didChangeDependencies'); super.didChangeDependencies(); } @override void didUpdateWidget(MyHomePage oldWidget) { print('MyHomePage --- didUpdateWidget'); super.didUpdateWidget(oldWidget); } @override void reassemble() { print('MyHomePage --- reassemble'); super.reassemble(); } @override void deactivate() { print('MyHomePage --- deactivate'); super.deactivate(); } @override void dispose() { print('MyHomePage --- dispose'); super.dispose(); } } ``` * 先點擊 5 次,每次點擊時都會觸發 **setState 函數**,透過該函數會觸發到 build 生命週期,使其重新執行 build 函數 > ![](https://i.imgur.com/Wm9KCPP.png) * 跳轉頁面到 SimpleTab Page,會看到**原本的頁面 (MyHomePage) 並不會 deactivate、dispose** > ![](https://i.imgur.com/0gww0mQ.png) ## setState 一開始我們在刷新畫面時,最常使用到的就是 setState 函數 ```java= class _MyHomePageState extends State<MyHomePage> { int _counter = 0; void _incrementCounter() { // 刷新畫面 setState(() { _counter++; }); } ... 省略 } ``` setState 是 State 類中的函數 ```java= // framewrok abstract class State<T extends StatefulWidget> with Diagnosticable { @protected void setState(VoidCallback fn) { ... } } ``` ### setState - 標記 dirty * setState 函數接收一個 VoidCallback,^1^ **引數不可為空**、^2^ **引數回傳不可以是 Future**、^3^ 最後會呼叫 markNeedsBuild 函數 ```java= // framewrok abstract class State<T extends StatefulWidget> with Diagnosticable { StatefulElement? _element; @protected void setState(VoidCallback fn) { assert(fn != null); // 1. 斷言非空 ... final Object? result = fn() as dynamic; assert(() { // 2. 判斷回傳值不能是 Future if (result is Future) { throw FlutterError.fromParts(<DiagnosticsNode>[ ... ]); } return true; }()); _element!.markNeedsBuild(); // 3. 將該元素標記為需要創建 } } ``` * markNeedsBuild 函數主要就是 ^1^ 判斷生命週期、^2^ 並將該元件設定為需要更新的狀態 (設為  dirty)、^3^ 最後呼叫 BuildOwner#scheduleBuildFor 函數 ```java= // framewrok abstract class Element extends DiagnosticableTree implements BuildContext { BuildOwner? get owner => _owner; BuildOwner? _owner; void markNeedsBuild() { // 1. 對該元件的生命週期進行判斷 assert(_lifecycleState != _ElementLifecycle.defunct); // 該元件仍活著 if (_lifecycleState != _ElementLifecycle.active) // 如果不處於 active 則忽略不管 return; assert(owner != null); assert(_lifecycleState == _ElementLifecycle.active); ... 省略 if (dirty) return; _dirty = true; // 2. 標記為需要更新 owner!.scheduleBuildFor(this); // 3. 呼叫 BuildOwner#scheduleBuildFor 函數 } } class BuildOwner { VoidCallback? onBuildScheduled; bool _scheduledFlushDirtyElements = false; final List<Element> _dirtyElements = <Element>[]; /// 這個註解很重要 !!! // /// Adds an element to the dirty elements list so that it will be rebuilt /// when [WidgetsBinding.drawFrame] calls [buildScope]. void scheduleBuildFor(Element element) { assert(element != null); assert(element.owner == this); ... 省略 if (!_scheduledFlushDirtyElements && onBuildScheduled != null) { _scheduledFlushDirtyElements = true; onBuildScheduled!(); // 執行 Callback 回調 } _dirtyElements.add(element); // 添加進需要更新的列表 element._inDirtyList = true; ... 省略 } } ``` > ![](https://i.imgur.com/hTOcd7Z.png) :::info * scheduleBuildFor 的註解 由於之後會觸發到 Native 函數,但目前先不接觸 Native 函數,所以看這個註解可以知道,**當使用 scheduleBuildFor 後最 ==終會呼叫到 WidgetsBinding.drawFrame 函數==** ::: ### setState - onBuildScheduled 回調 (Native) * 透過下斷點進入 Debug 模式,可以看到它執行到 WidgetsBinding#\_handleBuildScheduled 函數 > ![](https://i.imgur.com/TooYsux.png) * WidgetBinding 綁定請看另外一篇 [**文章**](https://hackmd.io/2Mh_c4clRhyAW7OOw9Eh_w?view#%E5%89%B5%E5%BB%BA-WidgetsFlutterBinding---ensureInitialized),這個類會在一開始建構三棵樹時就被綁定 ```java= // widgets\binding.dart mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding { void _handleBuildScheduled() { ... 省略斷言 ensureVisualUpdate(); } } ``` * 最後會觸發到 **scheduleFrame 方法 (native)**,目前先分析到這裡,不繼續往下 ```java= // scheduler\binding.dart mixin SchedulerBinding on BindingBase SchedulerPhase get schedulerPhase => _schedulerPhase; SchedulerPhase _schedulerPhase = SchedulerPhase.idle; void ensureVisualUpdate() { switch (schedulerPhase) { case SchedulerPhase.idle: case SchedulerPhase.postFrameCallbacks: scheduleFrame(); // 走這裡 return; case SchedulerPhase.transientCallbacks: case SchedulerPhase.midFrameMicrotasks: case SchedulerPhase.persistentCallbacks: return; } } ui.SingletonFlutterWindow get window => ui.window; void scheduleFrame() { if (_hasScheduledFrame || !framesEnabled) return; ... 省略斷言 ensureFrameCallbacksRegistered(); window.scheduleFrame(); _hasScheduledFrame = true; } } class SingletonFlutterWindow extends FlutterWindow { void scheduleFrame() => platformDispatcher.scheduleFrame(); // 透過 native 渲染 void scheduleFrame() native 'PlatformConfiguration_scheduleFrame'; } ``` > ![](https://i.imgur.com/1ev3UPZ.png) ### WidgetsBinding.drawFrame - 觸發 build * 透過 scheduleBuildFor 的註解可以知道,**使用 scheduleBuildFor 後最 ==終會呼叫到 WidgetsBinding.drawFrame 函數==**,^1^ 經過遍歷 dirty 列表 rebuild 後、^2^ 會刷新整個頁面 ```java= // widgets\binding mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding { @override void drawFrame() { ... 省略部分 try { if (renderViewElement != null) buildOwner!.buildScope(renderViewElement!); // 1. 依照 dirty 列表 rebuild // 呼叫到 RendererBinding#drawFrame super.drawFrame(); // 2. flush layout、paint、compositingBit .. buildOwner!.finalizeTree(); } finally { assert(() { debugBuildingDirtyElements = false; return true; }()); } ... 省略部分 } // framework class BuildOwner { void buildScope(Element context, [ VoidCallback? callback ]) { ... 省略部分 while (index < dirtyCount) { ... 省略斷言 try { _dirtyElements[index].rebuild(); // Element#rebuild 函數,最終會呼叫到 build 函數 } catch (e, stack) { ... } index += 1; if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting!) { _dirtyElements.sort(Element._sort); // 重新排序,dirty list _dirtyElementsNeedsResorting = false; dirtyCount = _dirtyElements.length; while (index > 0 && _dirtyElements[index - 1].dirty) { index -= 1; } } } } } // rendering\binding.dart mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable { @protected void drawFrame() { // 刷新各種 View assert(renderView != null); pipelineOwner.flushLayout(); pipelineOwner.flushCompositingBits(); pipelineOwner.flushPaint(); if (sendFramesToEngine) { renderView.compositeFrame(); // this sends the bits to the GPU pipelineOwner.flushSemantics(); // this also sends the semantics to the OS. _firstFrameSent = true; } } } ``` > ![](https://i.imgur.com/Ow4y1Wo.png) ### RendererBinding 重繪製 * WidgetsBinding#drawFrame 觸發的第二個方法最終會到達 RendererBinding#drawFrame 函數 ```java= // widgets\binding mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding { @override void drawFrame() { ... 省略部分 try { if (renderViewElement != null) buildOwner!.buildScope(renderViewElement!); // 1. 依照 dirty 列表 rebuild // 呼叫到 RendererBinding#drawFrame super.drawFrame(); // 2. flush layout、paint、compositingBit .. buildOwner!.finalizeTree(); } finally { assert(() { debugBuildingDirtyElements = false; return true; }()); } ... 省略部分 } // rendering\binding.dart mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable { PipelineOwner get pipelineOwner => _pipelineOwner; late PipelineOwner _pipelineOwner; @protected void drawFrame() { // 刷新各種 View assert(renderView != null); pipelineOwner.flushLayout(); pipelineOwner.flushCompositingBits(); pipelineOwner.flushPaint(); if (sendFramesToEngine) { renderView.compositeFrame(); // this sends the bits to the GPU pipelineOwner.flushSemantics(); // this also sends the semantics to the OS. _firstFrameSent = true; } } } ``` * 這裡分三個步驟,flushLayout、flushCompositingBits、flushPaint 1. **flushLayout**: 最終會呼叫到 RenderObject#performLayout,**重新布局** ```java= // rendering\object class PipelineOwner { List<RenderObject> _nodesNeedingLayout = <RenderObject>[]; void flushLayout() { ... 省略部分 try { while (_nodesNeedingLayout.isNotEmpty) { final List<RenderObject> dirtyNodes = _nodesNeedingLayout; _nodesNeedingLayout = <RenderObject>[]; // 按照 view 的深度調整 Render Object List for (final RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => a.depth - b.depth)) { if (node._needsLayout && node.owner == this) node._layoutWithoutResize(); // 走這裡 } } } finally { ... 省略部分 } } abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget { void _layoutWithoutResize() { ... 省略斷言 try { performLayout(); markNeedsSemanticsUpdate(); } catch (e, stack) { _debugReportException('performLayout', e, stack); } ... 省略斷言 _needsLayout = false; markNeedsPaint(); } void performLayout(); } ``` > ![](https://i.imgur.com/haduJ6M.png) 2. flushCompositingBits: 最終會呼叫到 RenderObject#\_updateCompositingBits 方法 ```java= // rendering\object class PipelineOwner { final List<RenderObject> _nodesNeedingCompositingBitsUpdate = <RenderObject>[]; void flushCompositingBits() { if (!kReleaseMode) { Timeline.startSync('Compositing bits'); } // 按照 view 的深度調整 Render Object List _nodesNeedingCompositingBitsUpdate.sort((RenderObject a, RenderObject b) => a.depth - b.depth); for (final RenderObject node in _nodesNeedingCompositingBitsUpdate) { if (node._needsCompositingBitsUpdate && node.owner == this) node._updateCompositingBits(); } _nodesNeedingCompositingBitsUpdate.clear(); if (!kReleaseMode) { Timeline.finishSync(); } } } abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget { void _updateCompositingBits() { if (!_needsCompositingBitsUpdate) return; final bool oldNeedsCompositing = _needsCompositing; _needsCompositing = false; visitChildren((RenderObject child) { // 更新子類的 _updateCompositingBits child._updateCompositingBits(); if (child.needsCompositing) _needsCompositing = true; }); ... 省略部分 } void visitChildren(RenderObjectVisitor visitor) { } } ``` > ![](https://i.imgur.com/GvZh4xx.png) 3. flushPaint: 重新繪製,最終會呼叫到 RenderObject#Paint 方法 ```java= // rendering\object class PipelineOwner { void flushPaint() { ... 省略部分 try { final List<RenderObject> dirtyNodes = _nodesNeedingPaint; _nodesNeedingPaint = <RenderObject>[]; // Sort the dirty nodes in reverse order (deepest first). for (final RenderObject node in dirtyNodes..sort((RenderObject a, RenderObject b) => b.depth - a.depth)) { assert(node._layer != null); if (node._needsPaint && node.owner == this) { if (node._layer!.attached) { PaintingContext.repaintCompositedChild(node); // 重新繪製 } else { node._skippedPaintingOnLayer(); } } } assert(_nodesNeedingPaint.isEmpty); } finally { ... 省略部分 } } } class PaintingContext extends ClipContext { static void repaintCompositedChild(RenderObject child, { bool debugAlsoPaintedParent = false }) { assert(child._needsPaint); _repaintCompositedChild( child, debugAlsoPaintedParent: debugAlsoPaintedParent, ); } static void _repaintCompositedChild( RenderObject child, { bool debugAlsoPaintedParent = false, PaintingContext? childContext, }) { ... 省略部分 childContext ??= PaintingContext(child._layer!, child.paintBounds); child._paintWithContext(childContext, Offset.zero); ... 省略部分 } } abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin implements HitTestTarget { void _paintWithContext(PaintingContext context, Offset offset) { ... 省略部分 if (_needsLayout) return; try { paint(context, offset); // 重新繪製 ... 省略斷言 } catch (e, stack) { _debugReportException('paint', e, stack); } ... 省略部分 } void paint(PaintingContext context, Offset offset) { } } ``` > ![](https://i.imgur.com/qhQ2usN.png) ## Appendix & FAQ :::info flushCompositingBits 的功能 ? ::: ###### tags: `Flutter`