setStateの定義
State<T extends StatefulWidget> with Diagnosticable
まず、このクラスで 定義されているsetStateの定義を見てみましょう。これはStatefulWidget
、そのサブクラスの状態クラスです。メソッド本体には多くのコードがなく、ビジネスコードの実行時にいくつかの例外処理が行われます。主に次のように、特定のコードを投稿しません。
- に渡される
setState
コールバックメソッドをnullにすることはできません。 - ライフサイクルチェック:コンポーネントは
dispose
コンポーネントツリーから削除されると削除されるため、dispose
後で呼び出す ことはできませんsetState
。通常、これはタイマー、アニメーション、または非同期コールバック中に発生します。このような呼び出しは、メモリリークにつながる可能性があります。 created
フェーズおよびアンロードフェーズ(mounted
) の間に 呼び出すことはできません。setState
つまり、コンストラクターで呼び出すことはできませんsetState
。通常、 のinitState
後に呼び出す 必要がありますsetState
。- setStateのコールバックメソッドはFutureオブジェクトを返すことができません。つまり、その
setState
中で非同期操作を実行することはできず、同期操作のみを実行できます。非同期操作を実行する場合は、setState
外部で呼び出す必要があります。
@protected
void setState(VoidCallback fn) {
// 省略异常处理代码
_element!.markNeedsBuild();
}
コードの最も重要な行:_element!.markNeedsBuild()
、関数名から、マークアップ要素を構築する必要があるということです。それで_element
、これはどこから来たのですか?掘り続けて!
Elementとは何ですか?
私たちが見ている_element
定義_element
は オブジェクトです。実際、それを取得すると、それも返されることがStatefulElement
わかりました。BuildContextを取得すると、注釈は次のようになります。BuildContext
_element
このウィジェットが構築されるツリー内の場所-ウィジェットによって構築されるレンダリングツリーの特定の場所。
BuildContext
StatefulElement
は抽象クラスであるため、実際にはそのインターフェイス実装クラスまたはサブクラスである と推測できます 。ソースに戻ると、クラス階層全体が次のようになっていることがわかります。そのうち Element
、ComponentElement
は抽象クラスであり、 markNeedsBuild
メソッドは 抽象クラスでElement
定義されています。Elementの場合、公式の定義は次のとおりです。
ツリー内の特定の場所でのウィジェットのインスタンス化-レンダリングツリー内のウィジェットインスタンス化オブジェクト。
Element
これは、 構成とレンダリングツリーを橋渡しするオブジェクトとして理解できますWidget
。つまり、実際のレンダリングプロセスは、ソースによって Element
より制御されます。
上の図は、要素の主要なプロパティとメソッドを示しています。
-
_depth
属性:コンポーネントツリー内の要素のレベル。ルートノードの値は0より大きくなければなりません。 -
_sort
方法:2つのElement
要素aとbのレベルを比較します。_depth
レベル値()が大きいほど、レベルが深くなり、表示されるレベルが高くなります。 -
_parent
:親ノード要素。空の場合があります。 -
_widget
:構成要素のコンポーネント構成(実際には、Widget
オブジェクトでWidget
あり、実際にレンダリングされる要素ではなく、レンダリング要素の構成パラメーターです)。 -
_owner
:要素宣言サイクルを管理するオブジェクト。 -
_lifecycleState
:ライフサイクル状態プロパティ。デフォルトはinitial
状態です。 -
取得したメソッド: 返された要素とそのサブ要素でレンダリングする必要
renderObject
の あるオブジェクトが再帰的に呼び出されます(サブ要素はオブジェクトです)。get
RenderObjectElement
-
reassemble
debug
方法:再組み立て方法。ホットリロードなどの段階で のみ 使用され、呼び出されます。このメソッドは、必要に応じて要素自体のマーク付けを処理しbuild
(メソッドを呼び出すmarkNeedsBuild
)、すべての子ノードを再帰的にトラバースし、子ノードのreassemble
メソッドを呼び出します。 -
updateChild
:これはレンダリングプロセスのコアメソッドであり、指定された子要素を新しいコンポーネント構成で更新します。次の4つの組み合わせがあります。-空の場合child
と空でnewWidget
ない場合は、レンダリングする新しい要素が作成されます。- 空で
child
はないが空newWidget
の場合は、要素がコンポーネント構成child
に含まれていないことを意味するため、削除する必要があります。 - 両方が空でない場合は、現在の要素を更新できるかどうかに応じ
child
て処理する必要があります(Widget.canUpdate
)。更新できる場合は、新しいコンポーネント構成で要素を更新します。そうでない場合は、古い要素を削除して、で作成する必要があります。新しいコンポーネント構成新しい要素。 - 両方が空の場合は、何もしません。
- 空で
返される結果も3つのケースに分けられます。
1. 如果创建了一个新的元素,则返回新构建的子元素。
2. 如果旧的元素被更新,返回更新后的子元素。
3. 如果子元素被移除,而没有新的替换的话,返回null。
mount
メソッド:このメソッドは、新しい要素が初めて作成されたときに呼び出され、要素は指定された挿入位置(スロット)に従って指定された親ノードに挿入されます。このメソッドを呼び出した後、要素の状態はからinitial
に 変わりますactive
。これにより、子要素のレベル(_depth)が親要素のレベル+1に設定されます。update
newWidget
メソッド:このメソッドは、親ノードが 新しい構成コンポーネント()で要素を変更したときに呼び出されます。新しい構成タイプは、古い構成タイプと同じである必要があります。detachRenderObject
およびattachRenderObject
:それぞれ、コンポーネントツリーからRenderObjectを削除および追加します。deactivateChild
方法:非アクティブな要素のリストに子要素を追加してから、レンダリングツリーから削除します。activate
方法:状態が非アクティブからアクティブに切り替わるときに呼び出されます。これはライフサイクル機能に属します。このメソッドは、コンポーネントが初めてマウントされるときに呼び出されるのではなく、mountメソッドが呼び出されることに注意してください。deactivate
メソッド:状態がアクティブから非アクティブに切り替わるとき、つまり要素が非アクティブリストに移動されたときに呼び出されます。。unmount
メソッド:状態が非アクティブ状態から無効(存在しない)状態に切り替わるときに呼び出されます。その時点で、要素はレンダリングツリーを離れ、レンダリングツリーに存在しなくなります。didChangeDependencies
:要素の依存関係が変更されたときに呼び出されます。このメソッドはメソッドも呼び出しますmarkNeedBuild
。markNeedsBuild
アプローチ:dirty
次のフレームがレンダリングされたときに要素を再構築できるように、要素を状態としてマークします。この方法の中核は、次のことを行うことです。
_dirty = true;
owner!.scheduleBuildFor(this)
rebuild
メソッド:要素のBuildOwner
オブジェクトがscheduleBuildFor
メソッドを呼び出すと、要素を再構築するためにメソッドが呼び出さrebuild
れます。これは、最初にロードされたときにmount
メソッドでトリガーされ、構成コンポーネントが変更されたときにメソッド でトリガーされますbuild
。このメソッドは、performRebuild
メソッドを呼び出して要素を再構築します。performRebuild
これは、Elementの単語クラスによって実装されるメソッドです。つまり、各要素がどのように再構築されるかは、サブクラスによって決定されます。
コンテンツはよく見えます。要素のライフサイクルの状態図であるレンダリングの状態フローを見てみましょう。コンポーネントは、要素が非アクティブな要素のリストに移動されたときにdeactivate
メソッドをトリガー するメソッドから削除され ます。deactivate
要素を非アクティブリストに移動する方法はdeactivateChild
、親ノードでの操作です。子要素が親によって構築されたレンダリングツリーの一部でなくなると、非アクティブ要素リストに追加されます。
performRebuild
方法
performRebuild
setStateの場合、コンポーネントツリーを再構築するためにメソッドが実際に呼び出されることが わかったので、 メソッドは何をしperformRebuild
ますか?Elementでは、performRebuildメソッドは空のメソッドであり、サブクラスで実装する必要があります。それでは、StatefulElementに移動して、コードを確認しましょう。コードは次のとおりです。
@override
void performRebuild() {
if (_didChangeDependencies) {
state.didChangeDependencies();
_didChangeDependencies = false;
}
super.performRebuild();
}
私は見上げる必要があります、そしてそれは ComponentElement
それです、私はついにそれを見つけました!
@override
void performRebuild() {
// 省略调试的代码
Widget? built;
try {
// ...
built = build();
// ...
} catch (e, stack) {
// ...
} finally {
// We delay marking the element as clean until after calling build() so
// that attempts to markNeedsBuild() during build() will be ignored.
_dirty = false;
// ...
}
try {
_child = updateChild(_child, built, slot);
assert(_child != null);
} catch (e, stack) {
// 省略异常处理
}
// 省略调试代码
}
ここで重要なのは、 build
メソッドとupdateChild
メソッドが呼び出されることです。その中で built = build()
、最新のものを取得することWidget
により、buildメソッドはコンポーネント構成を再構築するため、対応するウィジェットのコンストラクターとbuildメソッドが呼び出されます。次に、メソッドを呼び出し updateChild
て子要素を更新します。前述のように、updateChild
サブコンポーネントの更新には3つの組み合わせがあります。そして、ここでの合計は_child
間違い built
なく空ではありません。重要なのは、 built
(Widget
オブジェクト) canUpdate
がであるかどうかです true
。このメソッドは、Widgetクラスで定義されています。
static bool canUpdate(Widget oldWidget, Widget newWidget) {
return oldWidget.runtimeType == newWidget.runtimeType &&
oldWidget.key == newWidget.key;
}
Widget
設定されてい ない場合 key
(通常、コンポーネントのキーを設定することはお勧めしません)、 runtimeType
2つのコンポーネントの整合性を更新できることに注意してください。したがって、実際には、ほとんどの場合に返されるのはです true
。コードをデバッグおよび更新した場合の結果は同じであり、最終的にはElement
yes のこのブランチになりますupdateChild
。
// ...
else if (hasSameSuperclass &&
Widget.canUpdate(child.widget, newWidget)) {
if (child.slot != newSlot) updateSlotForChild(child, newSlot);
child.update(newWidget);
assert(child.widget == newWidget);
assert(() {
child.owner!._debugElementWasRebuilt(child);
return true;
}());
newChild = child;
}
setState
このことから、メソッド呼び出しは実際に全体 Widget
を再構築するが、必ずしも Widget
構成 されたElement
要素ツリーのすべての要素を削除し、それを新しい要素に置き換えて再レンダリングするとは限らないと推測できます。Flutter
実際、デバッグ中にデバッグツールが開いたことがわかります。ボタンをクリックしても、 実際のWidget
対応 Element
は変更されません。
要約する
呼び出しは、レイヤーのようにレンダーコントロールレイヤーのそのレイヤーの すべてを再構築するsetState
わけではありません が。ただし、これは 使用に問題がないことを意味するわけではありません。まず、前の章で説明したように、 ツリー全体が再構築され、パフォーマンスが低下します。次に、 ツリー全体が変更されたため、ツリー全体のレンダリングはに対応します。レイヤーオブジェクトは メソッドを実行しますが、再レンダリングされない場合がありますが、ツリー全体をトラバースするパフォーマンスのオーバーヘッドも高くなります。したがって、パフォーマンスの観点からは、使用しないようにしてください。 ただし、このコンポーネントが本当に単純で、従属コンポーネントがないか、ほとんどない場合を除きます。Widget
Element
element
setState
Widget
Widget
Element
update
setState