Flutter Providerを使ってForm入力された値を保持したい
現象
Providerマジで意味がわからん。。
— 高卒プログラマーげんと (@gento34165638) July 10, 2021
なんでmodel.point1TextがFormで入力した時点では入ってるのに、ボタンを押した時には空っぽになってる??!!
誰か助けてーーー!!! pic.twitter.com/nlr4RNF2uJ
環境と概要
environment:
sdk: ">=2.7.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
provider: ^4.3.3
Firebaseを使っていますが、あまり関係がなさそうなのでこの投稿内容では割愛します!
アプリの内容は、好きな映画をコメントと一緒に保存できる、という物です。
今回問題になっているのは「コメントの追加、編集、削除」です。
ToDoリストのようなものを想像していただけると良いかと。。
ソースの構造
lib/
├ main.dart
├ models
├ └ mypage_movie_model.dart // データ構造
├ providers
│ └ mypage_movie_provider.dart // prodiverの処理
├ widgets
├ save_movie_text.dart // 保存ボタン
└ mypage_movie_form.dart // Formの部分
現象の詳細
実現したいこと
①mypage_movie_form.dart
で値を入力
↓
②mypage_movie_provider.dart
で定義しているpoint1Text
に、①で入力された内容が入る
↓
③save_movie_text.dart
の保存ボタンを押したときに、point1Text
が保存される
現状の問題
①mypage_movie_form.dart
で値を入力
↓
②mypage_movie_provider.dart
で定義しているpoint1Text
に、①で入力された内容が入る
↓
③save_movie_text.dart
の保存ボタンを押したときに、point1Text
が空っぽになっている
該当のソース
main.dart
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
ChangeNotifierProvider(
create: (context) => MyPageMovieProvider(),
),
],
child: MaterialApp(
// 省略
mypage_movie_model.dart
import 'package:cloud_firestore/cloud_firestore.dart';
class MyPageMovieModel {
MyPageMovieModel(DocumentSnapshot doc) {
movieId = doc.id;
title = doc.data()['title'];
pointText1 = doc.data()['point1'];
}
String movieId;
String title;
String pointText1;
}
mypage_movie_provider.dart
import '../models/mypage_movie_model.dart';
class MyPageMovieProvider with ChangeNotifier {
MyPageMovieModel myPageMovie;
String point1Text = '';
final uid = FirebaseAuth.instance.currentUser.uid;
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
Future fetchMyMovies(int movieId) async {
final docs =
await _firestore.doc('users/${uid}/movies/${movieId.toString()}').get();
this.myPageMovie = MyPageMovieModel(docs);
notifyListeners();
}
Future editMyMovies(int movieId, String title) async {
final movieRef =
_firestore.doc('users/${uid}/movies/${movieId.toString()}');
await movieRef.update({
'id': movieId,
'title': title,
'point1': point1Text, // point1Textが空っぽなので何も保存されない
});
}
}
save_movie_text.dart
import 'mypage_movie_form.dart';
import '../../providers/mypage_movie_provider.dart';
class SaveMovieText extends StatelessWidget {
final int id;
final String title;
SaveMovieText(this.id, this.title);
@override
Widget build(BuildContext context) {
return Consumer<MyPageMovieProvider>(builder: (context, model, child) {
return FloatingActionButton(
onPressed: () async {
print('保存' + id.toString());
await model.editMyMovies(id, title);
print(model.point1Text); // 空っぽ
},
);
});
}
}
mypage_movie_form.dart
import '../../providers/mypage_movie_provider.dart';
class MyPageMovieForm extends StatelessWidget {
final int id;
MyPageMovieForm(this.id);
final formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<MyPageMovieProvider>.value(
value: MyPageMovieProvider()..fetchMyMovies(id),
child: Consumer<MyPageMovieProvider>(
builder: (context, model, child) {
final movie = model.myPageMovie;
return movie == null
? Center(child: CircularProgressIndicator())
: Form(
key: formKey,
child: ListView(
// 省略
Expanded(
child: TextFormField(
controller: TextEditingController(
text:
movie.pointText1 != null ? movie.pointText1 : '',
),
decoration: InputDecoration(labelText: '見所1'),
onChanged: (value) {
model.point1Text = value;
print(model.point1Text); // ここでは値が入ってる!!!
},
),
),
],
),
],
),
);
},
),
);
}
}
想定される原因
ChangeNotifierProviderが原因?
もしmypage_movie_form.dart
でChangeNotifierProvider
を使わなかった場合、save_movie_text.dart
のmodel.point1Text
にしっかり値が入っています。
@override
Widget build(BuildContext context) {
return Consumer<MyPageMovieProvider>(builder: (context, model, child) {
return Form(
key: formKey,
// 省略
onChanged: (value) {
model.point1Text = value;
print(model.point1Text);
},
class SaveMovieText extends StatelessWidget {
final int id;
final String title;
SaveMovieText(this.id, this.title);
@override
Widget build(BuildContext context) {
return Consumer<MyPageMovieProvider>(builder: (context, model, child) {
return FloatingActionButton(
onPressed: () async {
await model.editMyMovies(id, title);
print(model.point1Text); // 値が入ってる
なぜmypage_movie_form.dart
でChangeNotifierProvider
を使っているかというと、もしコメントが保存されていたら、その内容を入力欄にデフォルトで表示したいからです。
そしてコメントを取得するにはmovie id
が必要なので、mypage_movie_form.dart
でChangeNotifierProvider
を使っています。
他にいい方法があれば教えてください!!!
試したこと
mypage_movie_provider.dart
で定義されているpoint1Text
に初期値を入れてみました。
import '../models/mypage_movie_model.dart';
class MyPageMovieProvider with ChangeNotifier {
MyPageMovieModel myPageMovie;
String point1Text = 'テストです'; // ここに値を入れてみる
final uid = FirebaseAuth.instance.currentUser.uid;
final FirebaseFirestore _firestore = FirebaseFirestore.instance;
}
しかし、保存ボタンを押したときmodel.point1Text
は空っぽでした!!!
class SaveMovieText extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Consumer<MyPageMovieProvider>(builder: (context, model, child) {
return FloatingActionButton(
onPressed: () async {
await model.editMyMovies(id, title);
print(model.point1Text); // 「テストです」が表示されず空っぽ
},
);
});
}
}
最後に
Providerが難しくてあまり理解できていません。。どなたかご教授いただけると幸いです!
関連記事