なぜStatefullWidgetではなくStateがbuildメソッドを持っているのか

転載: https://www.yslibrary.net/2018/07/22/flutter-statefullwidget-build/

最近趣味と実益を兼ねてFlutterを触っている。

だいたいReactっぽくて良さそうな感じ。React Nativeと違って、UIレイヤについては基本的にFlutterの中で完結するのが好み。

ただStatefulWidgetを作るときにちょっと気になることがあった。StatefulWidgetはそれ以外にWidgetの状態を管理するStateクラスが必要なんだけど、StatefulWidgetではなくStateの方にUIを描画するbuildメソッドがあるのだ。

ReactでもそうだしMVPやらMVVMでもそうだけど、だいたい状態を管理するクラスとUIを描画するクラスを分離することで責任を分離している。それをなぜわざわざStateクラスにbuildメソッドが生えているんだろう? と違和感しかなかった。

そんなわけでちょっと調べてみたら、やっぱり同じように思う人はいたらしくissueがたっていた。

FAQ request: why is the build() method on State, and not StatefulWidget ?

要約すると下記のような理由らしい。

StatefulWidgetbuildメソッドを生やすことにすると、メソッドシグネチャがStatefulWidget#build(BuildContext, State)になる。


このメソッドの中でクロージャを定義すると暗黙的にthis(この場合はStatefulWidget)がキャプチャされる。で、buildメソッドが呼ばれたときに、StatefulWidgetは作り直されたにも関わらずクロージャのthisは古いStatefulWidgetを参照したままで古い状態を参照してしまう。


一方State#build(BuildContext)であれば、StateStatefulWidgetが再生成されたときでもそのまま保持されるので、thisの対象が変わることはない、ということだ。

あとは、StatefulWidget#build(BuildContext, State)だと、これ継承した新しいStatefulWidgetを作ったときに実装の詳細であるStateを子クラスに公開しなければならなくなる、とか。

仕様的にこうせざるを得なかった、というのはわかったけどやっぱり釈然としない感じはある。

こういった点を何とかするためにBLoCパターンとかMVW系のデザインパターンを実装するんだろうけど、そのへんはまだ分かりきっていないので引き続き調べる。

ちなみに上述のissueの中身は現在APIドキュメントの方にも記載されている。