How to display image file in firestore with Flutter - firebase

I found following issue. Then I understand it.
Flutter / FireStore: how to display an image from Firestore in Flutter?
File uploading is succeeding.
var imgUrl = await ref.getDownloadURL();
print(imgUrl.toString());
However I have following error.
It seems I'm doing same.
Unhandled Exception: PlatformException(Error -13010, FIRStorageErrorDomain, Object images/cars/40711b90-9db4-11ea-c602-a557c9b7697a.jpeg does not exist.)
However I have no idea how to display and handle it.
Please give me advice. Thanks.

You need to add the url to firestore first:
StorageTaskSnapshot snapshot = await storage
.ref()
.child("images/$imageName")
.putFile(file)
.onComplete;
if (snapshot.error == null) {
final String downloadUrl =
await snapshot.ref.getDownloadURL();
await Firestore.instance
.collection("images")
.add({"url": downloadUrl, "name": imageName});
}
Now in Firestore you will have collection called images and document with the image url and image name. The method getDownloadUrl() returns the url of the image so you can store it in Firestore. Then to display it you can do the following:
body: Container(
padding: EdgeInsets.all(10.0),
child: FutureBuilder(
future: getImages(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.documents.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
contentPadding: EdgeInsets.all(8.0),
title:
Text(snapshot.data.documents[index].data["name"]),
leading: Image.network(
snapshot.data.documents[index].data["url"],
fit: BoxFit.fill),
);
});
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
}
return CircularProgressIndicator();
},
),
),
/// code here
Future<QuerySnapshot> getImages() {
return fb.collection("images").getDocuments();
}
Here you use the method getImages() which retrieves all the images from the collection images. To display the image you can use Image.network widget.

Related

Merging stream in flutter firetore

I am using two quires to fetch data from the firestore.
Query 1.
_firestore
.collection('chats')
.doc(getCurrentUser().uid)
.collection('chatUsers')
.orderBy('timestamp');
with all the querysnapshot document from query 1. I am fetching last message and document id, and displaying the last message in listTile. With document id i am passing the id to fetch other data from other collection like name photo etc.
Query 2.
Future<DocumentSnapshot> fetchUserData(String uid) async => await _firestore
.collection('users')
.doc(uid).get();
So for this I need to use nested stream builder. First stream builder to fetch all data. Second stream builder to fetch user requested data from all data. what will be the best approach?
This is how i am using query 1 in my widgets for the query 2 I have to implement it inside the ListView.builder which will be the nested stream. please guide me with the best approach to this.
SafeArea(
child: Scaffold(
body: StreamBuilder<QuerySnapshot>(
stream: _fetchUserChatRoom.snapshots(),
builder:
(BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
return _tiles(snapshot.data.docs);
} else if (snapshot.hasError) {
return Icon(Icons.error_outline);
} else {
return CircularProgressIndicator();
}
}),
),
);
}
Widget _tiles(List<QueryDocumentSnapshot> docs) {
return ListView.builder(
itemCount: docs.length,
itemBuilder: (BuildContext context, int index) {
var data = ChatModel.fromMap(docs[index].data());
return GestureDetector(
onTap: () => Navigator.of(context).push(MaterialPageRoute(
builder: (_) => ChatScreen(uid: docs[index].id))),
child: ListTile(
leading: CircleAvatar(),
title: Text(data.message),
subtitle: Text(data.timestamp.toString()),
trailing: Text('time'),
),
);
});
You can either use async and await in your ListView.builder, however, I imaging this could slowdown you app and cause a lot of firestore calls.
Widget _tiles(List<QueryDocumentSnapshot> docs) {
return ListView.builder(
itemCount: docs.length,
itemBuilder: (BuildContext context, int index) async {
var data = ChatModel.fromMap(docs[index].data());
var userData = await fetchUserData(data[index].uid);
return GestureDetector(
onTap: () => Navigator.of(context).push(MaterialPageRoute(
builder: (_) => ChatScreen(uid: docs[index].id))),
child: ListTile(
leading: CircleAvatar(),
title: Text(data.message),
subtitle: Text(data.timestamp.toString()),
trailing: Text('time'),
),
);
});
Other options (which I use) is to use a Provider class with all the contacts. You can fill the Provider when the app initializes with all the users in your firestore. After that you can use each user data anywhere in your app.

Display images of Firebase Storage with a Firestore query to get image url in Flutter

I am trying to fill a GridView.builder with images from my Firebase storage. I want to get the url from my Firestore database with a query. I couldn't find any good tutorial and tried to build the function by myself but it didn't work. In the FutureBuilder I print the snapshot variable that should have the data variable, but it has not.
Container(
margin: EdgeInsets.all(5),
padding: EdgeInsets.all(5),
height: deviceHeight * 0.35,
width: deviceWith,
color: Colors.white,
child: FutureBuilder(
future: getImages(),
builder: (context, AsyncSnapshot<dynamic>snapshot) {
print(snapshot);
if (snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.docs.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
contentPadding: EdgeInsets.all(8.0),
title: Text(snapshot.data.docs[index].data()["name"]),
leading: Image.network(
snapshot.data.docs[index].data()["url"],
fit: BoxFit.fill),
);
});
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
}
return CircularProgressIndicator();
},
),
),
Future getImages() {
var data;
page.where("userID",isEqualTo: FirebaseAuth.instance.currentUser.uid).snapshots().listen((data){
return data.docs;
});
return data;
}
I have only changed the Future function from the tutorial, not the FutureBuilder variables but I know that I have to change them.
Change the future method to the following:
Future<QuerySnapshot> getImages() async {
return await page.where("userID",isEqualTo: FirebaseAuth.instance.currentUser.uid).get();
}
get() returns a Future<QuerySnapshot>

Flutter display firebase storage image from path

I have been reading some questions around this and I can't find anything really that is relevant to what I am trying to do, and what I am trying to do is very simple.
I have the path of an image file from Firebase Storage, say:
SharedPreferences prefs = await SharedPreferences.getInstance();
String fileName = prefs.getString('currentProfileImage');
StorageReference storageReference = FirebaseStorage.instance
.ref()
.child(widget.currentUserUID.toString() + '/profile/' + fileName);
So the image file path will be something like:
b8sJ7cEHCFdjh46wZNp5xThbvVzz2/profile/image_picker209656937087714.jpg
I just want to display this image. That's all. How can I convert knowing the exact path to this file into an object of type Image?
You need to save the image first to Firebase Storage using putFile():
StorageTaskSnapshot snapshot = await storage
.ref()
.child(widget.currentUserUID.toString() + '/profile/' + fileName)
.putFile(file)
.onComplete;
Then you get the url of the image in Firebase Storage and save it to Firestore, for example:
final String downloadUrl =
await snapshot.ref.getDownloadURL();
await Firestore.instance
.collection("images")
.add({"url": downloadUrl, "name": imageName});
Then to display the image you can do the following:
body: Container(
padding: EdgeInsets.all(10.0),
child: FutureBuilder(
future: getImages(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return ListView.builder(
shrinkWrap: true,
itemCount: snapshot.data.documents.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
contentPadding: EdgeInsets.all(8.0),
title:
Text(snapshot.data.documents[index].data["name"]),
leading: Image.network(
snapshot.data.documents[index].data["url"],
fit: BoxFit.fill),
);
});
} else if (snapshot.connectionState == ConnectionState.none) {
return Text("No data");
}
return CircularProgressIndicator();
},
),
),
/// code here
Future<QuerySnapshot> getImages() {
return fb.collection("images").getDocuments();
}
getImages() will retrieve the data inside the collection, then Image.network will take the url of the image and display the image in the application.

How to arrange cloud firestore documents in serial wise with flutter?

I'm trying to create chat demo app with firebase in flutter but when i send message, then those message documents are being created at randomly any places in firestore database. thats why my chat screen messages are in wrong manner, means not arranged according to time.
Some piece of code:
method for saving message details to firestore:
Future<void> sendMesssage() async{
if(messagesController.text.length>0){
String msgId = firestore.collection("messages").document().documentID.toString();
await firestore.collection("messages").document(msgId).setData({
'text': messagesController.text,
"from": widget.user.user.email, //sender email Id
"to":widget.chatMateEmail, // receiver email id
"msgId":msgId,
"senderUid": widget.user.user.uid, //sender uid
"receiverUid":widget.receiverUid //receiver uid
});
messagesController.clear();
}
}
UI For chat screen:
method for fetching messages from firestore:
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: firestore.collection("messages").snapshots(),
builder: (context, snapshot){
if(snapshot.hasError){
return Center(child: Text("${snapshot.error}"),);
}
if(!snapshot.hasData){
return Center(child: CircularProgressIndicator(),);
}else{
List<DocumentSnapshot> docs = snapshot.data.documents;
return
Padding(
padding: const EdgeInsets.all(8.0),
child: ListView.builder(
itemCount: docs.length,
itemBuilder: (context, index){
print("Messagessssssss:${docs[index]['text']}");
return Message( // custom class
from: docs[index]['from'],
text: docs[index]['text'],
me: widget.user.user.email == docs[index]['from'],
);
},
),
);
}
},
),
),
chat Screen:
My Cloud Firestore Screenshot:
It got solved according to #DougStevenson sir's answer, I added new field with name "messageTime" and add DateTime.now(), And fetched messages according to messageTime (sort by ascending order ).
I modified a little bit my code & working perfectly:
Future<void> sendMesssage() async{
if(messagesController.text.length>0){
String msgId = firestore.collection("messages").document().documentID.toString();
await firestore.collection("messages").document(msgId).setData({
..........
"messageTime": DateTime.now() // message DateTime
});
messagesController.clear();
}
}
Expanded(
child: StreamBuilder<QuerySnapshot>(
stream: firestore.collection("messages").orderBy('messageTime', descending: false).snapshots(), //and sort by ascending order according to datetime.
builder: (context, snapshot){
......
},
),
),

I'm Having trouble accesing my data in firestore with flutter

im getting this two errors in my debug console (core_booster, getBoosterConfig = false) and (Could not reach Firestore backend.) In my firestore data i ve got a Collection "Recipes" and then in de documents i ve got each recipe with its own attribute.
Here i leave you a sneek peek of the code.
new StreamBuilder(
stream: Firestore.instance.collection('Recipes').snapshots(),
builder: (context, snapshot) {
if (!snapshot.hasData)
return const Center(child: CircularProgressIndicator());
return ListView.builder(
itemCount: snapshot.data.documents.length,
itemBuilder: (context, index) =>
_buildListRecipe(context, snapshot.data.documents[index]),
);
});
Then in my _buildListRecipe I'm accessing each in the value of each recipe.
new Image.network(
document["firstImage"],
width: double.infinity,
height: 150.0,
fit: BoxFit.cover,
),
did you resolve your issue and do you remember how? I reach the exact same problem, I don't found any response or tips to resolve it.
Here, a bit of me code :
void _listenToUserDatabase(String key) async {
_userStream = _usersDatabase.child(key).onValue.listen((event) {
if (event.snapshot.value != null) {
final String source = jsonEncode(event.snapshot.value);
final Map<String, dynamic> json = jsonDecode(source);
_user = UserModel.fromJson(json, key: key);
_userKey = key;
notifyListeners();
}
}, onError: (e) {
print("Listen to user database error $e");
}, onDone: () {
print("listen done");
});
}

Resources