markNeedsBuildとは?
Element「再構築、必要、マークつけとくから頼んだ🙋♂️」
Elementが「再構築(rebuild)が必要(Needs)になりました🙋♂️」とrebuildを希望します。
rebuildを希望するElementは自身にマーク(mark)をつける決まりになっています。
(ここからは再構築とrebuildはrebuildで統一して表現します)
解説していきます。
マークとは具体的に何?
=> Elementが持つpropertyのdirty(bool)のことです
dirtyとは?
=> Elementの持つpropertyで型はboolです。
要素が再構築が必要であるとマークされている場合はtrueを返します。(google翻訳)
雰囲気を実際のコードで伝えます
class Element {
//...
/// Returns true if the element has been marked as needing rebuilding.
bool get dirty => _dirty;
bool _dirty = true;
//....
}
正確なコードはこちら
ここまでがmarkNeedsBuildの簡単な説明です。
Q&A形式で理解を深める
ここからはmarkNeedsBuildが
flutterの他の機能とどのように関わり
何を実現するかなどをQ&A形式で書いていきます
rebuildは誰に要請するの?
=> BuildOwnerクラスです。
BuildOwnerクラスとは何?
=> Elementツリーを管理するクラスです
rebuildはBuildOwnerクラスのどこでされるの?
=> BuildOwnerのbuildScopeメソッドです
フレーム毎に呼ばれるdrawFrameがbuildScopeを呼び出すようです。
markNeedsBuildのメソッドの中身は?何をしているの?
=> dirtyをtrueにしBuildOwnerのクラスのscheduleBuildForメソッドを呼びます。引数は自身(Element)
まずBuildOwnerのクラスのscheduleBuildForメソッドの説明をします
=> _dirtyElementsというprivateなpropertyに引数として渡されたElementを追加します
_dirtyElementsとはrebuild待ちElementリストのことです。
(まとめます)markNeedsBuildのメソッドの中身は?何をしているの?
ElementのmarkNeedsBuildメソッドの処理の流れを見てみましょう
1,ElementクラスのmarkNeedsBuildメソッドは
BuildOwnerクラスのscheduleBuildForメソッドを呼び出します。
引数として自身(Element)を渡します。
2,呼び出されたBuildOwnerクラスのscheduleBuildForメソッドは渡されたElementを
rebuild待ちElementリストである_dirtyElementsプロパティに追加します。
少々複雑な説明になってしまったので
最低限必要な部分のみを切り出した簡単なコードを見てみましょう
Element
//色々と省略...
void markNeedsBuild() {
//色々と省略...
_dirty = true;
owner!.scheduleBuildFor(this); //1
}
BuildOwner
//色々と省略...
final List<Element> _dirtyElements = <Element>[];
void scheduleBuildFor(Element element) {
//色々と省略...
_dirtyElements.add(element); //2
}
その_dirtyElementsはその後どのように使われるの?
=> フレーム毎に呼ばれるWidgetBindingのdrawFrameがBuildOwnerのbuildScopeを呼び出しrebuild待ちElementリストである_dirtyElementsを順に取り出しrebuildしていきます
buildScopeのコードを見てみる(抜粋し4行にした)
for (final Element element in _dirtyElements) {
//...
try {
element.rebuild();
} catch (e, stack) {
// ...
}
//...
}
気になる方はgithubで確認してみて下さい。
追記
flutterのrebuildは全てこの手順で行われるの?
そんなことはありません。
例えばStatelessElementはElementのupdateをoverrideして
rebuildをしています。
Widgetは再生成コストが低い => 積極的に再生成すべし
Elementは再生成コストが高い => パフォーマンスに悪影響を及ぼす。一度他の方法がないか見直すべし。
こんな感じの説明は至る所で見るかと思いますが
”Widgetを再生成しElementは使い回す”
をとてもわかりやすいコードで実装してくれている良い例です
内部のコードを直接読むことに抵抗がある人の0.1歩として良いと思います
何かのきっかけになれば。
void update(StatelessWidget newWidget) {
super.update(newWidget);
assert(widget == newWidget);
_dirty = true;
rebuild();
}
Discussion