Flutter×Firebase3_getメソッドで値を1回だけ取得

皆さんこんにちわ。
私事ではございますが、現在転職活動中でして
この度希望の会社に内定を頂くことができました！！
2ヶ月かかりましたが今はほっとしています:laughing:

そんな心が舞い上がっている最中ですが
内定後に研修もありますので気を引き締めつつ
自分のスキルも上げるために下記記事は進めていこうと思います。

備忘録として使用する予定ですが
皆さんの解決策にもなれば幸いです。
今回はFirebaseの値を１回のみ取得するために
steamでの取得からgetへの取得に変更します。


## 目次
[-完成イメージ](#完成イメージ)
[-1.構成](#1構成)
[-2.getメソッドで値を1回だけ取得①_ディレクトリの整理](#2getメソッドで値を1回だけ取得_ディレクトリの整理)
[-3.getメソッドで値を1回だけ取得②_Bookクラスの作成](#3getメソッドで値を1回だけ取得_bookクラスの作成)
[-4.getメソッドで値を1回だけ取得③_ChangeNotifierProviderを記載](#4getメソッドで値を1回だけ取得_changenotifierproviderを記載)
[-5.getメソッドで値を1回だけ取得④_データの追加](#5getメソッドで値を1回だけ取得_データの追加)
[-6.全体コード](#6全体コード)
[-参考文献](#参考文献)
[-最後に](#最後に)


## 完成イメージ
・前回の記事からbook_list_model.dartに変数を定義
　※下記前回記事

https://qiita.com/ds0743/items/61ac9335731d4a75f380

・snapshotの定義
・変数をリファクタリング
・データを一冊分追加
<img width="40%" alt="get①.png" img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121562/2f6a7bd1-d700-5a5d-1fc6-d0d7ce8fc828.png">


## 1.構成
・book_list_model.dartにfinal _userCollection変数を定義
・libフォルダ内でbook_listフォルダを作成
・book_listフォルダにbook_list_page.dartを移動し
　book_list_model.dartを作成する
 
・libフォルダ内でdomainフォルダを作成
・domainフォルダにbook.dartを作成
・book_list_page.dartにChangeNotifierProviderを記載
・Firebaseサイトより一冊分データを追加


## 2.getメソッドで値を1回だけ取得①_ディレクトリの整理
・libフォルダにbook_listフォルダを作成
・book_listフォルダにbook_list_page.dartを移動
・book_listフォルダにbook_list_model.dartを作成
![provider②.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121562/a03f97ce-a882-d66b-83e8-5630cd8f9500.png)
※現状のイメージ画像

・book_list_model.dartにBookListModel class作成
・fetchBookList関数を作成
・book_list_page.dartからfinal Stream<QuerySnapshot> 〜を切り取りし
　book_list_model.dartに貼り付け
・book_list_page.dartからsnapshot.data!docs.map〜をコピーして
　book_list_model.dartに貼り付け。少し加工
・firestoreをインポート
・material.dartをインポート
・データを返す準備処理を記載

```php:book_list_model.dart
import 'package:cloud_firestore/cloud_firestore.dart';//←firestoreをインポート
import 'package:flutter/material.dart';//←material.dartをインポート

class BookListModel extends ChangeNotifier {
  final Stream<QuerySnapshot> _usersStream =
      FirebaseFirestore.instance.collection('books').snapshots();//final Stream<QuerySnapshot> 〜をbook_list_page.dartから切り取りし貼り付け


  void fetchBookList() {//←fetchBookList関数を作成
    _usersStream.listen((QuerySnapshot snapshot) {
      final List<Book> books = snapshot.docs.map((DocumentSnapshot document) {//←snapshot.data!docs.map〜をbook_list_page.dartからコピーし貼り付け。少し加工。
        Map<String, dynamic> data = document.data()! as Map<String, dynamic>;//データを返す準備処理を記載

        
      }).toList();
    });
  }
}
```





## 3.getメソッドで値を1回だけ取得②_Bookクラスの作成
・libフォルダにdomainフォルダを作成
・book_listフォルダにbook.dartを作成
![provider③.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121562/510360c8-4885-e47a-4d40-563e6fa833af.png)

※現状のイメージ画像


・book.dartにBook classを作成しtitleとauthorを定義する
```php:book.dart
class Book {
  Book(this.title, this.author);
  String title;
  String author;
}
```
・book_list_model_dartのfetchBookList関数の中にBookへ変換するための値を入れる
・booksへの代入処理を行う
・Bookリストのインスタンスが生成された際にfetchBookListメソッドを呼び出すように処理
・UIやWidgetに通知する処理を記載

```php:book_list_model.dart
List<Book>? books;//←booksへの代入処理

  BookListModel() {//Bookリストのインスタンスが生成された際にfetchBookListメソッドを呼び出すように処理
    fetchBookList(); 
  }


void fetchBookList() {
    _usersStream.listen((QuerySnapshot snapshot) {
      final List<Book> books = snapshot.docs.map((DocumentSnapshot document) {
        Map<String, dynamic> data = document.data()! as Map<String, dynamic>;
        final String title = data['title'];//←Bookへ変換するための値
        final String author = data['author'];//←Bookへ変換するための値
        return Book(title, author);//←Bookへ変換するための値
      }).toList();
      this.books = books;//←booksへの代入処理
      notifyListeners();//←UIやWidgetに通知する処理
    });
  }
```
※fetchbooklist関数の処理前はList<Book>の中身はnullとなるためnull許容の?を入れて
 　List<　Book　>？ books;としている

## 4.getメソッドで値を1回だけ取得③_ChangeNotifierProviderを記載
・book_list_page.dartでCenter Widget内のStreamBuilderから全て削除
・下記を参考にConsumer〜をCenter　Widgetに代入
・providerをインポート
・下記記事を参考にScaffoldにWidgetを囲みChangeNotifierProviderを代入

https://github.com/flutteruniv/flutter_provider_samples/blob/master/lib/count_page.dart

・CountModelをBookListModelへ変更
・booksを定義しListviewで返すためにWidgetへ変換する
　※ListViewはWidgetで返せるので変換が必要
・bookがnullの場合の処理（Loading中となる）を記載

```php:book_list_page.dart
import 'package:firebase1_firebase_add/book_list/book_list_model.dart';
import 'package:firebase1_firebase_add/domain/book.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';//←providerをインポート

class BookListPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<BookListModel>(//←Widgetで囲み、ChangeNotifierProviderを代入・CountModelをBookListModelへ変更
      create: (_) => BookListModel(),//←CountModelをBookListModelへ変更
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: Text('本一覧'),
        ),
        body: Center(//CenterWidget内を削除し添付記事からConsumer~を代入
          child: Consumer<BookListModel>(builder: (context, model, child) {//←CountModelをBookListModelへ変更
            final List<Book>? books = model.books;//←booksを定義

            if (books == null) {//←bookがnullの場合の処理（Loading中となる）を記載
              return CircularProgressIndicator();
            }

            final List<Widget> widgets = books//←booksをWidgetに変換
                .map(
                  (book) => ListTile(
                    title: Text(book.title),
                    subtitle: Text(book.author),
                  ),//←booksをWidgetに変換
                )
                .toList();
            return ListView(//←widgets(books)をListviewに返すように処理
              children: widgets,
            );
          }),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: null,
          tooltip: 'Increment',
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}

```


## 5.getメソッドで値を1回だけ取得④_データの追加
・下記リンクにログインする
　<font color="Red">※ログインは先ほどログインしたものと同様のアカウント</font>
　https://firebase.google.com/
・右上の「Go to Console」をクリック
・作成したプロジェクトをクリック
・左のタブの「構築」→「Firestore Database」をクリック
 <img width="30%" alt="firebase_add⑨.png" img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121562/dd5f208b-8bd2-6b97-2449-0261a7c62aae.png">
・「ドキュメントを追加」をクリック
・ドキュメントIDの「自動ID」をクリック
・フィールドに「title」、値に「エコノミー革命」を入力
・フィールドの追加→フィールドに「採用100年史から読む 人材業界の未来シナリオ」、
　値に「黒田　真行」を入力
・保存をクリック
<img width="70%" alt="firebase_add⑨.png" img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3121562/bef526f2-6cf5-453f-0119-48bf9f6a6981.png">

これで完成！！！

## 6.全体コード
・変更点があったファイルのみ掲載しております。
```php:book_list_page.dart
import 'package:firebase1_firebase_add/book_list/book_list_model.dart';
import 'package:firebase1_firebase_add/domain/book.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class BookListPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<BookListModel>(
      create: (_) => BookListModel(),
      child: Scaffold(
        appBar: AppBar(
          backgroundColor: Theme.of(context).colorScheme.inversePrimary,
          title: Text('本一覧'),
        ),
        body: Center(
          child: Consumer<BookListModel>(builder: (context, model, child) {
            final List<Book>? books = model.books;

            if (books == null) {
              return CircularProgressIndicator();
            }

            final List<Widget> widgets = books
                .map(
                  (book) => ListTile(
                    title: Text(book.title),
                    subtitle: Text(book.author),
                  ),
                )
                .toList();
            return ListView(
              children: widgets,
            );
          }),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: null,
          tooltip: 'Increment',
          child: const Icon(Icons.add),
        ),
      ),
    );
  }
}
```
```php:book_list_model.dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase1_firebase_add/domain/book.dart';
import 'package:flutter/material.dart';

class BookListModel extends ChangeNotifier {
  List<Book>? books;

  BookListModel() {
    fetchBookList(); // コンストラクタでメソッドを呼び出す
  }

  void fetchBookList() async {
    final snapshot = await FirebaseFirestore.instance.collection('books').get();
    final List<Book> books = snapshot.docs.map((DocumentSnapshot document) {
      Map<String, dynamic> data = document.data()! as Map<String, dynamic>;
      final String title = data['title'];
      final String author = data['author'];

      return Book(title, author);
    }).toList();
    this.books = books;
    notifyListeners();
  }
}
```
```php:book.dart
class Book {
  Book(this.title, this.author);
  String title;
  String author;
}
```


## 参考文献
Flutter大学. 「【2021年8月最新】Firestoreのgetメソッドで値を取得しよう」.　YouTube.　2021/08,https://www.youtube.com/watch?v=ztLB7F0vSts&t=0, (参照 2024-07-09)

pub.dev. 「cloud_firestore 5.0.2」.　cloud_firestore example| Flutter packege.　
2024/06, https://pub.dev/packages/cloud_firestore/example, (参照 2024-07-09)

Firebase. 「Cloud Firestore でデータを取得する 」.　2024/06,
https://firebase.google.com/docs/firestore/query-data/get-data?hl=ja#dart, (参照 2024-07-09)



## 最後に
・今回は動画が短く内容も簡単だったので
　エラーもなくスムーズに進めることができました。
・いつもこの調子で進めてたいと思う今日この頃でした笑