質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Cloud Firestore

Cloud Firestore は、自動スケーリングと高性能を実現し、アプリケーション開発を簡素化するように構築された NoSQLドキュメントデータベースです。

Q&A

解決済

3回答

3260閲覧

【Flutter】 Cloud Firestoreを使い特定のドキュメントのフィールドを取得したいです

Takumi_pyg

総合スコア2

Flutter

Flutterは、iOSとAndroidのアプリを同じコードで開発するためのフレームワークです。オープンソースで開発言語はDart。双方のプラットフォームにおける高度な実行パフォーマンスと開発効率を提供することを目的としています。

Cloud Firestore

Cloud Firestore は、自動スケーリングと高性能を実現し、アプリケーション開発を簡素化するように構築された NoSQLドキュメントデータベースです。

0グッド

0クリップ

投稿2021/09/29 02:50

Cloud Firestoreを使い特定のドキュメントのフィールドを取得したいです

Flutterを始めて2週間ほどです。
自分で解決できなかったため、質問させていただきます。
Googleでも英語の記事を読んだりしたのですが、解決できなかったです。
![イメージ説明
コレクションにあるusersからドキュメントのhogehoge@gmail.comを指定してフィールドのnameのhogehogeを取得しようとしています。

発生している問題・エラーメッセージ

The operator '[]' isn't defined for the type 'Future<DocumentSnapshot<Map<String, dynamic>>>'. Try defining the operator '[]'.

該当のソースコード

Flutter

1import 'package:cloud_firestore/cloud_firestore.dart'; 2import 'package:flutter/material.dart'; 3import 'package:hooks_riverpod/hooks_riverpod.dart'; 4 5class ProgramsWiget extends ConsumerWidget { 6 const ProgramsWiget({Key? key}) : super(key: key); 7 8 @override 9 Widget build(BuildContext context, ScopedReader watch) { 10 //watch(usernameProvider).state 11 final documentStream = FirebaseFirestore.instance 12 .collection('users') 13 .doc('hogehoge@gmail.com') 14 .get(); 15 debugPrint(documentStream["name"]) 16 String nameStream = "ゲスト"; 17 18 //context.read(usernameProvider).state = nameStream.toString(); 19 return Scaffold( 20 body: Center( 21 child: Column( 22 mainAxisSize: MainAxisSize.min, 23 children: [ 24 Text('ようこそ ${documentStream["name"]} さん', 25 style: 26 const TextStyle(fontSize: 20, fontWeight: FontWeight.bold)), 27 // ignore: unnecessary_string_interpolations 28 ], 29 ), 30 ), 31 ); 32 } 33}

補足情報(FW/ツールのバージョンなど)

firebase_core: ^1.6.0
firebase_auth: ^3.1.1
cloud_firestore: ^2.5.3
provider: ^6.0.0
hooks_riverpod: ^0.14.0+4
flutter_hooks: ^0.17.0

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2021/09/29 06:03

Flutter は使ったことありませんが、Firebase の結果は非同期で返ってくるので、非同期処理をする必要があるのでは?
Takumi_pyg

2021/09/29 08:36

お答え頂いてありがとうございます。 全てのドキュメントを読み込む処理は、非同期処理を行はなくてもうまく実行できました。 documentStream["name"]で、取得できない事が困っています。色んな記事を見ても解決できなかったため質問させて頂いています。エラー文からは、取得の方法が間違っているという事は読み取れています。
guest

回答3

0

まず、Scaffoldはページの土台です。StreamBuilderはWidgetを返しているに過ぎません。必ず、Scaffoldのbody内に設置しましょう。

次に、ConsumerWidget内では状態の変更ができません。
状態を変更する場合はChangeNotifier、つまりuser_model.dart内でappBarTitleを定める必要があります。

その際はvarを使わない方がいいです。理由はappBarTitleの型がdynamicになり、なんでも取れる状態になってしまいます。

この状態は少し危険で、appBarTitleの型がintになってしまっても、エディタの方でエラーが検知できず、実際にアプリをビルドして初めてエラーが起こり、「エディタの方でエラー出てないから、何が悪いのか分からない」という状況におちいってしまいます。

なので変数名の前に型を宣言しましょう。

(ちなみにsnapshot.data!.docs.mapの部分でMapのvalueをdynamicとして宣言しているのは、cloudFirestoreのMapのvalueはdynamicでないといけないという決まりがあるからです。なので、Webアプリを制作している方々はその不安定さが嫌いなので、FirebaseといったNoSQLを使わず、RDBを使っています。モバイルアプリの場合のデータベースはcloudFirestore一択なので、その欠点には目をつぶるしかありません)

あと、watch(UserProvider)をprogramWidgetModelと名付けるのはやめといた方がいいと思います。

program_widget.dartで複数のModelを読み込むことがあるからです。(滅多にないですが)

素直にuserModelでいいと思います。

上記のコードを見る限り質問者さんは一人を取得したいように感じます。

それならリアルタイム取得である必要はなく、一回の取得だけで良いです。

以上のことを考慮すると、コードは以下のようになります。

(FirebaseAuthを利用してのユーザ登録、そしてその際にusersにuidを含むDocumentをcloudFirestoreに追加することは省略しています。)

dart

1// user_model.dart 2import 'package:flutter/material.dart'; 3 4import 'package:firebase_auth/firebase_auth.dart'; 5import 'package:cloud_firestore/cloud_firestore.dart'; 6import 'package:flutter_riverpod/flutter_riverpod.dart'; 7 8final userProvider = ChangeNotifierProvider( 9 (ref) => UserModel() 10); 11 12class UserModel extends ChangeNotifier { 13 14 bool isLoading = false; 15 String appBarTitle = ''; 16 User? currentUser; 17 late DocumentSnapshot currentUserDoc; 18 19 // watch(userProvider)が呼ばれた時に発火する 20 UserModel() { 21 init(); 22 } 23 24 Future init() async { 25 startLoading(); 26 setCurrentUser(); 27 await setCurrentUserDoc(); 28 endLoading(); 29 } 30 31 void startLoading() { 32 isLoading = true; 33 // 変更をWidgetに通知する 34 notifyListeners(); 35 } 36 37 void setCurrentUser() { 38 currentUser = FirebaseAuth.instance.currentUser; 39 } 40 41 void setAppBarTitle() { 42 appBarTitle = currentUserDoc['name']; 43 } 44 void endLoading() { 45 isLoading = false; 46 // 変更をWidgetに通知する 47 notifyListeners(); 48 } 49 50 Future setCurrentUserDoc() async { 51 try{ 52 await FirebaseFirestore.instance 53 .collection('users') 54 .where('uid',isEqualTo: currentUser!.uid) 55 .limit(1) 56 .get() 57 .then((querySnapshot) { 58 querySnapshot.docs.forEach((DocumentSnapshot documentSnapshot) { 59 currentUserDoc = documentSnapshot; 60 }); 61 }); 62 } catch(e) { 63 print(e.toString()); 64 } 65 } 66 67}

dart

1// program_widget.dart 2import 'package:flutter/material.dart'; 3 4import 'package:flutter_riverpod/flutter_riverpod.dart'; 5 6import 'user_model.dart'; 7 8class ProgramWidget extends ConsumerWidget { 9 10 const ProgramWidget({Key? key}) : super(key: key); 11 Widget build(BuildContext context, ScopedReader watch) { 12 final userModel = watch(userProvider); 13 return userModel.isLoading ? 14 Text('Loading') 15 : Scaffold( 16 appBar: AppBar( 17 // 普通であればText(userModel.currentUserDoc['name'])で良い 18 title: Text( 19 'ようこそ' + userModel.appBarTitle + 'さん', 20 style: const TextStyle( 21 // 細かいけれどint型ではなく、double型であることがわかるように20ではなく20.0になっています 22 fontSize: 20.0, 23 fontWeight: FontWeight.bold 24 ), 25 ), 26 ), 27 ); 28 } 29}

投稿2021/10/04 06:05

nekoyama141592

総合スコア53

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Takumi_pyg

2021/10/04 10:29

本当にありがとうございます。 nekoyamaさんに回答して頂いて感謝しています。 Flutterの基礎的な部分を学んだつもりでしたが、抜けていました。Modelファイルの書き方が特に参考になりました。
nekoyama141592

2021/10/04 11:27

こちらこそ、お役に立てて何よりです!!
guest

0

ベストアンサー

自分はhooks_riverpodの使い方が分からないので、flutter_riverpod(https://pub.dev/packages/flutter_riverpod)を使っての説明になります。

まず、ページを'program_widget.dart'と'program_widget_model.dart'の二つに分けてください。

dart

1// program_widget.dart 2import 'package:flutter/material.dart'; 3 4import 'package:flutter_riverpod/flutter_riverpod.dart'; 5import 'package:cloud_firestore/cloud_firestore.dart'; 6 7import 'package:whisper/program_widget_model.dart'; 8 9class ProgramWidget extends ConsumerWidget { 10 11 const ProgramWidget({ 12 Key? key 13 }) : super(key: key); 14 15 16 Widget build(BuildContext context, ScopedReader watch) { 17 final programWidgetModel = watch(programWidgetProvider); 18 return StreamBuilder<QuerySnapshot>( 19 stream: programWidgetModel.usersStream, 20 builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) { 21 if (snapshot.hasError) Text('something went wrong'); 22 if (snapshot.connectionState == ConnectionState.waiting) Text('Loading'); 23 return Center( 24 child: ListView( 25 children: snapshot.data!.docs.map((DocumentSnapshot document) { 26 Map<String, dynamic> data = document.data()! as Map<String, dynamic>; 27 return Text( 28 'ようこそ' + data['name'] + 'さん', 29 style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold) 30 ); 31 }).toList(), 32 ), 33 ); 34 } 35 ); 36 } 37} 38

dart

1/// program_widget_model.dart 2import 'package:flutter/material.dart'; 3 4import 'package:flutter_riverpod/flutter_riverpod.dart'; 5import 'package:cloud_firestore/cloud_firestore.dart'; 6 7final programWidgetProvider = ChangeNotifierProvider( 8 (ref) => ProgramWidgetModel() 9); 10 11class ProgramWidgetModel extends ChangeNotifier { 12 13 final Stream<QuerySnapshot> usersStream = FirebaseFirestore.instance 14 .collection('users') 15 .where('email',isEqualTo: 'hogehoge@gmail.com') 16 .limit(1) 17 .snapshots(); 18}

こんな感じでしょうか。
質問者さんの意図を汲み取ると、複数名のゲストがいるのを考慮していると思われます。
そうしたことを実装したい場合、
program_widget_model.dartが次のようになります。

dart

1// program_widget_model.dart 2import 'package:flutter/material.dart'; 3 4import 'package:flutter_riverpod/flutter_riverpod.dart'; 5import 'package:cloud_firestore/cloud_firestore.dart'; 6 7final programWidgetProvider = ChangeNotifierProvider( 8 (ref) => ProgramWidgetModel() 9); 10 11class ProgramWidgetModel extends ChangeNotifier { 12 // emailListを構成 13 final List<String> emailList = [] 14 final Stream<QuerySnapshot> usersStream = FirebaseFirestore.instance 15 .collection('users') 16 .where('email',whereIn: emailList) 17 .limit(1) 18 .snapshots(); 19}

投稿2021/10/01 08:11

nekoyama141592

総合スコア53

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Takumi_pyg

2021/10/03 06:45

ありがとうございます。私が、考えていた理想通りに動作しました。 質問なのですが、 final Stream<QuerySnapshot> usersStream = FirebaseFirestore.instance .collection('users') .where('email',whereIn: emailList) .limit(1) .snapshots(); 上記のコードでは、全てのドキュメントからemailのカラムを照合しているのですがドキュメント名から取得することは可能でしょうか?
nekoyama141592

2021/10/04 02:33

ごめんなさい。ドキュメント名が自分には何かわかりません。String型のドキュメントIDのことでしょうか。それとも、フィールド名(FirestoreにおけるRDBにおいての'カラム名')のことでしょうか。 それと、そのコードでは複数取得を目標にしているため.limit(1)は不要なコードでした。重ねてお詫び申し上げます。
Takumi_pyg

2021/10/04 02:50

本当に詰まっているのでお助けして頂いて感謝しています。 String型のドキュメントIDの事です。画像上のhogehoge@gmail.comのデータだけを取得する方法を教えて頂きたいです。もしよければ、参考にしたサイトも教えて頂きたいです。調べ方も参考にさせて頂きたいと思っているのでお願いします。
nekoyama141592

2021/10/04 06:23

StreamBuilderのところで参考にしたのは( https://firebase.flutter.dev/docs/firestore/usage )です。これはpub.devのcloud_firestoreのUsageのところにリンクが貼ってあります。Firebase周りで困ったらpub.devのページをよく見て、他にヒントがないか探してみると良いでしょう。 調べ方のコツはGoogleの検索欄に日本語を入れないで検索してみることでしょうか。 すると、英語情報に当たるので、翻訳サイト(deepLとかGoogle翻訳とか)を積極的に使って解読しましょう。 自分も質問者さんと全く同じエラーにつっかかり、丸一日を解決に費やしたり、型をdynamic型に設定して、その不安定さゆえのエラーに何度も引っかかりました(笑)。
guest

0

もう1つ質問があります。
教えて頂いたコードを応用してAppBarで取得した情報を表示したいと思っているのですが、更新ができないです。具体的には、下記のコードが実行されていないです。初歩的な質問だとは思うのですがお願いします。

flutter

1 snapshot.data!.docs.map((DocumentSnapshot document) { 2 Map<String, dynamic> data = 3 document.data()! as Map<String, dynamic>; 4 appBarTitle = data["name"]; 5 });

全体コード

flutter

1import 'package:flutter/material.dart'; 2 3import 'package:flutter_riverpod/flutter_riverpod.dart'; 4import 'package:cloud_firestore/cloud_firestore.dart'; 5import 'package:sample/user/user_model.dart'; 6 7class ProgramWidget extends ConsumerWidget { 8 const ProgramWidget({Key? key}) : super(key: key); 9 10 @override 11 Widget build(BuildContext context, ScopedReader watch) { 12 final programWidgetModel = watch(UserProvider); 13 var appBarTitle = "ゲスト"; 14 return StreamBuilder<QuerySnapshot>( 15 stream: programWidgetModel.usersStream, 16 builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) { 17 if (snapshot.hasError) return const CircularProgressIndicator(); 18 if (snapshot.connectionState == ConnectionState.waiting) { 19 return const CircularProgressIndicator(); 20 } 21 if (snapshot.hasData) { 22 snapshot.data!.docs.map((DocumentSnapshot document) { 23 Map<String, dynamic> data = 24 document.data()! as Map<String, dynamic>; 25 appBarTitle = data["name"]; 26 }); 27 } 28 debugPrint("test2"); 29 return Scaffold( 30 appBar: AppBar( 31 title: Text('ようこそ ' + appBarTitle + 'さん', 32 style: const TextStyle( 33 fontSize: 20, fontWeight: FontWeight.bold)))); 34 }); 35 } 36} 37

投稿2021/10/04 02:54

Takumi_pyg

総合スコア2

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問