12
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Flutter + Provider + Firebase 取得した値をConsumerで表示するまで

Last updated at Posted at 2021-05-17

#はじめに
Flutter + FirebaseのアプリでProviderを使うためにはどうすればいいのか?
この記事では上記の疑問を、ほんの少しでも理解するための1つの参考例としてみていただければと思います。。:sweat_smile:
かなり我流な気もするので「これが正しい書き方だ!」という方がいれば、コメントください!

###追記

このやり方でいくと値の更新がリアルタイムで画面に反映されませんでした。。。:sob:
もはやどうしたらいいのかわかりません!!!助けてください!!!!

###追記 その2

かなり無理矢理ですが、なんとか更新できました。。。

#前提
バージョンは下記の通り

pubspec.yaml
environment:
  sdk: ">=2.7.0 <3.0.0"

dependencies:
  flutter:
    sdk: flutter
  provider: ^4.3.3
  firebase_core: "0.7.0"
  firebase_auth: "^0.20.1"
  cloud_firestore: "^0.16.0+1"

この記事ではFirebaseとの連携部分は省略します:relieved:

#全体像
まずユーザーのプロフィールページをイメージしてください。
スクリーンショット 2021-05-17 9.22.36.jpg
ドキュメントusers/${uid}が保持する
・displayName
・email
・introText

①これらの値をProviderで監視!

②displayName変更のように「プロフィール情報」が変更されたときにnotifyListeners()で変更を知らせる

③変更の通知を受け取ってConsumerで囲ってある「プロフィール情報」のStatelessWidgetが変わる!
(状態はProviderから取得できるため、Widgetで保持する必要が無くなった。そのため、StatelessWidgetを使う。)

#フォルダ構成
ざっくりとこんな感じで。。。

lib/
 ├ models/user_model.dart
 ├ provider/user_provider.dart
 ├ screen/my_page_screen.dart
 └ main.dart/

#一番困ったところ
ここがわかるとProvider と Firebaseの連携がとってもスムーズにいくかも。

Providerで監視するには「データモデル」みたいな型(データ構造を表すEntityとかいうやつかな?)を指定する必要があり、Firestoreから取得した値をそのままProviderに適応することはできないのです。。

私の場合は「型の変換」という部分が一番わけがわからず迷走していた部分でした:joy:
ここ重要なので覚えておきましょう!

#実装
長々と前置きを挟みましたが、ここから実際のコードを書きます。。

##user_model.dart

models/user_model.dart
class UserModel {
  String uid;
  String email;
  String displayName;
  String photoURL;
  String introText;

  UserModel({
    this.uid,
    this.email,
    this.displayName,
    this.photoURL,
    this.introText,
  });
}

##user_provider.dart

providers/user_provider.dart
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import '../models/user_model.dart';

class UserProvider with ChangeNotifier {
  UserModel _userModels;

  final FirebaseAuth _firebaseAuth = FirebaseAuth.instance;
  final FirebaseFirestore _firestore = FirebaseFirestore.instance;

  Future fetchUserData() async {
    final userId = _firebaseAuth.currentUser.uid;
    final docs = await _firestore.doc('users/${userId}').get();
    final userModels = UserModel(
      uid: userId,
      displayName: docs.data()['displayName'],
      email: docs.data()['email'],
      photoURL: docs.data()['photoUrl'],
      introText: docs.data()['introText'],
    );
    _userModels = userModels;
    notifyListeners();
  }
}
 final docs = await _firestore.doc('users/${userId}').get();

ここでFirestoreから取得したDocumentSnapshot(docsという変数に格納)をUserModelに変換しないといけません。

変換している部分がここ

final userModels = UserModel(
      uid: userId,
      displayName: docs.data()['displayName'],
      email: docs.data()['email'],
      photoURL: docs.data()['photoUrl'],
      introText: docs.data()['introText'],
    );

ちなみに、今回はDocumentSnapshot(ドキュメント1つ)を変換しましたが、コレクションで取得して変換する場合は下記を参考にmapを使ってみてください(ちょっと古いですが・・)

##main.dart
状態を供給するには、アプリ全体をProviderでラップする必要があるのでmain.dartで**「MaterialApp」を「MultiProvider」で囲う**

また、UserProvider()..fetchUserData()で、createされると同時にfetchUserData()が実行されます。
(「createされる」の意味があんまりわかっていないのですが、、:sweat_smile:誰か教えてください。。)

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import './screens/my_page_screen.dart';
import 'providers/user_provider.dart';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_auth/firebase_auth.dart';


void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  // Create the initialization Future outside of `build`:
  final Future<FirebaseApp> _initialization = Firebase.initializeApp();

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return FutureBuilder(
      future: _initialization,
      builder: (context, snapshot) {
        // Check for errors
        if (snapshot.hasError) {
          return Center(
            child: Text('読み込みでエラー発生' + snapshot.error),
          );
        }
        // Once complete, show your application
        if (snapshot.connectionState == ConnectionState.done) {
          return Home();
        }
        // Otherwise, show something whilst waiting for initialization to complete
        return CircularProgressIndicator();
      },
    );
  }
}

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(
          create: (context) => UserProvider()..fetchUserData(),
        )
      ],
      child: MaterialApp(
       // 省略
      ),
    );
  }
}

##my_page_screen.dart
SizeConfigについては下記を参考に:point_down:

Consumerのmodelを使って、user_provider.dartから値を受け取ります。

screen/my_page_screen.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../config/size_config.dart';
import '../providers/user_provider.dart';

class MyPageScreen extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    SizeConfig().init(context);
    return Scaffold(
      body: Container(
        child: Padding(
          padding: const EdgeInsets.only(left: 30, right: 30, top: 30),
          child: Column(
            children: <Widget>[
              Consumer<UserProvider>(
                builder: (context, model, child) {
                  final userData = model.userModels;
                  return Row(
                    children: <Widget>[
                      Container(
                        height: 10 * SizeConfig.blockSizeVertical,
                        width: 20 * SizeConfig.blockSizeHorizontal,
                        decoration: BoxDecoration(
                            shape: BoxShape.circle,
                            image: DecorationImage(
                              fit: BoxFit.fill,
                              image: NetworkImage(
                                  'https://lh5.googleusercontent.com/-rEw1ckfg8Sc/AAAAAAAAAAI/AAAAAAAAAAA/AMZuuckW028Ka5poorv9UE629d5mtR13CA/s96-c/photo.jpg'),
                            )),
                      ),
                      SizedBox(
                        width: 5 * SizeConfig.blockSizeHorizontal,
                      ),
                      Flexible(
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          children: <Widget>[
                            Text(
                              userData.displayName,
                              style: Theme.of(context).textTheme.headline6,
                            ),
                            SizedBox(
                              height: 1 * SizeConfig.blockSizeVertical,
                            ),
                            Text(userData.introText),
                          ],
                        ),
                      )
                    ],
                  );
                },
              ),
            ],
          ),
        ),
      ),
    );
  }
}

#問題点

一瞬エラーが発生します。。。
どうすれば良いのでしょうか????!!

###追記

こちらにでエラーを回避できました!:relaxed:

#参考
https://fireship.io/lessons/advanced-flutter-firebase/

12
9
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?