FutureBuilder — その概要、FlutterにおけるFutureの扱い方

著者: IT Sectr 公開日: 2026-07-02 読了時間: 8 分

FutureBuilderはFlutterのウィジェットで、渡されたFutureから取得したAsyncSnapshotの現在の状態に基づいて自動的にインターフェースを再構築します。awaitの後に手動でsetStateを呼び出すのとは異なり、FutureBuilderは宣言的アプローチを提供します。最初のレンダリングでFutureに購読し、ローディング、エラー、データ準備完了の各状態変化ごとにbuilder関数を呼び出します。Flutter API Reference (2026)によると、FutureBuilderはネットワークからのデータ読み込み、データベースからの読み取り、そしてUIがローディングインジケーター、エラーメッセージ、または準備完了コンテンツを表示する必要がある非同期操作に特に便利です。

重要ポイント

  • FutureBuilder — AsyncSnapshot(none, waiting, active, done)を介したFutureの状態に基づいてUIを構築するFlutterウィジェット
  • AsyncSnapshot — 非同期操作の現在の状態(connectionState、data、error)を含むオブジェクト
  • builder — UIを再構築するためにFutureの状態変化ごとに呼び出されるコールバック関数
  • エラー処理 — AsyncSnapshot.hasErrorを使用して非同期操作の失敗時にフォールバックUIを表示可能
  • ConnectionState — 4つの値(none: 操作なし、waiting: 待機中、active: ストリーム、done: 完了)を持つ列挙型

FlutterにおけるFutureBuilderとは

FutureBuilderはwidgetsパッケージの組み込みFlutterウィジェットで、Futureとbuilder関数を受け取ります。Futureの状態(実行中、データで完了、エラーで完了)が変わると、FutureBuilderは新しいAsyncSnapshotでbuilderを呼び出して自動的にUIを再構築します。これにより、setStateやフラグを使って手動でローディング状態を管理する必要がなくなります。

データストリーム(Stream)を扱うStreamBuilderとは異なり、FutureBuilderは1回限りの非同期操作(HTTPリクエスト、ファイル読み込み、データベースクエリ)向けに設計されています。FutureBuilderはFutureへの購読を自身で管理します。最初のビルド時にFutureを開始し、その完了を追跡します。ウィジェットが破棄されても、FutureBuilderはFutureをキャンセルしません — それは開発者の責任です。

Flutter Cookbook (2026)によると、FutureBuilderは画面の初期化時に非同期操作が1回実行される場合に推奨されます。繰り返し操作やデータストリームにはStreamBuilderを使用してください。両方のウィジェットは同じリアクティブUIパターンに従いますが、FutureBuilderは1回限りのリクエストに最適化されています。

FutureBuilderの内部動作

FutureBuilderの内部実装は、Future.thenとcatchErrorを使用してFutureに購読します。FutureBuilderが開始すると、connectionStateをConnectionState.waitingに設定し、空のデータでbuilderを呼び出します。正常に完了すると、connectionStateはデータとともにConnectionState.doneに変わります。エラー時には、snapshot.errorにエラーオブジェクトが格納されます。各変更がウィジェットの再構築をトリガーします。

AsyncSnapshot: 状態とプロパティ

AsyncSnapshotは、FutureBuilderが状態変化ごとにbuilder関数に渡すコンテナオブジェクトです。これには、非同期操作の現在のステータス(ローディング中か、どのデータを受信したか、エラーが発生したか)に関するすべての情報が含まれています。AsyncSnapshotを理解することは、FutureBuilderでUIを正しく構築するための鍵です。

プロパティ説明
connectionStateConnectionState現在の接続状態(none, waiting, active, done)
dataT?Futureから受信したデータ(完了までまたはエラー時はnull)
errorObject?Futureが例外で完了した場合のエラーオブジェクト
hasDatabooldataがnullでなくconnectionStateがConnectionState.doneの場合にtrue
hasErrorboolFutureがエラーで完了した場合にtrue

ConnectionState: 非同期操作の4つの状態

ConnectionState列挙型は非同期操作の段階を定義します。None — Futureがまだ開始されていない初期状態(稀に使用、通常はinitialDataなしの最初のビルド時)。Waiting — Futureが実行中、データ未受信。Active — 部分的なデータを持つストリームのためにStreamBuilderのみが使用。Done — Futureが完了、データはsnapshot.dataまたはエラーはsnapshot.errorで利用可能。

builder関数内でのすべてのAsyncSnapshot状態の適切な処理は、プロダクションコードの必須要件です。waiting状態を処理しないと、ユーザーはローディング中に空の画面を見ることになります。hasErrorを処理しないと、ユーザーは説明なしの例外を受け取ります。推奨パターン: hasErrorを確認 → hasDataを確認 → デフォルトでローディングを表示。

FutureBuilderの使用パターン

FutureBuilderはいくつかの標準パターンで使用でき、それぞれが特定のタスクを解決します。主なシナリオを見てみましょう: 初期化時のデータ読み込み、キャッシュを使用した読み込み、並列リクエスト、リトライ付きエラー処理。

画面初期化時のデータ読み込み

最も一般的なパターン — StatefulWidgetまたはStatelessWidgetのbuildメソッドでFutureBuilderを使用します。FutureはinitStateから渡されるか、build内で直接作成されます。再構築のたびにbuildメソッドでFutureを作成しないことが重要です — これによりリクエストが繰り返し発生します。Stateフィールドに保存されたFutureを使用してください。

キャッシュとリフレッシュを使用した読み込み

リクエストの繰り返しを防ぐために、FutureBuilderをCachedNetworkImageやローカルキャッシュと組み合わせることができます。最初の読み込み後、データはメモリまたはSharedPreferencesに保存され、FutureBuilderはネットワークから並行して更新しながら、キャッシュされたデータを即座に表示します。これにより、即時応答によるUXが向上します。

pub.dev (2026)によると、キャッシングは特に画像やデータリストに関連します。CachedNetworkImageProviderを使用したFutureBuilderは、キャッシュされた画像を自動的に表示し、存在しない場合はダウンロードされたファイルの後にローディングインジケーターを表示します。

FutureBuilder vs setState: どちらを選ぶか

FutureBuilderとsetStateによる手動状態管理は、Flutterにおける非同期UIへの2つのアプローチです。それぞれに利点と制限があります。選択は画面の複雑さと非同期操作の数に依存します。

FutureBuilderはシンプルさで勝ります。ローディング状態、データ、エラー用のフィールドを宣言する必要はありません — すべてAsyncSnapshotを通じて管理されます。1つの非同期操作(1つのHTTPリクエスト、データベース読み取り)を持つシンプルな画面に最適です。ただし、1つの画面に5つ以上の非同期操作がある場合、FutureBuilderは過剰なネストを生み出し — 結果としてネストされたFutureBuilderの“ピラミッド”ができます。

手動状態フラグを使用したsetStateは、複雑なロジックに対してより多くの制御と可読性を提供します。複数の依存リクエストがある画面(ユーザー読み込み → 注文読み込み → 注文詳細読み込み)では、ChangeNotifierやBlocと共にsetStateを使用する方が良いでしょう。Flutter State Management Guide (2026)によると、複雑なシナリオではFutureBuilderよりもRiverpodやBlocが推奨されます。これらはロジックとプレゼンテーションのより良い分離を提供するからです。

ネットワークデータ読み込みのFutureBuilder例

REST APIからユーザーリストを読み込む実用的なFutureBuilderの例を見てみましょう。このコードは、AsyncSnapshotの3つの状態(ローディング、エラー、データ準備完了)すべての正しい処理を示しています。

dart
class UserListPage extends StatefulWidget {
  const UserListPage({super.key});

  @override
  State<UserListPage> createState() => _UserListPageState();
}

class _UserListPageState extends State<UserListPage> {
  final Future<List<User>> usersFuture = UserRepository().fetchUsers();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('ユーザー')),
      body: FutureBuilder<List<User>>(
        future: usersFuture,
        builder: (context, AsyncSnapshot<List<User>> snapshot) {
          if (snapshot.hasError) {
            return Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  const Icon(Icons.error_outline, size: 48, color: Colors.red),
                  const SizedBox(height: 16),
                  Text('エラー: ${snapshot.error}'),
                ],
              ),
            );
          }

          if (snapshot.hasData) {
            final users = snapshot.data!;
            return ListView.builder(
              itemCount: users.length,
              itemBuilder: (context, index) {
                return ListTile(
                  leading: CircleAvatar(backgroundImage: NetworkImage(users[index].avatarUrl)),
                  title: Text(users[index].name),
                  subtitle: Text(users[index].email),
                );
              },
            );
          }

          return const Center(child: CircularProgressIndicator());
        },
      ),
    );
  }
}

この例では、FutureBuilderが3つの状態すべてを処理しています。エラー時にはエラーメッセージ付きのアイコンが表示されます。正常読み込み時には — アバターと名前のListView。読み込み中 — CircularProgressIndicator。Futureはクラスフィールドとして宣言されており、再構築時の繰り返し呼び出しを防ぎます。このパターンはモバイルアプリでのFutureBuilder使用シナリオの90%をカバーします。

よくある質問

なぜFutureBuilderはbuilderを複数回呼び出すのですか?

FutureBuilderはFutureの状態変化ごとにbuilderを呼び出します。最初は作成時(connectionState: noneまたはwaiting)、2回目は完了時(connectionState: done)。親ウィジェットが再構築されると、FutureBuilderも再構築されます。繰り返し呼び出しを防ぐには、Futureがbuildメソッドの外部で作成されていることを確認してください — そうしないと、buildが呼ばれるたびに新しいFutureが作成されます。

再構築時のリクエスト繰り返しを防ぐには?

FutureをStatefulWidgetのフィールド(initState)に保存するか、メモ化を使用します。Futureがbuildメソッド内で作成されると、buildが呼ばれるたびに新しいFutureが作成され、FutureBuilderが非同期操作を再起動します。StatelessWidgetの場合は、cached_futureパッケージやkeep-aliveウィジェットを使用して、再構築に関係なくFutureが1回だけ実行されるようにします。

FutureBuilderとStreamBuilderの違いは?

FutureBuilderは1回限りの非同期操作(1つのHTTPリクエスト、1つのデータベース読み取り)向けに設計されています。StreamBuilderは時間とともに複数の値を発行できるデータストリーム(チャット、価格更新、位置情報)を扱います。StreamBuilderは部分的なデータのためにConnectionState.activeをサポートしますが、FutureBuilderはwaitingとdoneのみをサポートします。

複数のFutureでFutureBuilderを使用するには?

複数の並列Futureには、Future.waitを使用して結果を1つのFutureBuilderに渡します。Future.waitはFutureのリストを受け取り、Futureを返します — すべてのFutureが完了すると、builderは結果の配列を受け取ります。逐次リクエストには、1つのFuture内でFuture.thenチェーンを使用するか、ネストされたFutureBuilders(可読性が低い)を使用します。代替案として、複数の非同期状態のためにAsyncValueを持つriverpodパッケージがあります。

画面を離れるときにFutureをキャンセルするには?

FutureBuilderはFutureを自動的にキャンセルしません。キャンセルするには、asyncパッケージのCancelableOperationまたはState内のcancelledフラグによるカスタムメカニズムを使用します。dispose()でフラグを設定し、Future完了後にsetStateを呼び出す前にそれを確認します。代替として、AutoDispose付きのriverpodパッケージを使用すると、画面を離れるときに非同期操作が自動的にキャンセルされます。

まとめ

  • FutureBuilder — AsyncSnapshot(waiting, done, error)を介したFuture状態に基づく宣言的UI構築のためのFlutterウィジェット
  • AsyncSnapshot — connectionState、data、errorを含むコンテナ。すべての非同期操作状態の正しい処理に必須
  • builder — 3つの分岐を持つコールバック: hasError(エラー表示)、hasData(データ表示)、default(ローディングインジケーター)
  • FutureBuilder vs setState — FutureBuilderは1つの操作にはよりシンプル、setState + Bloc/Riverpodは複数リクエストの複雑なロジックに優れる
  • リクエスト繰り返し防止 — FutureはStateフィールドにする必要があり、再構築ごとの再起動を避けるためにbuildメソッドで作成しない
  • Futureのキャンセル — FutureBuilderはdispose時にFutureをキャンセルしない。CancelableOperationまたはキャンセルフラグを使用して破棄後のsetStateを防止
  • 複数のFuture — 並列リクエストにはFuture.waitを1つのFutureBuilderで使用。逐次リクエストには1つのFuture内でチェーン

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください