最近趣味と実益を兼ねて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 ?
要約すると下記のような理由らしい。
StatefulWidget
にbuild
メソッドを生やすことにすると、メソッドシグネチャがStatefulWidget#build(BuildContext, State)
になる。
このメソッドの中でクロージャを定義すると暗黙的にthis(この場合はStatefulWidget)
がキャプチャされる。で、build
メソッドが呼ばれたときに、StatefulWidget
は作り直されたにも関わらずクロージャのthis
は古いStatefulWidget
を参照したままで古い状態を参照してしまう。
一方State#build(BuildContext)
であれば、State
はStatefulWidget
が再生成されたときでもそのまま保持されるので、this
の対象が変わることはない、ということだ。
あとは、StatefulWidget#build(BuildContext, State)
だと、これ継承した新しいStatefulWidget
を作ったときに実装の詳細であるState
を子クラスに公開しなければならなくなる、とか。
仕様的にこうせざるを得なかった、というのはわかったけどやっぱり釈然としない感じはある。
こういった点を何とかするためにBLoCパターンとかMVW系のデザインパターンを実装するんだろうけど、そのへんはまだ分かりきっていないので引き続き調べる。
ちなみに上述のissueの中身は現在APIドキュメントの方にも記載されている。