終于放假啦金闽,不用一直寫業(yè)務代碼了条摸,也終于有點時間可以整理整理筆記啦。
我在這里先給大家拜個早年覆旭,恭祝大家新春快樂退子,吉祥安康啦!
拜年.gif
Flutter系列學習筆記
- Flutter筆記——runApp發(fā)生了什么(源碼學習)
- Flutter筆記——State.setState發(fā)生了什么(源碼學習)
- 用Dart寫的身份證號校驗代碼(可用于flutter項目)
- HelloDart-構造函數(shù)特性
- HelloDart-MixIn型将,土話記錄多繼承機制
- Flutter筆記——MethodChannel(Native&Flutter數(shù)據(jù)交互)
- Flutter筆記——FlutterActivity
1 State.setState
見該函數(shù)描述:
Notify the framework that the internal state of this object has changed.
Whenever you change the internal state of a State object, make the change in a function that you pass to setState.
通知Framework層:該State對象的內(nèi)部狀態(tài)發(fā)生了變化寂祥。無論你何時改變一個State對象的內(nèi)部狀態(tài),請在傳遞給setState函數(shù)中做出修改七兜。
在開發(fā)中我們通過控制setState
對當前Widget做出修改壤靶,讓UI元素重繪,過程見下面序列圖
setState序列圖.png
注釋部分不清楚見下文:
- 斷言傳遞函數(shù)不為null惊搏;
當前state的狀態(tài)判斷贮乳,參見_StateLifecycle枚舉和源碼;
傳遞的函數(shù)不能是future恬惯;
最后調(diào)用_element.markNeedsBuild()向拆,下一幀時當前State中Element需要重新build。 - 判斷_debugLifecycleState的狀態(tài)酪耳,參考_ElementLifecycle枚舉和源碼浓恳;
當前Element的owner和active斷言刹缝,active在deactivate
函數(shù)被置為false,在mounted
和active
被置為true颈将,所以調(diào)用setStae的時機需要注意梢夯;
debug模式下的斷言;
如果當前Element._dirty已經(jīng)為true的話返回;
調(diào)用owner.scheduleBuildFor(this)函數(shù)晴圾。 - 將element加入到BuildOwner的dirty列表中颂砸,WidgetBinding在下一幀時會通過drawFrame繪制該element。
- element的_dirty標志位置為true死姚,加入到dirty列表中人乓,并且已經(jīng)mount、active且未deactivate都毒。dispose色罚。下一幀該Element會通過RenderObject在其區(qū)域重繪。
2 源碼學習
-
State.setState(VoidCallback fn)
:@protected void setState(VoidCallback fn) { //斷言fn不為空 assert(fn != null); assert(() { //當前State的狀態(tài)账劲,不能等于_StateLifecycle.defunct戳护, //該狀態(tài)時,dispose函數(shù)已經(jīng)調(diào)用 if (_debugLifecycleState == _StateLifecycle.defunct) { throw FlutterError.fromParts(<DiagnosticsNode>[ ErrorSummary('setState() called after dispose(): $this'), ErrorDescription( 'This error happens if you call setState() on a State object for a widget that ' 'no longer appears in the widget tree (e.g., whose parent widget no longer ' 'includes the widget in its build). This error can occur when code calls ' 'setState() from a timer or an animation callback.' ), ErrorHint( 'The preferred solution is ' 'to cancel the timer or stop listening to the animation in the dispose() ' 'callback. Another solution is to check the "mounted" property of this ' 'object before calling setState() to ensure the object is still in the ' 'tree.' ), ErrorHint( 'This error might indicate a memory leak if setState() is being called ' 'because another object is retaining a reference to this State object ' 'after it has been removed from the tree. To avoid memory leaks, ' 'consider breaking the reference to this object during dispose().' ), ]); } //State即使創(chuàng)建了瀑焦,也必須調(diào)用了mounted函數(shù)添加到樹上 if (_debugLifecycleState == _StateLifecycle.created && !mounted) { throw FlutterError.fromParts(<DiagnosticsNode>[ ErrorSummary('setState() called in constructor: $this'), ErrorHint( 'This happens when you call setState() on a State object for a widget that ' 'hasn\'t been inserted into the widget tree yet. It is not necessary to call ' 'setState() in the constructor, since the state is already assumed to be dirty ' 'when it is initially created.' ), ]); } return true; }()); //將fn轉(zhuǎn)為dynamic動態(tài)類型 final dynamic result = fn() as dynamic; assert(() { if (result is Future) { //如果result是一個Future腌且,拋出異常。因為setState函數(shù)在下一幀就會重繪 //Future函數(shù)是異步的蝠猬,不能確定具體重繪時間 throw FlutterError.fromParts(<DiagnosticsNode>[ ErrorSummary('setState() callback argument returned a Future.'), ErrorDescription( 'The setState() method on $this was called with a closure or method that ' 'returned a Future. Maybe it is marked as "async".' ), ErrorHint( 'Instead of performing asynchronous work inside a call to setState(), first ' 'execute the work (without updating the widget state), and then synchronously ' 'update the state inside a call to setState().' ), ]); } // We ignore other types of return values so that you can do things like: // setState(() => x = 3); return true; }()); //調(diào)用State中的_elemnt.markNeedsBuild()函數(shù) _element.markNeedsBuild(); }
-
Element.markNeedsBuld()
:void markNeedsBuild() { //斷言Element的當前狀態(tài)不能等于defunct assert(_debugLifecycleState != _ElementLifecycle.defunct); //未處于active狀態(tài)切蟋, if (!_active) return; //owner等于空斷言異常 assert(owner != null); //當前狀態(tài)必須等于_ElementLifecycle.active assert(_debugLifecycleState == _ElementLifecycle.active); assert(() { //此Widget樹是否出于構建階段 if (owner._debugBuilding) { //當前構建的目標不能等于null assert(owner._debugCurrentBuildTarget != null); //調(diào)用BuildOwner.lockState函數(shù)_debugStateLockLevel會增加统捶,也就是 //當前BuildOwner已經(jīng)鎖定State assert(owner._debugStateLocked); //判斷當前構建的目標是否在構建域中 if (_debugIsInScope(owner._debugCurrentBuildTarget)) return true; //_debugAllowIgnoredCallsToMarkNeedsBuild該標志位位false時榆芦, //在State的initState、didUpdateWidget和build函數(shù)中調(diào)用setState函數(shù)都會報錯喘鸟。 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); } else if (owner._debugStateLocked) { //狀態(tài)已經(jīng)鎖定匆绣,斷言會報錯 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; }()); //如果該Element已經(jīng)被標志位dirty,返回 if (dirty) return; //當前Element的_dirty設置為true _dirty = true; //調(diào)用owner.scheduleBuildFor(Element)函數(shù) owner.scheduleBuildFor(this); }
-
BuildOwner.scheduleBuildFor(Element)
:void scheduleBuildFor(Element element) { //Element不能為空 assert(element != null); //element的BuildOwner對象必須等于當前對象 assert(element.owner == this); assert(() { if (debugPrintScheduleBuildForStacks) debugPrintStack(label: 'scheduleBuildFor() called for $element${_dirtyElements.contains(element) ? " (ALREADY IN LIST)" : ""}'); if (!element.dirty) { //當前Element不是dirty狀態(tài) throw FlutterError.fromParts(<DiagnosticsNode>[ ErrorSummary('scheduleBuildFor() called for a widget that is not marked as dirty.'), element.describeElement('The method was called for the following element'), ErrorDescription( 'This element is not current marked as dirty. Make sure to set the dirty flag before ' 'calling scheduleBuildFor().'), ErrorHint( 'If you did not attempt to call scheduleBuildFor() yourself, then this probably ' 'indicates a bug in the widgets framework. Please report it:\n' ' https://github.com/flutter/flutter/issues/new?template=BUG.md' ), ]); } return true; }()); if (element._inDirtyList) { //element已經(jīng)處于dirty臟列表中 assert(() { if (debugPrintScheduleBuildForStacks) debugPrintStack(label: 'BuildOwner.scheduleBuildFor() called; _dirtyElementsNeedsResorting was $_dirtyElementsNeedsResorting (now true); dirty list is: $_dirtyElements'); //_debugIsInBuildScope該值等于true時什黑,才可以調(diào)用scheduleBuildFor函數(shù) if (!_debugIsInBuildScope) { throw FlutterError.fromParts(<DiagnosticsNode>[ ErrorSummary('BuildOwner.scheduleBuildFor() called inappropriately.'), ErrorHint( 'The BuildOwner.scheduleBuildFor() method should only be called while the ' 'buildScope() method is actively rebuilding the widget tree.' ), ]); } return true; }()); //需要排序Element樹 _dirtyElementsNeedsResorting = true; return; } //忽略 if (!_scheduledFlushDirtyElements && onBuildScheduled != null) { _scheduledFlushDirtyElements = true; onBuildScheduled(); } //將element添加到臟元素列表中 _dirtyElements.add(element); //將element的_inDirtyList標記為true element._inDirtyList = true; assert(() { if (debugPrintScheduleBuildForStacks) debugPrint('...dirty list is now: $_dirtyElements'); return true; }()); }
3 小結(jié)
'State.setState()'函數(shù)將當前State攜帶的Element
對象加入到BuildOwner
對象的dirtyList
集合中崎淳,等待下幀繪制時重繪。