パルカワ2

最近はFlutterをやっています

_buildWidgetについての考えと今ならどうするか

第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を書く時に知ってると便利なこと」というドキュメントで書かれているので入社してください。

open.talentio.com

各メソッドの影響範囲が掴みにくい

上記の通り、メソッド内だけではなくメソッド外にもUIの再構築という影響を与えうる。

Widget build(BuildContext context) の中でしか利用していないのならまだよくて、_buildHogeの中で更に_buildFooを呼んでいるみたいなことも多々あり、_buildFooの影響範囲はコードを追わないとちゃんと掴めない。

1つのWidgetクラスにたくさんの役割が与えられてしまいコードの再利用性が低い

最初は1画面でしか利用していなかったが他の画面で利用するようになった場合など、クラスであればprivateからpublicにするだけで基本よい。そのクラスがViewModelに依存していると話は変わってくるけどそれは別の話。

メソッドにしていると再利用するにはクラス化する必要があり、クラス化にあたって依存しているメソッドやパラメータを引き剥がす必要がある。またUIの再構築という影響をメソッド外にも及ぼしていたので、クラス化することでUIの再構築が起きなくなってバグが出ないかなどリファクタリングするときに考慮することが多くなる。

今ならどうするか

Widgetクラスをつくって影響範囲と役割を狭め再利用性を高める。

まとめ

最初は高速化のためにメソッドで切り分けるのをやめたほうがいいなくらいに軽く考えていて、まあ速度面で困ったらクラスに分けるくらいでいいかなと思っていたのだが、変化のしにくさを感じるようになってからはクラスを分けるようにしている。言ってしまえば高速化のところはわりかしどうでもよくてクラスの責務をしっかり分けましょうみたいな話のほうが個人的には重要。

次回はWidgetの設計について書きたいけど、書くことが多すぎるので分けるかも。