FutureBuilderはFlutterのウィジェットで、渡されたFutureから取得したAsyncSnapshotの現在の状態に基づいて自動的にインターフェースを再構築します。awaitの後に手動でsetStateを呼び出すのとは異なり、FutureBuilderは宣言的アプローチを提供します。最初のレンダリングでFutureに購読し、ローディング、エラー、データ準備完了の各状態変化ごとにbuilder関数を呼び出します。Flutter API Reference (2026)によると、FutureBuilderはネットワークからのデータ読み込み、データベースからの読み取り、そしてUIがローディングインジケーター、エラーメッセージ、または準備完了コンテンツを表示する必要がある非同期操作に特に便利です。
重要ポイント
FutureBuilderはwidgetsパッケージの組み込みFlutterウィジェットで、Future
データストリーム(Stream)を扱うStreamBuilderとは異なり、FutureBuilderは1回限りの非同期操作(HTTPリクエスト、ファイル読み込み、データベースクエリ)向けに設計されています。FutureBuilderはFutureへの購読を自身で管理します。最初のビルド時にFutureを開始し、その完了を追跡します。ウィジェットが破棄されても、FutureBuilderはFutureをキャンセルしません — それは開発者の責任です。
Flutter Cookbook (2026)によると、FutureBuilderは画面の初期化時に非同期操作が1回実行される場合に推奨されます。繰り返し操作やデータストリームにはStreamBuilderを使用してください。両方のウィジェットは同じリアクティブUIパターンに従いますが、FutureBuilderは1回限りのリクエストに最適化されています。
FutureBuilderの内部実装は、Future.thenとcatchErrorを使用してFutureに購読します。FutureBuilderが開始すると、connectionStateをConnectionState.waitingに設定し、空のデータでbuilderを呼び出します。正常に完了すると、connectionStateはデータとともにConnectionState.doneに変わります。エラー時には、snapshot.errorにエラーオブジェクトが格納されます。各変更がウィジェットの再構築をトリガーします。
AsyncSnapshotは、FutureBuilderが状態変化ごとにbuilder関数に渡すコンテナオブジェクトです。これには、非同期操作の現在のステータス(ローディング中か、どのデータを受信したか、エラーが発生したか)に関するすべての情報が含まれています。AsyncSnapshotを理解することは、FutureBuilderでUIを正しく構築するための鍵です。
| プロパティ | 型 | 説明 |
|---|---|---|
| connectionState | ConnectionState | 現在の接続状態(none, waiting, active, done) |
| data | T? | Futureから受信したデータ(完了までまたはエラー時はnull) |
| error | Object? | Futureが例外で完了した場合のエラーオブジェクト |
| hasData | bool | dataがnullでなくconnectionStateがConnectionState.doneの場合にtrue |
| hasError | bool | Futureがエラーで完了した場合にtrue |
ConnectionState列挙型は非同期操作の段階を定義します。None — Futureがまだ開始されていない初期状態(稀に使用、通常はinitialDataなしの最初のビルド時)。Waiting — Futureが実行中、データ未受信。Active — 部分的なデータを持つストリームのためにStreamBuilderのみが使用。Done — Futureが完了、データはsnapshot.dataまたはエラーはsnapshot.errorで利用可能。
builder関数内でのすべてのAsyncSnapshot状態の適切な処理は、プロダクションコードの必須要件です。waiting状態を処理しないと、ユーザーはローディング中に空の画面を見ることになります。hasErrorを処理しないと、ユーザーは説明なしの例外を受け取ります。推奨パターン: hasErrorを確認 → hasDataを確認 → デフォルトでローディングを表示。
FutureBuilderはいくつかの標準パターンで使用でき、それぞれが特定のタスクを解決します。主なシナリオを見てみましょう: 初期化時のデータ読み込み、キャッシュを使用した読み込み、並列リクエスト、リトライ付きエラー処理。
最も一般的なパターン — StatefulWidgetまたはStatelessWidgetのbuildメソッドでFutureBuilderを使用します。FutureはinitStateから渡されるか、build内で直接作成されます。再構築のたびにbuildメソッドでFutureを作成しないことが重要です — これによりリクエストが繰り返し発生します。Stateフィールドに保存されたFutureを使用してください。
リクエストの繰り返しを防ぐために、FutureBuilderをCachedNetworkImageやローカルキャッシュと組み合わせることができます。最初の読み込み後、データはメモリまたはSharedPreferencesに保存され、FutureBuilderはネットワークから並行して更新しながら、キャッシュされたデータを即座に表示します。これにより、即時応答によるUXが向上します。
pub.dev (2026)によると、キャッシングは特に画像やデータリストに関連します。CachedNetworkImageProviderを使用したFutureBuilderは、キャッシュされた画像を自動的に表示し、存在しない場合はダウンロードされたファイルの後にローディングインジケーターを表示します。
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が推奨されます。これらはロジックとプレゼンテーションのより良い分離を提供するからです。
REST APIからユーザーリストを読み込む実用的なFutureBuilderの例を見てみましょう。このコードは、AsyncSnapshotの3つの状態(ローディング、エラー、データ準備完了)すべての正しい処理を示しています。
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は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は1回限りの非同期操作(1つのHTTPリクエスト、1つのデータベース読み取り)向けに設計されています。StreamBuilderは時間とともに複数の値を発行できるデータストリーム(チャット、価格更新、位置情報)を扱います。StreamBuilderは部分的なデータのためにConnectionState.activeをサポートしますが、FutureBuilderはwaitingとdoneのみをサポートします。
複数の並列Futureには、Future.waitを使用して結果を1つのFutureBuilderに渡します。Future.waitはFutureのリストを受け取り、Futureを返します — すべてのFutureが完了すると、builderは結果の配列を受け取ります。逐次リクエストには、1つのFuture内でFuture.thenチェーンを使用するか、ネストされたFutureBuilders(可読性が低い)を使用します。代替案として、複数の非同期状態のためにAsyncValueを持つriverpodパッケージがあります。
FutureBuilderはFutureを自動的にキャンセルしません。キャンセルするには、asyncパッケージのCancelableOperationまたはState内のcancelledフラグによるカスタムメカニズムを使用します。dispose()でフラグを設定し、Future完了後にsetStateを呼び出す前にそれを確認します。代替として、AutoDispose付きのriverpodパッケージを使用すると、画面を離れるときに非同期操作が自動的にキャンセルされます。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。