Flutter Firestoreの値を更新しても画面に反映されない
解決したいこと
firestoreのデータ更新まではできたけど、、、リアルタイムで画面に反映されない。。。
— 高卒プログラマーげんと (@gento34165638) May 23, 2021
なんで??😭 pic.twitter.com/7RSa49gFeJ
動画のように、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