FutureProviderはProviderと同等ですが、非同期コードのためのものです。
FutureProviderは、通常、次のような用途に使われます。
非同期オペレーション(ネットワーク・リクエストなど)の実行とキャッシュ
非同期操作のエラーやロードの状態をうまく処理する。
複数の非同期な値を別の値に結合する。
FutureProviderは、ref.watchと組み合わされると、多くの利益を得ます。この組み合わせは、いくつかの変数が変更されたときに、いくつかのデータの自動的な再取得を可能にし、常に最新の値を持つことを保証します。
インフォメーション
FutureProviderは、ユーザーとの対話の後に、計算を直接修正する方法を提供しません。これは、単純なユースケースを解決するために設計されています。
より高度なシナリオのために、StateNotifierProviderの使用を検討してください。
使用するユースケース
APIとの通信以外には、shared_preferencesと組み合わせる例があります。
非同期処理が必要なパッケージですので、StatefulWidgetで使う場合だと、async awaitつけたメソッドとinitStateが必要な場面がありますが、RiverpodのFutureProviderを使用すれば、メソッドを定義しなくも呼び出して、画面に保存したデータを表示するのに使うことができます。
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:shared_preferences/shared_preferences.dart';
// 数値を表示するプロバイダー
final countStateProvider = StateProvider<int>((ref) => 0);
// SharedPreferencesは、非同期処理が必要なので、FutureProviderを使用する
// SharedPreferencesから保存されたデータを表示する
final countFutureProvider = FutureProvider<int?>((ref) async {
final pref = await SharedPreferences.getInstance();
return pref.getInt('counter');
});
class TemplatePage extends ConsumerWidget {
const TemplatePage({Key? key}) : super(key: key);
Widget build(BuildContext context, WidgetRef ref) {
// SharedPreferencesを呼び出す
final future = ref.watch(countFutureProvider);
return Scaffold(
appBar: AppBar(
actions: [
IconButton(
onPressed: () async {
// 保存したデータを削除する
final SharedPreferences prefs =
await SharedPreferences.getInstance();
final success = await prefs.remove('counter');
},
icon: const Icon(Icons.delete))
],
title: const Text('FutureProvider'),
),
// 数値をSharedPreferencesに保存するロジック
floatingActionButton: FloatingActionButton(
onPressed: () async {
final counter = ref.read(countStateProvider.notifier).state++;
final SharedPreferences prefs = await SharedPreferences.getInstance();
final set = prefs.setInt('counter', counter);
final int? sum = prefs.getInt('counter');
print(sum); // 保存された値をログに表示
},
child: const Icon(Icons.add),
),
body: Center(
// FutureProviderのデータを取得する
child: future.when(
error: (err, _) => Text(err.toString()), //エラー時
loading: () => const CircularProgressIndicator(), //読み込み時
data: (data) => Text(data.toString()), // 保存された値を表示
),
),
);
}
}
今回のユースケースだと、以下のようにFutureProviderを定義して、counterというキーを持っているデータを非同期処理で取得して画面に表示することができます。
// SharedPreferencesは、非同期処理が必要なので、FutureProviderを使用する
// SharedPreferencesから保存されたデータを表示する
final countFutureProvider = FutureProvider<int?>((ref) async {
final pref = await SharedPreferences.getInstance();
return pref.getInt('counter');
});
他には、最近趣味でやっていたSupabaseのデータを取得するのに使ったり、Firestoreの特定のドキュメントIDを取得して、コレクションのデータを取得する方法があります。
Supabaseのデータを一度だけ取得する
FutureProviderを使用して、一度だけデータを取得して、画面に表示するサンプル。
過去に書いた記事
// Supabaseから一度だけデータを取得するプロバイダー.
final notesFutureProvider = FutureProvider<List<Map<String, dynamic>>>((ref) async {
return await Supabase.instance.client
.from('notes')
.select<List<Map<String, dynamic>>>();
});
画面に取得したデータを描画するコード
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:supabase_notes/presentation/utils/notes_provider.dart';
class NotesFuture extends ConsumerWidget {
const NotesFuture({super.key});
Widget build(BuildContext context, WidgetRef ref) {
// AsyncValue型が帰ってくるので、こちら指定する.
AsyncValue config = ref.watch(notesFutureProvider);
return Scaffold(
appBar: AppBar(
title: const Text('Future'),
),
body: config.when(
loading: () => const CircularProgressIndicator(),
error: (err, stack) => Text('Error: $err'),
data: (notes) {
return ListView.builder(
itemCount: notes.length,// なぜかコードの保管機能で出てこない?
itemBuilder: (BuildContext context, int index) {
final note = notes[index];
return ListTile(
title: Text(note['body']),
);
},
);
},
),
);
}
}
Firestoreから特定のデータを取得する
ハードコーディングで書いてますが、本来でしたらuidを指定します。
過去に書いた記事
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
// FutureProviderで単一のドキュメントを取得するコード.
final getProvider = FutureProvider((ref) async {
final id = 'rkoqDMhR6BynStA6AyXI';
final docRef = FirebaseFirestore.instance.collection('barchart');
final fetch = await docRef.doc(id).get();
return fetch;
});
過去に書いた棒グラフの記事のソースコード
画面に取得したデータを描画して特定のuidのデータをグラフで可視化します。
import 'dart:async';
import 'dart:ffi';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:fl_chart/fl_chart.dart';
import 'package:flutter/material.dart';
import 'dart:math' as math;
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:timelineview_app/provider.dart';
class LineChartSample extends ConsumerWidget {
const LineChartSample({Key? key}) : super(key: key);
Widget build(BuildContext context, WidgetRef ref) {
// 変数configはAsyncValue型になる.
// finalをAsyncValueに変更.
AsyncValue config = ref.watch(getProvider);
// 棒グラフの棒の横幅
const double barWidth = 20.0;
// グラフタイトルのラベル書式
final TextStyle _labelStyle =
const TextStyle(fontSize: 16, fontWeight: FontWeight.w800);
return Scaffold(
appBar: AppBar(
centerTitle: true,
automaticallyImplyLeading: false,
title: const Text(
'ブログの棒グラフをFutureProviderで表示',
style: TextStyle(color: Colors.white),
)),
body: config.when(
// FutureBuilderから、変数.whenに変更.
loading: () => const CircularProgressIndicator(),
error: (err, stack) => Text('Error: $err'),
data: (config) {
return Column(
// FutureBuilderの中に、Widgetを組み込む.
children: [
const SizedBox(height: 50),
AspectRatio(
// ここから棒グラフのWidget.
aspectRatio: 2,
child: BarChart(
BarChartData(
maxY: 20,
//Y軸の最大値を指定.
// 棒グラフの位置
alignment: BarChartAlignment.spaceEvenly,
// 棒グラフタッチ時の動作設定
barTouchData: BarTouchData(
touchTooltipData: BarTouchTooltipData(
tooltipBgColor: Colors.black,
)),
// グラフタイトルのパラメータ
titlesData: FlTitlesData(
show: true,
//右タイトル
rightTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false)),
//上タイトル
topTitles: AxisTitles(
sideTitles: SideTitles(showTitles: false),
),
//下タイトル
bottomTitles: AxisTitles(
axisNameWidget: Text(
'ブログ運営年月',
style: _labelStyle,
),
axisNameSize: 40,
sideTitles: SideTitles(
showTitles: true,
),
),
// 左タイトル
leftTitles: AxisTitles(
axisNameWidget: Container(
alignment: Alignment.topCenter,
child: Text(
'記事数',
style: _labelStyle,
)),
axisNameSize: 25,
sideTitles: SideTitles(
showTitles: true,
),
),
),
// 外枠表の線を表示/非表示
borderData: FlBorderData(
border: const Border(
top: BorderSide.none,
right: BorderSide.none,
left: BorderSide(width: 1),
bottom: BorderSide(width: 1),
)),
// barGroups: 棒グラフのグループを表す
// BarChartGroupData: 棒グラフの1つのグループを表す
// X : 横軸
// barRods: 棒グラフのデータを含むBarRodクラスのリスト
// BarChartRodData
// toY : 高さ
// width : 棒の幅
/// [DartのListを使って、値をグラフに入れる]
barGroups: [
BarChartGroupData(x: 2, barRods: [
BarChartRodData(
toY: config['blogLog'][0].toDouble(), // 0
width: barWidth),
]),
BarChartGroupData(x: 3, barRods: [
BarChartRodData(
toY: config['blogLog'][1].toDouble(), // 1
width: barWidth),
]),
BarChartGroupData(x: 4, barRods: [
BarChartRodData(
toY: config['blogLog'][2].toDouble(), // 2
width: barWidth),
]),
BarChartGroupData(x: 5, barRods: [
BarChartRodData(
toY: config['blogLog'][3].toDouble(), // 3
width: barWidth),
]),
BarChartGroupData(x: 6, barRods: [
BarChartRodData(
toY: config['blogLog'][4].toDouble(), // 4
width: barWidth),
]),
BarChartGroupData(x: 7, barRods: [
BarChartRodData(
toY: config['blogLog'][5].toDouble(), // 5
width: barWidth),
]),
BarChartGroupData(x: 8, barRods: [
BarChartRodData(
toY: config['blogLog'][6].toDouble(), // 6
width: barWidth),
]),
]),
)),
],
);
},
));
}
}