Error while retrieving data from FireBase into flutter project - firebase

I am working with Flutter sdk version 2.12.0.I am creating a chat app which can be used to chat with other users. The chat history will be stored in fireBase . I am trying to retrieve the data of what I chatted and display it on the screen using Stream Builder widget.
As i keep chatting the data should get automatically added.
I am getting the following error:
Closure call with mismatched arguments: function '[]'
Receiver: Closure: () => Map<String, dynamic> from Function 'data':.
Tried calling: []("text")
Found: []() => Map<String, dynamic>
I am not able to figure out which function has mis Matched arguments. Can you please me with it. Here is my code:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flashchat1/constants.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class ChatScreen extends StatefulWidget {
static String id='Chat_Screen';
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final _fireStore = FirebaseFirestore.instance;//an instance of fireBase store that stored data created
final _auth = FirebaseAuth.instance;//instance/object of fireBase auth that authorizes users is created
late User loggedInUser;//LoggedInUser is of type FireBase user(now changed to user)
late String messageText;
#override
void initState()
{
super.initState();
getCurrentUser();//calling the getCurrentUser
}
void getCurrentUser()
async{
try
{
final user= await _auth.currentUser;//get the current user id/name/email.Also currentUser return a future so make it async by adding await and async keywords
if(user!=null)
{
loggedInUser=user ;//LoggedInUser = user contains email of the info
print(loggedInUser.email);
}
}
catch(e)
{
print(e);
}
}// Under collection there is documents.Inside documents there are fields like type ,values etc.These fields contain our information
Future<void> messageStream()//Using a stream it becomes very easy .U just need to click once after you run the app .Then u will be done.
async {//The snapShot here is FireBase's Query SnapShot
await for(var snapshot in _fireStore.collection('messages').snapshots()){//make a variable snapshot to store the entire items of the collection in fireBase (Look at the fireBase console there is a collection called messages).This collection takes the snapshot of all the iteams (not literal snapshot .Think it like a snapShot)
for(var message in snapshot.docs)//make a variable message to access the snapShot.docs .(docs stands for Documentation.Look at the fireBase console)
print(message.data());
}
}
void getMessages()//(The problem with this is that we need to keep clicking on the onPressed button every single time the new message is sent .So it is not convinient
async {
final messages = await _fireStore.collection('messages').get();//to retrieve the data from fire base we are creating a variable message
messages.docs;//retreive the data from document section under the collection in firestore
for(var message in messages.docs)//since it is a messages.docs is a list we need to loop through it
{
print(message.data());//print the data its messge.data()
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: null,
actions: <Widget>[
IconButton(
icon: Icon(Icons.close),
onPressed: () {
messageStream();
//_auth.signOut();
//Navigator.pop(context);
//Implement logout functionality
}),
],
title: Text('⚡️Chat'),
backgroundColor: Colors.lightBlueAccent,
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: StreamBuilder(
stream:_fireStore.collection('messages').snapshots(),
builder: (context, AsyncSnapshot snapshot) {
//This is Flutter's Async snapShot
//if(!snapshot.data)
// {
// return Center(
//child: CircularProgressIndicator(
//backgroundColor:Colors.lightBlueAccent,
//),
//);
//}
if(snapshot.hasData){//flutters async snapshot contains a query snapshot
final messages = snapshot.data.docs;
List<Text> messageWidgets = [];
for(var message in messages)//Loop through the messages
{
final messageText = message.data['text'];//retrieve the data under the text field in message collection
final messageSender = message.data['Sender'];//retrieve the data under the Sender field in message collection
final messageWidget = Text('$messageText from $messageSender');
messageWidgets.add(messageWidget);//add the text to the List messageWidget
}
return Column(//
children: messageWidgets,//if u don't write else with a return it will show an error as null returned and null safety broken
);
}
else{
return Column();
}
},
),
),
Container(
decoration: kMessageContainerDecoration,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
onChanged: (value) {
messageText=value;//Whatever you chat will be stored in the variable String variable messageText
},
decoration: kMessageTextFieldDecoration,
),
),
FlatButton(
onPressed: () {
_fireStore.collection('messages').add({
'text': messageText,//add the messages sent to fireStore under the messages object that we created manually
'Sender': loggedInUser.email,//add the current users email to the sender field
},);
},//goal is to send the data that we type here to the fireStore cloud
child: Text(
'Send',
style: kSendButtonTextStyle,
),
),
],
),
),
],
),
),
);
}
}

Change this:
final messageText = message.data['text'];
final messageSender = message.data['Sender'];
into this:
final messageText = message.data()['text'];
final messageSender = message.data()['Sender'];

Related

Incorrect use of parent Widget

I am trying to make a flash Chat App that retrieves the chats from fireBase and displays it on the Screen .I have wrapped it under an Expanded widget .I have give some padding to it .
I am getting the following error
The following assertion was thrown while looking for parent data.:
Incorrect use of ParentDataWidget.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flashchat1/constants.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class ChatScreen extends StatefulWidget {
static String id='Chat_Screen';
#override
_ChatScreenState createState() => _ChatScreenState();
}
class _ChatScreenState extends State<ChatScreen> {
final _fireStore = FirebaseFirestore.instance;//an instance of fireBase store that stored data created
final _auth = FirebaseAuth.instance;//instance/object of fireBase auth that authorizes users is created
late User loggedInUser;//LoggedInUser is of type FireBase user(now changed to user)
late String messageText;
#override
void initState()
{
super.initState();
getCurrentUser();//calling the getCurrentUser
}
void getCurrentUser()
async{
try
{
final user= await _auth.currentUser;//get the current user id/name/email.Also currentUser return a future so make it async by adding await and async keywords
if(user!=null)
{
loggedInUser=user ;//LoggedInUser = user contains email of the info
print(loggedInUser.email);
}
}
catch(e)
{
print(e);
}
}// Under collection there is documents.Inside documents there are fields like type ,values etc.These fields contain our information
Future<void> messageStream()//Using a stream it becomes very easy .U just need to click once after you run the app .Then u will be done.
async {//The snapShot here is FireBase's Query SnapShot
await for(var snapshot in _fireStore.collection('messages').snapshots()){//make a variable snapshot to store the entire items of the collection in fireBase (Look at the fireBase console there is a collection called messages).This collection takes the snapshot of all the iteams (not literal snapshot .Think it like a snapShot)
for(var message in snapshot.docs)//make a variable message to access the snapShot.docs .(docs stands for Documentation.Look at the fireBase console)
print(message.data());
}
}
void getMessages()//(The problem with this is that we need to keep clicking on the onPressed button every single time the new message is sent .So it is not convinient
async {
final messages = await _fireStore.collection('messages').get();//to retrieve the data from fire base we are creating a variable message
messages.docs;//retreive the data from document section under the collection in firestore
for(var message in messages.docs)//since it is a messages.docs is a list we need to loop through it
{
print(message.data());//print the data its messge.data()
}
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leading: null,
actions: <Widget>[
IconButton(
icon: Icon(Icons.close),
onPressed: () {
messageStream();
//_auth.signOut();
//Navigator.pop(context);
//Implement logout functionality
}),
],
title: Text('⚡️Chat'),
backgroundColor: Colors.lightBlueAccent,
),
body: SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Expanded(
child: StreamBuilder(
stream:_fireStore.collection('messages').snapshots(),
builder: (context, AsyncSnapshot snapshot) {
//This is Flutter's Async snapShot
//if(!snapshot.data)
// {
// return Center(
//child: CircularProgressIndicator(
//backgroundColor:Colors.lightBlueAccent,
//),
//);
//}
if(!snapshot.hasData){//flutters async snapshot contains a query snapshot
return Center(
child:CircularProgressIndicator(
backgroundColor:Colors.lightBlueAccent,
),
);
}
final messages = snapshot.data.docs;
List<Text> messageWidgets = [];
for(var message in messages)//Loop through the messages
{
final messageText = message.data()['text'];//retrieve the data under the text field in message collection
final messageSender = message.data()['Sender'];//retrieve the data under the Sender field in message collection
final messageWidget = Text('$messageText from $messageSender',
style:TextStyle(
fontSize:50,
),
);
messageWidgets.add(messageWidget);//add the text to the List messageWidget
}
return Expanded(
flex:2,
child: ListView(//changed from Column to ListView as we want to scroll down .Or else only finite messages can be fit
children: messageWidgets, //if u don't write else with a return it will show an error as null returned and null safety broken
padding: EdgeInsets.symmetric(horizontal: 5,vertical: 5),
),
);
},
),
),
Container(
decoration: kMessageContainerDecoration,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Expanded(
child: TextField(
onChanged: (value) {
messageText=value;//Whatever you chat will be stored in the variable String variable messageText
},
decoration: kMessageTextFieldDecoration,
),
),
FlatButton(
onPressed: () {
_fireStore.collection('messages').add({
'text': messageText,//add the messages sent to fireStore under the messages object that we created manually
'Sender': loggedInUser.email,//add the current users email to the sender field
},);
},//goal is to send the data that we type here to the fireStore cloud
child: Text(
'Send',
style: kSendButtonTextStyle,
),
),
],
),
),
],
),
),
);
}
}
return Expanded(
flex:2,
child: ListView(//changed from Column to ListView as we want to scroll down .Or else only finite messages can be fit
children: messageWidgets, //if u don't write else with a return it will show an error as null returned and null safety broken
padding: EdgeInsets.symmetric(horizontal: 5,vertical: 5),
),
);
This code block is the issue here. You cannot use Expanded widget anywhere you like. The Expanded widget can only be used inside Row or Column Widget.
Remove the Expanded widget in the above code block. It will works.

Exception: Bad state: cannot get a field on a DocumentSnapshotPlatform which does not exist

The method mentioned in this thread https://stackoverflow.com/a/50867881/13153574 I am trying to fetch data from Firestore. But getting the following exception. The 'name' field is a String and 'overview' field is a List of Strings.
Bad state: cannot get a field on a DocumentSnapshotPlatform which does not exist
My code is as below:
import 'package:firebaseAuth/firebaseAuthDemo.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class FindDiseases extends StatefulWidget {
final User user;
const FindDiseases({Key key, this.user}) : super(key: key);
#override
_FindDiseasesState createState() => _FindDiseasesState();
}
class _FindDiseasesState extends State<FindDiseases> {
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
FirebaseAuth _auth = FirebaseAuth.instance;
List diseasesList = [];
//dynamic data;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Colors.teal,
automaticallyImplyLeading: false,
title: Text(
"Diseases List",
),
),
key: _scaffoldKey,
body: Center(
child: FlatButton(
color: Colors.white,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Text("get Disease Record"),
StreamBuilder<DiseaseRecord>(
stream: getDisease(),
builder: (BuildContext c, AsyncSnapshot<DiseaseRecord> data) {
if (data?.data == null) return Text("Error");
DiseaseRecord r = data.data;
return Text("${r.name}");
},
),
],
),
onPressed: () {
getDisease();
},
),
),
);
}
Future _signOut() async {
await _auth.signOut();
}
}
Stream<DiseaseRecord> getDisease() {
return FirebaseFirestore.instance.collection("diseases").doc().get().then(
(snapshot) {
try {
return DiseaseRecord.fromSnapshot(snapshot);
} catch (e) {
print(">>> Error:"+e.toString());
return null;
}
},
).asStream();
}
class DiseaseRecord {
String name;
List<String> overview = new List<String>();
DiseaseRecord.fromSnapshot(DocumentSnapshot snapshot)
: name = snapshot['name'],
overview = List.from(snapshot['overview']);
}
Data is something like as below:
name: "name--"
overview: "['a', 'b', 'c']"
The problem is here:
return FirebaseFirestore.instance.collection("diseases").doc().get()
Calling doc() without any arguments creates a reference to a new, non-existing document. Then calling get() on that, returns a DocumentSnapshot for a non-existing document, and trying to get fields from that is an invalid operation.
Most likely you'll need to know the ID of the disease document you're trying to load, and pass that in to the call to doc(id).

How to get the related image from Firebase storage in flutter

I'm trying to build a list of widgets that are displayed using streambuilder for each entry in my cloud firestore. Here's the code:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_storage/firebase_storage.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class ProperHomeScreen extends StatefulWidget {
#override
_ProperHomeScreenState createState() => _ProperHomeScreenState();
}
class _ProperHomeScreenState extends State<ProperHomeScreen> {
final _firestore = Firestore.instance;
String _downloadURL;
StorageReference _reference = FirebaseStorage.instance.ref();
#override
void initState() {
super.initState();
}
void postsStream() async {
await for (var snapshot in _firestore.collection('posts').snapshots()) {
for (var post in snapshot.documents) {
print(post.data);
}
}
}
testFunction(postImage) async {
print('Here\'s the postImage data from test function: $postImage');
String downloadAddress = await _reference.child(postImage).getDownloadURL();
setState(() {
_downloadURL = downloadAddress;
});
print('THIS IS THE DOWNLOAD URL FROM THE TEST FUNCTION! ::: $_downloadURL');
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: ListView(
children: <Widget>[
StreamBuilder<QuerySnapshot> (
stream: _firestore.collection('posts').snapshots(),
builder: (context, snapshot) {
if(!snapshot.hasData) {
return Center(
child: CircularProgressIndicator(
backgroundColor: Colors.lightBlueAccent,
),
);
}
final posts = snapshot.data.documents;
List<Widget> postWidgets = [];
for (var post in posts) {
final postText = post.data['questionOne'];
final postSender = post.data['email'];
final postImage = post.data['filePath'];
testFunction(postImage);
print('THIS IS THE DOWNLOAD ADDRESS : $_downloadURL');
final postWidget = Container(
child: Column(
children: <Widget>[
Text('$postText from $postSender with image : $postImage'),
Image.network('$_downloadURL'),
],
),
);
postWidgets.add(postWidget);
}
return Column(
children: postWidgets,
);
},
),
],
),
);
}
}
In the console, it is printing urls fine, but the problem I have is that it keeps running the testFunction() continuously until I stop main.dart.
I'm trying to show a different image for each post.
Essentially, I am saving data in cloud firestore and saving images in firebase storage. I'm storing the file name of the image in cloud firestore so that I can access it.
Here's a sample of how I'm saving a post in firestore:
void submitPostSection() {
DateTime now = DateTime.now();
_firestore.collection('posts').add({
'email': loggedInUser.email,
'date': now,
'questionOne': widget.questionOne, //this is a simple string. Example data: 'Here is the latest post today 31st July 2020'
'filePath' : _filePath, // this is just the image name that its saved as in firebase storage. datatype for this is string. here's an example of the data: 'myimage2.jpg'
});
}
I think the problem is because the method keeps getting called and setting state of _downloadURL. I'm not really sure the best way to go about this.
Any ideas?
Thanks in advance!
The problem is that inside testFunction() you are calling setState() which will keep calling the build() method, you can do the following:
List<String> listOfUrl = [];
for (var post in posts) {
final postText = post.data['questionOne'];
final postSender = post.data['email'];
final postImage = post.data['filePath'];
String downloadAddress = await _reference.child(postImage).getDownloadURL();
listOfUrl.add(downloadAddress);
}
ListView.builder(
shrinkWrap: true,
itemCount: listOfUrl.length,
itemBuilder: (BuildContext context, int index) {
return Card(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Image.network(listOfUrl[index]),
],
),
);
});
add the urls inside a list and then use a listview to display them.
I've solved my problem. I deleted the testFunction() and just saved the actual imageURL inside the cloud firestore document. Then I can access it really easily by adding the following line:
final postImageUrl = post.data['imageURL'];

Not able to fetch particular user document by user id in flutter app

I want to retrieve a particular user document by its id from the collection users. When I directly pass the particular user id, I get the data. But when I pass it using variable it shows null.
My code is as follows:
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
import '../services/crud.dart';
class test extends StatefulWidget {
#override
_testState createState() => _testState();
}
class _testState extends State<test> {
String userID="";
#override
void initState() {
super.initState();
///get current user and assign his id
FirebaseAuth.instance.currentUser().then((FirebaseUser user) {
setState(() {
userID = user.uid;
print(userID);
});
});
}
#override
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Column(
children: <Widget>[
StreamBuilder(
stream: Firestore.instance.collection('users').document(userID).snapshots(),
builder: (context,snapshot){
if (!snapshot.hasData) return const Text("Loading...");
else return Container(
child: Column(
children: <Widget>[
Text(snapshot.data["name"]),
Text(snapshot.data["email"]),
Text(snapshot.data["phone"].toString()),
],
),
);
},
)
],
)
);
}
}
When I'm using the following line of code with specifying uid it shows the result :
stream: Firestore.instance.collection('users').document('d6DshRomJMkIe9mAARAi').snapshots(),
But it does not work when I pass userID inside the document(). Even though userID contains the actual id of the logged-in user.
stream: Firestore.instance.collection('users').document(userID).snapshots(),
The error says :
NoSuchMethodError: The method '[]' was called on null.
Receiver: null
Tried calling []("name")
This is the structure of my database.
This is the error that I get on my app screen.
When using userID is doesn't work because currentUser() is asynchronous and the StreamBuilder is being called even before getting the userId therefore try the following:
Stream<DocumentSnapshot> getData()async*{
FirebaseUser user = await FirebaseAuth.instance.currentUser();
yield* Firestore.instance.collection('users').document(user.uid).snapshots();
}
Create a method that returns a Stream and then inside the StreamBuilder do the following:
children: <Widget>[
StreamBuilder(
stream: getData(),
builder: (context,snapshot){
if (!snapshot.hasData) return const Text("Loading...");
else if(snapshot.hasData){
return Container(
child: Column(
children: <Widget>[
Text(snapshot.data["name"]),
Text(snapshot.data["email"]),
Text(snapshot.data["phone"].toString()),
],
),
);
},
return CircularProgressIndicator();
},
)
],

Saving a value as a string with Flutter Firestore Firebase

I want to save a value from my Cloud Firestore as a string. I am using Flutter with Dart. I have been able to save it when building the page using MaterialepageRoute:
MaterialPageRoute(
builder: (context) => MainScreen(
currentUserId: firebaseUser.uid,
currentUserGender: document['gender'],
currentUserPreference: document['preference'],
)),
But this isn't an option with all of my pages, so I have to look for something else. I want to get the value from my Firestore Database, and then save it as a string, since I want to:
if (currentUserGender == 'male') {
//then do something
}
I have no idea how to do this, I have thought about using a Class, maybe the "get"-function with Firebase, but none have worked. I am not really sure how to do this, so any help is appreciated. I am able to get the currentUser. Here is a picture of my database:
https://imgur.com/KL7HX6P
Thanks in advance.
A Minimal Example: To fetch a Single Document Fields. Swap Collection & Document name in the code with your Own Names.
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
class GetUser extends StatefulWidget {
#override
_GetUserState createState() => _GetUserState();
}
class _GetUserState extends State<GetUser> {
Map<String, dynamic> userDetails = {};
Future<Null> getUser() async {
await Firestore.instance
.collection('users') // Your Collections Name
.document('eMAE4XF9cTYS12MpfOuWBW4P2WH3') // Your user Document Name
.get()
.then((val) {
userDetails.addAll(val.data);
}).whenComplete(() {
print('Data Fetched');
setState(() {});
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
RaisedButton(
textColor: Colors.white,
color: Theme.of(context).accentColor,
onPressed: () async {
await getUser();
},
child: Text('Get User Detail from Cloud'),
),
userDetails.length > 0
? Column(
children: <Widget>[
Text('${userDetails['gender']}'),
Text('${userDetails['id']}'),
Text('${userDetails['nickname']}'),
userDetails['gender'] == 'male'
? Text('Its Boy')
: Text('Girl'),
],
)
: Text('No user Data, Please Fetch'),
],
),
),
);
}
}

Resources