I have a model and I want to use my services file to fill it from Firebase but I don't know how to do that ?
I am filling it with FutureBuilder that's okey. But it is exhausting me.
Here is my model:
class ReviewModel {
String? uid;
String? userID;
String? comment;
dynamic rate;
ReviewModel({
this.uid,
this.userID,
this.comment,
this.rate,
});
Map<String, dynamic> toMap() {
return {
'uid': uid,
'userID': userID,
'comment': comment,
'rate': rate,
};
}
factory ReviewModel.fromMap(Map<String, dynamic> map) {
return ReviewModel(
uid: map['uid'],
userID: map['userID'],
comment: map['comment'],
rate: map['rate'],
);
}
factory ReviewModel.fromDatabase(
DocumentSnapshot snapshot, Map<String, dynamic> map) {
return ReviewModel(
uid: snapshot['uid'],
userID: map['userID'],
comment: map['comment'],
rate: map['rate'],
);
}
}
Code is Following below,
Future<ReviewModel> getSalonReviews(String salonUID) async {
CollectionReference aRef = FirebaseFirestore.instance
.collection("salons")
.doc(salonUID)
.collection('bucket')
.doc('reviewbox')
.collection('reviews');
dynamic _doc;
var snapshot;
try {
await aRef.get().then((querySnapshot) => {
for (var dummyDoc in querySnapshot.docs)
{
_doc = dummyDoc.data(),
print(_doc),
}
});
return ReviewModel.fromMap(_doc);
} on FirebaseException catch (e) {
Get.snackbar("Hata", e.code);
rethrow;
}
}
This code is not returning my ReviewModel.
Also I am using GetX and this is my GetX code:
final Rx<ReviewModel> _reviewModel = ReviewModel().obs;
ReviewModel get reviewModel => _reviewModel.value;
set reviewModel(ReviewModel value) => _reviewModel.value;
Future fillReviewModel(String uid) async {
SalonController.instance.reviewModel =
await FakeService().getSalonReviews(uid);
}
it return me this:
And this is my Firebase docs:
How do I achive my ReviewModel with Obx. If I try it, it returns null.
You don't have to return a model you'll do something like this in your prvoider file:
List _reviews = [];
List get reviews => [..._reviews];
// IN your future void function
Future<void> myFunction () async{
myReviews = ...result of forEach;
// now update _reviews
_reviews = [...myReviews];
//And then notify listeners
notifylisteners;
}
And then in your futurebuilder
FutureBuilder(future: Provider.of<myClass>(context, listen:false).myFunction(),
builder:(context, snapshot){
// check the state like the following
if(snapshot.connectionState == ConnectionState.done){
final myValues = Provider.of<myClass>(context, listen:false).reviews;
...do something
return your_values}
if(snapshot.connectionState == ConnectionState.waiting){return progressIndicator}
})
Related
In my app I have a model which consists of the store name and store image and looks like this:
class StoreModel
{
String? imageofStore;
String? storeName;
StoreModel({ this.imageofStore, this.storeName});
//data from server
factory StoreModel.fromMap(map)
{
return StoreModel(
imageofStore: map['imageofStore'],
storeName: map['storeName'],
);
}
// data to server
Map<String, dynamic> toMap(){
return{
'imageofStore': imageofStore,
'storeName': storeName,
};
}
}
and my database for stores looks like this:
to call the store name I use initstate and setState as such:
class addStore extends StatefulWidget {
const addStore({Key? key}) : super(key: key);
#override
_addStoreState createState() => _addStoreState();
}
class _addStoreState extends State<addStore> {
User ? user = FirebaseAuth.instance.currentUser;
StoreModel storebox = StoreModel();
#override
void initState()
{
super.initState();
FirebaseFirestore.instance
.collection("stores")
.doc("XQjbm665g2a2xAiiydjr")
.get()
.then((value){
this.storebox = StoreModel.fromMap(value.data());
setState(() {
});
});
}
#override
Widget build(BuildContext context) {
return Container(
child: Text("${storebox.storeName}"),
);
}
}
With this, I get the store name of the store with id XQjbm665g2a2xAiiydjr displaying but the thing is I want to get the name of all the stores. I know I need to change the .doc() but im not sure as to what I am to put in it that will start displaying all the names. Can someone please help?
By providing a document id, what you're getting is a DocumentSnapshot which is the data of a particular Document, but when you remove it, you get QuerySnapshot which is a list of the data of all the documents. So, to read all, you change your code as:
List<StoreModel> storesList = [];
FirebaseFirestore.instance
.collection("stores")
.get()
.then((value){
//Now, this value is of type QuerySnapshot unlike earlier.
if (value != null && value.docs.isNotEmpty) {
//If it comes here means the collection is not empty.
//Because this value is a list of DocumentSnapshot, We've to map it to extract documents.
//After mapping, returning it as a list and assigning it to storesList
storesList = value.docs.map((doc) => StoreModel.fromMap(doc.data())).toList();
setState(() {
});
} else {
//If it comes here means there are no documents in your collection.
//Notify User there's no data.
}
});
This code will get all the documents your collection have, but, you can limit or filter using limit or where respectively, just place .limit(int) or .where() before .get().
for model try to convert to json or you can use your either way this is just for example model
import 'dart:convert';
List<StoreModel> storeModelFromJson(String str) => List<StoreModel>.from(json.decode(str).map((x) => StoreModel.fromJson(x)));
String storeModelToJson(List<StoreModel> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));
class StoreModel {
StoreModel({
this.imageofStore,
this.storeName,
});
final String? imageofStore;
final String? storeName;
factory StoreModel.fromJson(Map<String, dynamic> json) => StoreModel(
imageofStore: json["imageofStore"] == null ? null : json["imageofStore"]!,
storeName: json["storeName"] == null ? null : json["storeName"]!,
);
Map<String, dynamic> toJson() => {
"imageofStore": imageofStore == null ? null : imageofStore!,
"storeName": storeName == null ? null : storeName!,
};
}
try this get all list
static Future<List<StoreModel>> getStorelist() async {
List<dynamic> list = [];
await FirebaseFirestore.instance
.collection("stores")
.get()
.then((value){
for(var x in value.docs){
final Map<String,dynamic> toMap = x.data() as Map<String,dynamic>;
/// Try to print all data first to see if fetching use log to view on terminal
log(toMap.toString());
list.add(toMap);
}
});
return list.map((e)= > StoreModel.fromJson(e)).toList();
}
as in my case to get only 1 data
static getSingleName(String? uid) async {
final result =
await FirebaseFirestore.instance
.collection("stores").doc(uid).get();
final response = result.data() as Map<String, dynamic>;
return response['storeName'].toString();
}
I have a button with an onPressed function that returns _shopActivatePage function. It is meant to navigate to either ShopActivatePage() or EmptyContent(), depending on a Future<bool>. However, this function returns the else statement even if there's data in my stream. Without the if else, this function works fine.
The code for the stream is:
#override
//print all docs from firebase collection
Stream<List<Shop>> shopStream() => _service.collectionStream(
path: APIPath.shops(uid),
builder: (data, documentId) => Shop.fromMap(data, documentId),
);
and the function is:
void _shopActivatePage (BuildContext context) async{
final db = Provider.of<Database>(context, listen: false);
if (await db.shopStream().isEmpty) {
Navigator.of(context).push(MaterialPageRoute<void>(
builder: (context) => ShopActivatePage(),
fullscreenDialog: true,
));
} else{
Navigator.of(context).push(MaterialPageRoute<void>(
builder: (context) => EmptyContent(
title: 'Shop activated',
message: 'Your shop has been set up',
),
fullscreenDialog: true,
));
}
}
Thank you so much in advance and sorry if this is an amateur question. Any suggestions is appreciated.
I give you my FirestoreService. I thought you were using one like this.
So in your repository declare
final _service = FirestoreService.instance;
My FirestoreService :
class FirestoreService {
FirestoreService._();
static final instance = FirestoreService._();
void setData({
required String path,
required Map<String, dynamic> data,
}) {
final reference = FirebaseFirestore.instance.doc(path);
reference.set(data, SetOptions(merge: true));
}
Future<void> setDataWithAwait({
required String path,
required Map<String, dynamic> data,
}) async {
final reference = FirebaseFirestore.instance.doc(path);
return reference.set(data, SetOptions(merge: true));
}
void updateData({
required String path,
required Map<String, dynamic> data,
}) {
final reference = FirebaseFirestore.instance.doc(path);
reference.update(data).catchError((onError) {});
}
Future<void> updateDataWithAwait({
required String path,
required Map<String, dynamic> data,
}) async {
final reference = FirebaseFirestore.instance.doc(path);
return reference.update(data).catchError((onError) {});
}
void deleteDoc({required String path}) {
final reference = FirebaseFirestore.instance.doc(path);
reference.delete().catchError((onError) {});
}
Future<void> deleteCollection({required String path}) async {
final reference = FirebaseFirestore.instance.collection(path);
final query = await reference.get();
for (final doc in query.docs) {
doc.reference.delete();
await Future.delayed(const Duration(milliseconds: 200));
}
}
Stream<List<T>> collectionStream<T>({
required String path,
required T Function(Map<String, dynamic>? data) builder,
Query<Map<String, dynamic>?> Function(Query<Map<String, dynamic>?> query)?
queryBuilder,
int Function(T lhs, T rhs)? sort,
}) {
Query<Map<String, dynamic>?> query =
FirebaseFirestore.instance.collection(path);
if (queryBuilder != null) {
query = queryBuilder(query);
}
final Stream<QuerySnapshot<Map<String, dynamic>?>> snapshots =
query.snapshots();
return snapshots.map((snapshot) {
final result = snapshot.docs
.map((snapshot) => builder(snapshot.data()))
.where((value) => value != null)
.toList();
if (sort != null) {
result.sort(sort);
}
return result;
});
}
Stream<List<T>> collectionGroupStream<T>({
required String path,
T Function(Map<String, dynamic>? data)? builder,
Query<Map<String, dynamic>?> Function(Query<Map<String, dynamic>?> query)?
queryBuilder,
int Function(T lhs, T rhs)? sort,
}) {
Query<Map<String, dynamic>?> query =
FirebaseFirestore.instance.collectionGroup(path);
if (queryBuilder != null) {
query = queryBuilder(query);
}
final Stream<QuerySnapshot<Map<String, dynamic>?>> snapshots =
query.snapshots();
return snapshots.map((snapshot) {
final result = snapshot.docs
.map((snapshot) => builder!(snapshot.data()))
.where((value) => value != null)
.toList();
if (sort != null) {
result.sort(sort);
}
return result;
});
}
Future<List<T>> collectionFuture<T>({
required String path,
required T Function(Map<String, dynamic>? data) builder,
Query<Map<String, dynamic>?> Function(Query<Map<String, dynamic>?> query)?
queryBuilder,
int Function(T lhs, T rhs)? sort,
}) async {
Query<Map<String, dynamic>?> query =
FirebaseFirestore.instance.collection(path);
if (queryBuilder != null) {
query = queryBuilder(query);
}
final QuerySnapshot<Map<String, dynamic>?> docs = await query
.get()
.timeout(const Duration(seconds: timeOutSecond), onTimeout: () {
throw Exception();
});
final result = docs.docs
.map((e) => builder(e.data()))
.where((element) => element != null)
.toList();
if (sort != null) {
result.sort(sort);
}
return result;
}
}
I have this method the get all my documents from a given collection:
getData() async {
await databaseReference
.collection("app").doc('usr').collection(_id).
.get()
.then((querySnapshot) {
querySnapshot.docs.forEach((result) {
return result.data();
});
})
}
What I want to get is all the documents from this collection, and not only the last. With the code above I get this when calling getData()
Instance of 'Future < dynamic>'
What I want to get:
[{name: Victor, age: 18}, {name: Tommy, age: 40}]
How can I reach it?
UPDATE
If I run the code below...:
await databaseReference
.collection("app").doc('usr').collection(_id)
.get()
.then((QuerySnapshot snapshot) {
snapshot.docs.forEach((f) => print(f.data()));
})
In the console it prints all the documents but separately (First prints one, after another):
I/flutter (16316): {name: Victor, age: 18}
I/flutter (16316): {name: Tommy, age: 40}
UPDATE 2
If I write what #Sahil Chadha and #kovalyovi suggest, and just print the list ... :
var items = List<dynamic>();
... my code....
snapshot.docs.forEach((f) => items.add(f.data()));
return items;
//returns exactly what I want
... It returns exactly what I want, but if I write return items and in the calling do var a = getData();, The A value is Instance of future. How can I have the result expected?
UPDATE 3
I forgot the await before getData(). Now it's working:
var a = await getData();
print(a); //my expected result
getData() async {
var items = List<dynamic>();
await databaseReference
.collection("app").doc('usr').collection(_id)
.get()
.then((QuerySnapshot snapshot) {
snapshot.docs.forEach((f) => items.add(f.data()));
})
return items
}
To be able to store the data you receive in a form of a list, you will need to initialize a list at the beginning of the method and then populate that list where you forEach the response. As you mentioned in the comments, I am posting an answer here for you:
getData() async {
// initialize your list here
var items = List<dynamic>();
await databaseReference.collection("app").doc('usr').collection(_id).get().then((QuerySnapshot snapshot) {
snapshot.docs.forEach(
// add data to your list
(f) => items.add(f.data()),
);
});
return items;
}
Like there is no problem with the code. It will be healthier if you just model the data you get. You just did not mention that you were async where you were calling. Since it is future data, there must be a waiting event.
Future<List<User>> getData() async {
await databaseReference
.collection("app").doc('usr').collection(_id).
.get()
.then((querySnapshot) {
querySnapshot.docs.map((f) =>
User.fromJson(f)).toList());
})
}
You should try two of them.
List<User> loaddata=await getData();
--OR------
var loadData;
getData().then((result){
List<User>=result;
});
and you create the user model. It will be more revealing if you do it this way.
import 'dart:convert';
User userFromJson(String str) => User.fromJson(json.decode(str));
String userToJson(User data) => json.encode(data.toJson());
class User {
User({
this.name,
this.age,
});
String name;
int age;
factory User.fromJson(Map<String, dynamic> json) => User(
name: json["name"],
age: json["age"],
);
Map<String, dynamic> toJson() => {
"name": name,
"age": age,
};
}
If there is a problem in the code, you are calling the getData method in the build widget. So you have to call it from initstate.
So I am using the nearby connections API to discover devices around me and store their data in firestore however I keep getting 2 warnings about the location I am getting from the user that I came in contact with and the time i came in contact with them
These are the 2 warnings:
1)DateTime not a subtype of type TimeStamp
2)Unhandled Exception: Invalid argument: Instance of Future<.LocationData.>
as I try to add these values to firestore
here is my discovery method:
void discovery() async {
try {
bool a = await Nearby().startDiscovery(loggedInUser.email, strategy,
onEndpointFound: (id, name, serviceId) async {
print('I saw id:$id with name:$name'); // the name here is an email
var docRef =
_firestore.collection('users').document(loggedInUser.email);
// When I discover someone I will see their email
docRef.collection('met_with').document(name).setData({
'email': await getUsernameOfEmail(email: name),
'contact time': DateTime.now() as Timestamp ,
'contact location': location.getLocation(),
});
}, onEndpointLost: (id) {
print(id);
});
print('DISCOVERING: ${a.toString()}');
} catch (e) {
print(e);
}
}
This is another method where I retrieve the info I discovered from firestore:
void addContactsToList() async {
await getCurrentUser();
_firestore
.collection('users')
.document(loggedInUser.email)
.collection('met_with')
.snapshots()
.listen((snapshot) {
for (var doc in snapshot.documents) {
String currEmail = doc.data['email'];
DateTime currTime = doc.data.containsKey('contact time')
? (doc.data['contact time'] as Timestamp).toDate()
: null;
String currLocation = doc.data.containsKey('contact location')
? doc.data['contact location']
: null;
String _infection = doc.data['infected'];
if (!contactTraces.contains(currEmail)) {
contactTraces.add(currEmail);
contactTimes.add(currTime);
contactLocations.add(currLocation);
infection.add(_infection);
}
}
setState(() {});
print(loggedInUser.email);
});
}
Any fix for this please?
Use an async function to convert the Future<.LocationData.> to LocationData.
var data;
void convertData() async{
var futuredata = await FutureLocationData;
setState(() {
data = futuredata });
}
i want to send coupon card to fire store that contain ( name - address - coupon ) and i want to make user set an specific image for every single card
that's my FireStoreService file
class FireStoreService {
FireStoreService._internal();
static final FireStoreService firestoreService = FireStoreService._internal();
Firestore db = Firestore.instance ;
factory FireStoreService() {
return firestoreService;
}
Stream<List<Coupon>> getCoupon() {
return db.collection('coupon').snapshots().map(
(snapshot) => snapshot.documents.map(
(doc) => Coupon.fromMap(doc.data, doc.documentID),
).toList(),
);
}
Future<void> addCoupon(Coupon coupon) {
return db.collection('coupon').add(coupon.toMap());
}
Future<void> deleteCoupon(String id) {
return db.collection('coupon').document(id).delete();
}
Future<void> updateCoupon(Coupon coupon) {
return db.collection('coupon').document(coupon.id).updateData(coupon.toMap());
}
}
and this is Coupon Model file
class Coupon {
final String id;
final String storeName;
final String storeLink;
final String couponCode;
Coupon(
{this.id, this.storeName, this.storeLink, this.couponCode});
Coupon.fromMap(Map<String, dynamic> data, String id)
: storeName = data["storeName"],
storeLink = data['storeLink'],
couponCode = data["couponCode"],
id = id;
Map<String, dynamic> toMap() {
return {
"storeName": storeName,
'storeLink': storeLink,
'couponCode': couponCode,
};
}
}
and this is Image Picker code and it's work fine and picked up the image
Future getImage() async {
try {
File image = await ImagePicker.pickImage(source: ImageSource.gallery);
setState(() {
image = image;
});
} catch (e) {
print(e);
}
}
any help ?
This is a function that asks for an imageFile. If you run your code (getImage function): pass the image variable to the uploadImage function.
String myUrl = await uploadImage(file);
Then you can use setData or updateData to put the url in the database.
Firestore.instance.collection('books').document()
.setData({ 'title': 'title', 'url': '$myUrl' })
final StorageReference storageRef = FirebaseStorage.instance.ref();
Future<String> uploadImage(imageFile) async {
StorageUploadTask uploadTask =
storageRef.child("myPath&Name.jpg").putFile(imageFile);
StorageTaskSnapshot storageSnap = await uploadTask.onComplete;
String downloadURL = await storageSnap.ref.getDownloadURL();
return downloadURL;
}