LoginSignup
3
4

More than 1 year has passed since last update.

Flutter + Provider + Firebase Firestoreの値を更新(update)する

Posted at

はじめに

一応上記の続きということになります。
※しかしながら、大幅にコードを変えているのであまり続き感はありません。。。

動作はこんな感じ

かなり無理やり実装した感があるので、「このやり方がおすすめ!という人は是非教えて下さいませ:relieved:

前提

バージョンは下記の通り

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:

フォルダ構成

ざっくりとこんな感じで。。。

lib/
 ├ main.dart
 ├ models
 ├    └ user_model.dart
 ├ providers
 │   └ user_provider.dart
 ├ widgets
 │   └ mypage_view.dart
 └ screens
     ├ mypage_screen.dart
     └ mypage_edit_screen.dart 

一番困ったところ

このやり方でいくと値の更新がリアルタイムで画面に反映されませんでした。。。:sob:
よくわかりませんが、下記動画を参考にとにかく根本から作り替えてみることにしました。

時として、今まで作り上げたコードを全て捨て去る時も重要かと。。

実装

極力省けるところは省きます!

user_provider.dart

models/user_model.dart
import 'package:cloud_firestore/cloud_firestore.dart';

class UserModel {
  UserModel(DocumentSnapshot doc) {
    uid = doc.id;
    email = doc.data()['email'];
    displayName = doc.data()['displayName'];
    photoURL = doc.data()['photoURL'];
    introText = doc.data()['introText'];
  }
  String uid;
  String email;
  String displayName;
  String photoURL;
  String introText;
}

UserModelを「DocumentSnapshot」という型にしておきます。

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 user;

  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();
    this.user = UserModel(docs);
    notifyListeners();
  }

  Future updateUserData(UserModel editedUserData) async {
    final doc = _firestore.doc('users/${editedUserData.uid}');
    await doc.update({
      'displayName': editedUserData.displayName,
      'email': editedUserData.email,
      'photoURL': editedUserData.photoURL,
      'introText': editedUserData.introText,
    });
    notifyListeners();
  }
}

mypage_view.dart

userのデータを表示するだけのwidget。
これを後記するmypage_screen.dartで呼び出す。

widgets/mypage_view.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../config/size_config.dart';
import '../../providers/user_provider.dart';

class MyPageView extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    SizeConfig().init(context);
    return Consumer<UserProvider>(builder: (context, model, child) {
      final userData = model.user;

      return userData == null
          ? Container(
              child: CircularProgressIndicator(),
            )
          : 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:96-c/photo.jpg'),
                      )),
                ),
                SizedBox(
                  width: 5 * SizeConfig.blockSizeHorizontal,
                ),
                Flexible(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: <Widget>[
                      Text(
                        userData.displayName != null
                            ? userData.displayName
                            : 'No Name',
                        style: Theme.of(context).textTheme.headline6,
                      ),
                      SizedBox(
                        height: 1 * SizeConfig.blockSizeVertical,
                      ),
                      Text(userData.introText != null
                          ? userData.introText
                          : 'No introduction'),
                    ],
                  ),
                )
              ],
            );
    });
  }
}

mypage_screen.dart

screens/mypage_screen.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../config/size_config.dart';
import '../../providers/user_provider.dart';
import '../../widgets/mypage/mypage_view.dart';
import './mypage_edit_screen.dart';

class MyPageScreen extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    SizeConfig().init(context);
    return Scaffold(
      appBar: AppBar(
        title: Text('My Page'),
        actions: <Widget>[
            Consumer<UserProvider>(
              builder: (context, model, child) {
                final user = model.user;
                return IconButton(
                  icon: Icon(
                    Icons.edit_sharp,
                  ),
                  onPressed: () async {
                    await Navigator.push(
                        context,
                        MaterialPageRoute(
                          builder: (context) => MyPageEditScreen(
                            user: user,
                          ),
                          fullscreenDialog: true,
                        ));
                    model.fetchUserData();
                  },
                );
              },
            )
        ],
      ),
      body: Container(
        child: Padding(
          padding: const EdgeInsets.only(left: 30, right: 30, top: 30),
          child: Column(
            children: <Widget>[
              MyPageView(),
            // 省略

AppBarにアイコンを設置して、マイページの編集画面へと遷移させる。
その時、現在ログインしてるユーザーをMyPageEditScreenに渡す。

//「現在ログインしてるユーザー」とはここで取得してる部分
final user = model.user;

で、model.userがどこから来るかというと、user_provider.dartから。

providers/user_provider.dart
 UserModel user;
// 省略
 Future fetchUserData() async {
    final userId = _firebaseAuth.currentUser.uid;
    final docs = await _firestore.doc('users/${userId}').get();
    this.user = UserModel(docs);
    notifyListeners();
  }

mypage_edit_screen.dart

screens/mypage_edit_screen.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../../models/user_model.dart';
import '../../config/size_config.dart';
import '../../providers/user_provider.dart';

class MyPageEditScreen extends StatelessWidget {
  //  mypage_screen.dartから受け取ったuser
  MyPageEditScreen({this.user});
  final UserModel user;
  String _displayName;
  String _email;
  String _photoURL;
  String _introText;
  final _form = GlobalKey<FormState>();

  Future _saveProfile(UserProvider model, BuildContext ctx) async {
    _form.currentState.save();
    // 「user」の情報を無理やり上書き
    user.displayName = _displayName;
    user.email = _email;
    user.photoURL = _photoURL;
    user.introText = _introText;

    await model.updateUserData(user);
   // 省略
  }

  @override
  Widget build(BuildContext context) {
    final nameEditingController = TextEditingController();
    final introEditingController = TextEditingController();
    // 初期値を入れる。エラー処理をしていないのでnullならエラー出る。。。
    nameEditingController.text = user.displayName;
    introEditingController.text = user.introText;

    // 保存した時に空っぽのまま更新されてしまうので、現在のuserの値を入れておく
    _email = user.email;
    _photoURL = user.photoURL;
    return Consumer<UserProvider>(builder: (context, model, child) {
      final userData = user;
      return Scaffold(
        appBar: AppBar(
          title: Text('Edit Profile'),
          actions: <Widget>[
            IconButton(
              icon: Icon(Icons.check_sharp),
              onPressed: () {
                _saveProfile(model, context);
              },
            )
          ],
        ),
        body: Container(
          child: Padding(
            padding: const EdgeInsets.only(left: 30, right: 30, top: 30),
            child: Column(
              children: <Widget>[
                userData == null
                    ? Container(
                        child: CircularProgressIndicator(),
                      )
                    : Form(
                        key: _form,
                        child: Row(
                          children: <Widget>[
                           // 省略
                            SizedBox(
                              width: 5 * SizeConfig.blockSizeHorizontal,
                            ),
                            Flexible(
                              child: Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: <Widget>[
                                  TextFormField(
                                    controller: nameEditingController,
                                    style:
                                        Theme.of(context).textTheme.headline6,
                                    textInputAction: TextInputAction.done,
                                    onSaved: (value) {
                                      _displayName = value;
                                    },
                                  ),
                                  SizedBox(
                                    height: 1 * SizeConfig.blockSizeVertical,
                                  ),
                                  TextFormField(
                                    controller: introEditingController,
                                    textInputAction: TextInputAction.done,
                                    keyboardType: TextInputType.multiline,
                                    maxLines: null,
                                    onSaved: (value) {
                                      _introText = value;
                                    },
                                  )
                                ],
                              ),
                            ),
                          ],
                        ),
                      ),
              ],
            ),
          ),
        ),
      );
    });
  }
}

model.updateUserDataで渡さないといけないのはDocumentSnapshotであるUserModelなので、

  _editedUserData = UserModel(
      displayName: _editedUserData.displayName,
      email: user.email,
      photoURL: user.photoURL,
      introText: _editedUserData.introText,
    );
   model.updateUserData(_editedUserData);

みたいな形にすることができなかった。。(私が無理やっただけで、やり方によってはできるのかな?)

更新すると何故かemailとかが空っぽになってしまうので、無理やりだが

user.displayName = _displayName;
    user.email = _email;
    user.photoURL = _photoURL;
    user.introText = _introText;

    await model.updateUserData(user);

みたいな形にした。。。:sweat_smile:

main.dart

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

最後に

参考にした動画の通りChangeNotifierProviderを使うのか、StreamProviderを使うのか、どっちを使えばいいのか今一わかりません!

今回はかなり無理やり実現しちゃった感がすごいので、、、どなたか同じような挙動を実現できるお勧めの方法があれば教えて下さい:bow_tone4:

3
4
0

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
3
4