📚

FutureProviderでFireStoreの値を取得したい

2023/01/18に公開

どうやって取得する?

以前、特定のデータをFutureProviderで取得したかったのですが、全然できなくて、仕方なくFutureBuiderを使って画面に、FireStoreから取得した単一のドキュメントを表示していました。
でもRiverpodでも単一のドキュメントは取る方法はあるはず!

StreamProviderみたいにやればできると思った!

これ実は罠があって、Streamのときは、mapメソッドを使ってたから、同じようにやればできるだろうと思ったら、違って、なんでそもそも出来ないのに、気づいたのかというと、ツルオカさんの記事を読んでいて、「んっ、Streamしか、map使ってないぞ!」を見つけて、思いつきで書いたのが始まりでした。
https://zenn.dev/tsuruo/articles/a3d77c4854e108

このコードが分かりやすいかもですね

  • getだと、.dataしかない.
  • snapshotsだと、mapがある.
// get
Future<Hoge> fetchHoge({required DocumentReference<Hoge> ref}) async =>
      (await ref.get()).data()!;

// snapshots
Stream<Hoge> hogeStream({required DocumentReference<Hoge> ref}) =>
      ref.snapshots().map(
            (snapshot) => snapshot.data()!,
          );

riverpodだとどうするのか?

getの後に、dataというのはコードの保管機能で出てこないので、もしかしたら書かなくていいんじゃないかと、思って一旦、.get();まで書いた。

provider.dart
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;
});

画面に描画するときは、公式にも書いてある.whenを使います。
FutureBuiderで書くより、短くエラーの処理も書けるから楽ですね。
比較するために、以前記事に書いたFutureBuilderのコードをコメントアウトして書いておきました。

barchart_page.dart
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),
                            ]),
                          ]),
                    )),
              ],
            );
          },
        ));
  }
}
// これが以前使ったFutureBuilderのコード
// class LineChartSample extends ConsumerWidget {
//   const LineChartSample({Key? key}) : super(key: key);
//
//   @override
//   Widget build(BuildContext context, WidgetRef ref) {
//     // 棒グラフの棒の横幅
//     const double barWidth = 20.0;
//
//     // グラフタイトルのラベル書式
//     final TextStyle _labelStyle =
//         const TextStyle(fontSize: 16, fontWeight: FontWeight.w800);
//     // ハードコーディングでuidを指定する.
//     // final uid = 'rkoqDMhR6BynStA6AyXI';
//     // final docRef =
//     //     FirebaseFirestore.instance.collection('barchart').doc(uid).get();
//     return Scaffold(
//         appBar: AppBar(
//             centerTitle: true,
//             automaticallyImplyLeading: false,
//             title: const Text(
//               'ブログの棒グラフ',
//               style: TextStyle(color: Colors.white),
//             )),
//         body: FutureBuilder<DocumentSnapshot>(
//           // FutureBuilderで画面に描画する.
//           future: docRef,
//           builder:
//               (BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
//             if (snapshot.hasError) {
//               return const SizedBox(
//                   height: 50,
//                   width: 50,
//                   child: CircularProgressIndicator(
//                     valueColor: AlwaysStoppedAnimation(Colors.blue),
//                     strokeWidth: 8.0,
//                   ));
//             }
//
//             if (snapshot.hasData && !snapshot.data!.exists) {
//               return Text("データを入力するとグラフが表示されます");
//             }
//
//             if (snapshot.connectionState == ConnectionState.done) {
//               Map<String, dynamic> data =
//                   snapshot.data!.data() as Map<String, dynamic>;
//               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: data['blogLog'][0].toDouble(), // 0番目.
//                                     width: barWidth),
//                               ]),
//                               BarChartGroupData(x: 3, barRods: [
//                                 BarChartRodData(
//                                     toY: data['blogLog'][1].toDouble(), // 1番目.
//                                     width: barWidth),
//                               ]),
//                               BarChartGroupData(x: 4, barRods: [
//                                 BarChartRodData(
//                                     toY: data['blogLog'][2].toDouble(), // 2番目.
//                                     width: barWidth),
//                               ]),
//                               BarChartGroupData(x: 5, barRods: [
//                                 BarChartRodData(
//                                     toY: data['blogLog'][3].toDouble(), // 3番目.
//                                     width: barWidth),
//                               ]),
//                               BarChartGroupData(x: 6, barRods: [
//                                 BarChartRodData(
//                                     toY: data['blogLog'][4].toDouble(), // 4番目.
//                                     width: barWidth),
//                               ]),
//                               BarChartGroupData(x: 7, barRods: [
//                                 BarChartRodData(
//                                     toY: data['blogLog'][5].toDouble(), // 5番目.
//                                     width: barWidth),
//                               ]),
//                               BarChartGroupData(x: 8, barRods: [
//                                 BarChartRodData(
//                                     toY: data['blogLog'][6].toDouble(), // 6番目.
//                                     width: barWidth),
//                               ]),
//                             ]),
//                       )),
//                 ],
//               );
//             }
//             return Text("loading");
//           },
//         ));
//   }
// }

アプリを実行するコード

main.dart
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:timelineview_app/barchart_page.dart';
import 'package:timelineview_app/firebase_options.dart';

Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();

  await Firebase.initializeApp(
    options: DefaultFirebaseOptions.currentPlatform,
  );

  runApp(
    const ProviderScope(child: MyApp()),
  );
}

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const LineChartSample(),
    );
  }
}

実行結果

おお!
データを取れている様ですね!
FireStoreから、Riverpodで特定のデータを取ってくるときは、FutureProvderを使えば実現できる様ですね。

最後に

Flutterのお仕事をしていたときは、現場にいるから趣味でやってる人より分かっていると思っていたのですが、全然理解できてませんでした😱
最近は、コミュニティのメンバーたちに、知識の共有をすることが増えてきたので、調べて勉強していると、以前できなかったことの理由が見つかることが多いことに、気づきました!
もし、仕事をするなら、複数の人がいる職場の方がいいのだろうなと思いました!

私、Flutter大学というコミュニティに所属しています。
共同開発を経験できたり、友達ができて楽しい体験ができる学校の部活みたいなところです。
もしご興味あれば入会してみてください!
最近は、Flutter合宿が外部のFlutter勉強してる人たちに大人気です!
https://flutteruniv.com/

Discussion