LoginSignup
kokogento
@kokogento (ここ げんと)

Are you sure you want to delete the question?

If your question is resolved, you may close it.

Leaving a resolved question undeleted may help others!

We hope you find it useful!

Flutter Firestoreの値を更新しても画面に反映されない

解決したいこと

動画のように、Firestoreの値の更新は成功していても、画面にリアルタイムで反映されません。

これをリアルタイムで反映するようにしたいです!

環境

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

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

providerを使って状態管理をしています。(providerに関しては超初心者なので、あまりわかっていません。。)

該当するソースコード

全体の構成

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

main.dart

main.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(
      // 省略

user_model.dart

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

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'],
    );
    this.userModels = userModels;
    notifyListeners();
  }

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

mypage_view.dart

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.userModels;

      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://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),
                    ],
                  ),
                )
              ],
            );
    });
  }
}

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

class MyPageScreen extends StatelessWidget {
  static const routeName = '/mypage';

  @override
  Widget build(BuildContext context) {
    SizeConfig().init(context);
    return ChangeNotifierProvider<UserProvider>(
      create: (_) => UserProvider()..fetchUserData(),
      child: Scaffold(
        body: Container(
          child: Padding(
            padding: const EdgeInsets.only(left: 30, right: 30, top: 30),
            child: Column(
              children: <Widget>[
                MyPageView(),
                  // 省略
                  ],
                )
              ],
            ),
          ),
        ),
      ),
    );
  }
}

mypage_edit_screen.dart

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 StatefulWidget {
  static const routeName = '/mypage-edit';

  @override
  _MyPageEditScreenState createState() => _MyPageEditScreenState();
}

final _form = GlobalKey<FormState>();
var _editedUserData = UserModel(
  displayName: '',
  introText: '',
);

class _MyPageEditScreenState extends State<MyPageEditScreen> {
  _saveForm() {
    _form.currentState.save();
    UserModel userData = Provider.of<UserModel>(context);
    _editedUserData = UserModel(
      displayName: _editedUserData.displayName,
      email: userData.email,
      photoURL: userData.photoURL,
      introText: _editedUserData.introText,
    );
    Provider.of<UserProvider>(context, listen: false)
        .updateUserData(_editedUserData);
  }

  void _saveProfile(BuildContext ctx) {
    _saveForm();
    ScaffoldMessenger.of(ctx).showSnackBar(SnackBar(
      content: Row(
        children: <Widget>[
          Icon(Icons.check),
          const Text('Save'),
        ],
      ),
      duration: const Duration(milliseconds: 3000),
      padding: const EdgeInsets.symmetric(
        horizontal: 8.0, // Inner padding for SnackBar content.
      ),
      behavior: SnackBarBehavior.floating,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(15),
      ),
    ));
  }

  @override
  Widget build(BuildContext context) {
    final userData = Provider.of<UserProvider>(context).userModels;
    return ChangeNotifierProvider<UserProvider>(
      create: (_) => UserProvider(),
      child: Scaffold(
        appBar: AppBar(
          title: Text('Edit Profile'),
          actions: <Widget>[
            IconButton(
              icon: Icon(Icons.check_sharp),
              onPressed: () {
                _saveProfile(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>[
                            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>[
                                  TextFormField(
                                    initialValue: userData.displayName,
                                    style:
                                        Theme.of(context).textTheme.headline6,
                                    textInputAction: TextInputAction.done,
                                    onSaved: (value) {
                                      _editedUserData = UserModel(
                                        displayName: value,
                                        introText: _editedUserData.introText,
                                      );
                                    },
                                  ),
                                  SizedBox(
                                    height: 1 * SizeConfig.blockSizeVertical,
                                  ),
                                  TextFormField(
                                    initialValue: userData.introText,
                                    textInputAction: TextInputAction.done,
                                    keyboardType: TextInputType.multiline,
                                    maxLines: null,
                                    onSaved: (value) {
                                      _editedUserData = UserModel(
                                        displayName:
                                            _editedUserData.displayName,
                                        introText: value,
                                      );
                                    },
                                  )
                                ],
                              ),
                            ),
                          ],
                        ),
                      ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

疑問点

上記の動画を参考にChangeNotifierProviderを使って実装しましたが、Streamを監視する?StreamProviderというものもあると知りました。。

一体どっちを使うのが良いのでしょうか?

リアルタイムで値が変更されていない原因は、ウィジェットのリビルド?がうまくできていないからだと思われます。
Consumerなら勝手に変わってくれるのかな?とか思っていましたが、想像していたよりもはるかに複雑で意味がわからないです。。。

0

1Answer

Your answer might help someone💌