# 【Flutter初心者向け】providerを利用したwatchとselectの違いとパフォーマンス向上のポイント

> Flutterの状態管理におけるwatchとselectの違いを解説し、初心者向けにパフォーマンス向上のための実践的な活用法を紹介します。アプリの動作を最適化するためのポイントを学びましょう。

- 公開日: 2025-04-25
- 著者: Tsukamura
- タグ: Flutter
- URL: https://tech.anycloud.co.jp/articles/flutter-provider

---

## はじめに

[provider](https://pub.dev/packages/provider) を利用した状態の監視について、watch select の違いを理解し適切に使用したい初学者向けの記事になります!

## `watch` と `select` の違い

### `context.watch<T>()`

-   モデル全体の変更を監視
-   モデルのどの部分が変更されても、ウィジェットが再構築される
-   簡単に使えるが、不必要な再構築を引き起こす可能性がある

```dart
// MovieModel全体を監視
final model = context.watch<MovieModel>();
final selectedMovieId = model.selectedMoviewId;
```

### `context.select<T, R>(R selector(T value))`

-   モデルの特定のプロパティのみを監視
-   選択したプロパティが変更された場合のみ、ウィジェットが再構築される
-   パフォーマンスの向上につながる

```dart
// selectedMovieIdプロパティのみを監視
final selectedMovieId = context.select((MovieModel model) => model.selectedMovieId);
```

## なぜ `select` を使うべきか

1.  **パフォーマンスの向上**：必要なプロパティの変更時のみウィジェットが再構築されるため、無駄な処理を減らせます
2.  **メモリ効率**：不要な再構築を避けることでメモリ使用量が減少します
3.  **より予測可能な動作**：ウィジェットがいつ再構築されるかが明確になります

## 実際の使用例

MovieSelectorWidget と WatchListWidget を例に、`select`, `watch` を使用したときのウィジェットの更新状態について見ていきます。

ユーザーは利用可能な映画リストから映画を選択し、ウォッチリストに追加・削除できます。

### `select` を使ったMovieSelectorWidget

このウィジェットは `select` を使って、`selectedMovieId` プロパティのみを監視しています。そのため、ウォッチリストが変更されても、このウィジェットは再構築されません。これにより、必要な状態変更時のみ画面が更新されます。

```dart
// selectedMovieIdプロパティのみを監視
class MovieSelectorWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final selectedMovieId = context.select((MovieModel model) => model.selectedMovieId);
    // 読み取りのみなので、watch, selectではなくreadを使用
    final availableMovies = context.read<MovieModel>().availableMovies;

    return ListView.builder(
      itemCount: availableMovies.length,
      itemBuilder: (context, index) {
        final movie = availableMovies[index];
        final isSelected = movie.id == selectedMovieId;

        return ListTile(
          title: Text(movie.title),
          selected: isSelected,
          onTap: () {
            context.read<MovieModel>().selectMovie(movie.id);
          },
        );
      },
    );
  }
}

```

### `watch` を使ったWatchListWidget

このウィジェットは `watch` を使って、`MovieModel` 全体を監視しています。つまり、このウィジェットが実際に必要としていない状態（例：`selectedMovieId` ）が変更された場合でも、ウィジェット全体が再構築されます。これはデモンストレーションのために意図的に非効率な実装になっています。

```dart

class WatchListWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // MovieModel全体を監視
    final model = context.watch<MovieModel>();
    final watchList = model.watchList;
    final selectedMovieId = model.selectedMovieId;
    final availableMovies = model.availableMovies;

    return ListView.builder(
      itemCount: watchList.length,
      itemBuilder: (context, index) {
        final movieId = watchList[index];
        final movie = availableMovies.firstWhere((m) => m.id == movieId);

        return ListTile(
          title: Text(movie.title),
          trailing: IconButton(
            icon: const Icon(Icons.remove),
            onPressed: () {
              model.toggleWatchList(movie.id);
            },
          ),
        );
      },
    );
  }
}
```

## 実際のアプリでの挙動の違い

以下の動画は上記の2つのWidgetをベースにUIを整えたアプリです。Widgetの再構築のタイミングでWidgetが青くふわっとアニメーションをする実装を加えています!

<video class="embed-video" controls="" playsinline="" preload="metadata" src="./media-01.mp4"></video>

1.  映画を選択すると
    -   `MovieSelectorWidget` は `selectedMovieId` を監視しているため再構築されます
    -   `WatchListWidget` も `MovieModel` 全体を監視しているため、不要にも関わらず再構築されます
2.  ウォッチリストを変更すると
    -   `MovieSelectorWidget` は `watchList` を監視していないため再構築されません
    -   `WatchListWidget` は `watchList` を含むモデル全体を監視しているため再構築されます

## おわりに

状態管理においては、「必要なものだけを監視する」という原則が重要です。この記事を参考に `select` もつかっていきましょう! (｀･ω･´)
