第4回 今ならこうするシリーズ
UIをつくるときにStatelessWidget(またはStatefulWidget)を継承してWidgetを作るのではなく、以下のようにWidgetを返すメソッドを作って切り分けることが出来る。メソッドで切り分けるとクラスを作るよりは比較的楽に作れるという理由でそうやってることが多い。僕もそう思ってやっていた。
ここでは、これを_buildWidget
と呼ぶ。今回はこの_buildWidget
について書く。
class HogeView extends StatelessWidget { @override Widget build(BuildContext context) { return Column( children: [ _buildHoge(context), _buildFuga(context), _buildPiyo(context), _buildNyan(context), ], ); } Widget _buildHoge() { ... } ... }
考え
高速化・コードの読みやすさ・リファクタリングの観点からやめたほうがよい。
なぜ
無駄なbuildがされやすい
上の例で _buildNyan
メソッドの中で context.watch<Config>()
していたとする。
Configに変更があったとき本来は _buildNyan
の内容だけrebuildされてほしいが、_buildHoge
なども再度buildされることになり、無駄なbuildが走る。
詳しくは10Xの社内向けに書いた「FlutterのWidgetを書く時に知ってると便利なこと」というドキュメントで書かれているので入社してください。
各メソッドの影響範囲が掴みにくい
上記の通り、メソッド内だけではなくメソッド外にもUIの再構築という影響を与えうる。
Widget build(BuildContext context)
の中でしか利用していないのならまだよくて、_buildHoge
の中で更に_buildFoo
を呼んでいるみたいなことも多々あり、_buildFoo
の影響範囲はコードを追わないとちゃんと掴めない。
1つのWidgetクラスにたくさんの役割が与えられてしまいコードの再利用性が低い
最初は1画面でしか利用していなかったが他の画面で利用するようになった場合など、クラスであればprivateからpublicにするだけで基本よい。そのクラスがViewModelに依存していると話は変わってくるけどそれは別の話。
メソッドにしていると再利用するにはクラス化する必要があり、クラス化にあたって依存しているメソッドやパラメータを引き剥がす必要がある。またUIの再構築という影響をメソッド外にも及ぼしていたので、クラス化することでUIの再構築が起きなくなってバグが出ないかなどリファクタリングするときに考慮することが多くなる。
今ならどうするか
Widgetクラスをつくって影響範囲と役割を狭め再利用性を高める。
まとめ
最初は高速化のためにメソッドで切り分けるのをやめたほうがいいなくらいに軽く考えていて、まあ速度面で困ったらクラスに分けるくらいでいいかなと思っていたのだが、変化のしにくさを感じるようになってからはクラスを分けるようにしている。言ってしまえば高速化のところはわりかしどうでもよくてクラスの責務をしっかり分けましょうみたいな話のほうが個人的には重要。
変更できるようにするのではなくて変更しやすくするのは大事だよな〜って最近思ってる
— ひさいち (@hisaichi5518) 2021年6月2日
次回はWidgetの設計について書きたいけど、書くことが多すぎるので分けるかも。