そのWidget、methodで分けるか？classで分けるか？

# はじめに
Flutterで開発をしていて、Widgetの構造が複雑になった際にある程度の意味のあるまとまりでWidgetを切り分けることがよくあると思います。宣言的UI自体、ネストが深くなりがちで1つのクラス内で複雑なUIをネストさせると可読性も下がってメンテナンスがしにくくなることが考えられます。

たとえば、TwitterのこのUIをFlutterでつくるとします。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185927/1b998731-6286-669f-7de8-38f83f6aeb84.jpeg  width=500>

皆さんだったらどのようにWidgetを切り分けて作っていくでしょうか？

たとえば、こんな分け方が考えられます。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/185927/8c722b8c-823e-3848-596d-f2609f6def44.jpeg  width=500>

そのときに、この枠で囲ったWidgetたちはそれぞれmethodとして分けるかStatelessWidgetとしてclassで分けるか、どちらで分ければいいのか。というのが今回の話です。

# TL;DR
こちらの和訳のような記事です。基本的にはclassで分けるのがよさそうです。

https://stackoverflow.com/questions/53234825/what-is-the-difference-between-functions-and-classes-to-create-reusable-widgets

# 本題
さきほどのUIを作るとき、意味のあるまとまりでWidgetを返すprivate methodとしてWidgetを切り分けるでしょうか？それともprivateなStatelessWidgetのclassとして切り分けるでしょうか？どちらでも同じUIを作ることができます。
それぞれざっくりWidgetを組んだ例を載せようと思います。


methodで分ける例

```dart
class TweetItem extends StatelessWidget {
  const MyHomePage();

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Image.network(''),  // アイコン
        _tweetContent(), // Tweet情報
      ],
    );
  }
}

Widget _tweetContent() {
  return Column(
    children: [
      _header(), // 名前、id、日時
      _body(), // 内容
      _bottom(), // リプライアイコン、リツイートアイコン、...
    ],
  );
}

Widget _header() {
  return Container();
}

Widget _body() {
  return Container();
}

Widget _bottom() {
  return Container();
}
```



classで分ける例

```dart
class TweetItem extends StatelessWidget {
  const MyHomePage();

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Image.network(''), // アイコン
        _TweetContent(), // Tweet情報
      ],
    );
  }
}

class _TweetContent extends StatelessWidget {
  const _TweetContent({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        _Header(), // 名前、id、日時
        _Body(), // 内容
        _Bottom(), // リプライアイコン、リツイートアイコン、...
      ],
    );
  }
}

class _Header extends StatelessWidget {
  const _Header({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

class _Body extends StatelessWidget {
  const _Body({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

class _Bottom extends StatelessWidget {
  const _Bottom({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container();
  }

```

これらは見た目は同じになりますが、生成されるWidget Treeは異なります。これが重要な違いです。

methodで分けた場合

```
Row
  Image
  Column
    Container
    Container
    Container
```

classで分けた場合

```
Row
  Image
  _TweetContent
    Column
      _Header
        Container
      _Body
        Container
      _Bottom
        Container
```

こちらのstackoverflowでは、methodでWidgetを分けることでバグが発生したりパフォーマンスの最適化ができなかったりするということが書かれています。
methodを使えば必ずバグが発生するということは無いようですが、classを使えばこれらの問題の影響を受けない保証がされるみたいです。

https://stackoverflow.com/questions/53234825/what-is-the-difference-between-functions-and-classes-to-create-reusable-widgets

いくつかのmethodとclassで分けるときに起こる挙動の違いの例が紹介されています。

- AnimatedSwitcherがうまく動かない例（ https://dartpad.dev/1870e726d7e04699bc8f9d78ba71da35 ）
- classで分けることでより細かい範囲でrebuildできてパフォーマンス向上できる例（ https://dartpad.dev/a869b21a2ebd2466b876a5997c9cf3f1 ）
- methodを使うことでBuildContextを誤って使うことにより、InheritedWidgetを使うときにバグを生む可能性があることを示した例（ https://dartpad.dev/06842ae9e4b82fad917acb88da108eee ）

# まとめ
stackoverflowの回答ではそれぞれの場合で以下のメリットがまとめられています。

- classで分けたとき
  - パフォーマンスの最適化が可能（constコンストラクタ、より細かい範囲でのrebuild）
  - 異なるWidgetを切り替えるときmethodは以前の状態を再利用することがあるが、classにすることでリソースが正しく処理されるようになる
  - ホットリロードが最適に動く
  - devtoolでWidget TreeにClass Widgetが表示されるので画面に描画されているレイアウトがわかりやすくなる
  - `debugFillProperties`をオーバーライドして、Widgetに渡されたパラメータを表示することができる
  - `ProviderNotFound`のようなエラーが発生した場合に、classで分けた場合は現在build中のWidget名を表示するが、methodでWidget Treeをbuildした場合、エラーに役立つ名前が表示されない
  - keyを指定できる
  - context APIが使える
- methodで分けたとき
  - ボイラープレートを減らしてコード量が減る

ミスリーディングしてはいけないのは、methodでわけることで問題が起こるというわけではなく、classを使えば起こり得る問題を解決できるということみたいです。

そもそも、Widgetを返すmethodで全て事足りるのであればStateless Widgetは存在しないですよね。

上記で書いたように、特にmethodでもclassでもどっちでもいい場合は最優先でclassでWidgetを分けるのがよさそうです。

methodで分けるときがどういうときがあるかというと、複数Widgetを`Column`や`Row`で分けるのではなく、`List<Widget>`で返したいときなどかなと思います。
あとは、Widgetを分けてバケツリレーさせるプロパティが多くなってきたとき少量ながらもclassで分けたときは新しくclass内でコンストラクタの引数に渡ってきた値を受け取るプロパティを宣言する必要があり、メモリ使用量が増えそうなのでprivate methodで定義するとかもあるっちゃありそうです。