Can not put firestore data into flutter list - firebase

I've tried to put my firestore data which is scrapped image links into flutter list (var imageList). However, there is a 'RangeError (index): Invalid value: Valid value range is empty: 0' and not worked.
Printed imageList shows like list in list [['link1.jpg','link2.jpg2',link3.jpg3',......]]
My firestore data is one field and several values please, check bellow captured image link.
https://imgur.com/a/7Mt0lOQ
final firestore = FirebaseFirestore.instance;
class ProfileController extends GetxController {
List imageList = [];
getData() async {
try {
var imgJson = await firestore.collection('insta-scrap1').get();
for (var doc in imgJson.docs) {
imageList.add(doc['data']);
}
print(imageList); }

use doc.data() instead of doc['data']
for (var doc in imgJson.docs) {
imageList.add(doc.data()['data']);
}

Related

How to make a Model Class Within Model Class (Flutter / Dart)

I am trying to make a multivendor app with Flutter.
The problem I am facing right now is how can I make a model class within a Model class:
Example:
class ProductModel {
String? name;
double? price;
String? imgUrl;
double? weight;
ProductModel();
ProductMode.fromSnapShot(DocumentSnapshot snapshot){
name = snapshot['name'];
price= snapshot['price'];
imgUrl= snapshot['imgUrl'];
weight= snapshot['weight'];
}
}
I have already retrieved all the products from Firestore:
class VendorModel{
String? name;
String? imgUrl;
DateTime? opensAt;
DateTime? closesAt;
VendorModel();
VendorModel.fromSnapshot(DocumentSnapshot snapshot){
name = snapshot['name'];
imgUrl= snapshot['imgUrl'];
opensAt= DateTime.tryParse(snapshot['opensAt'].toString());
closesAt= DateTime.tryParse(snapshot['closesAt'].toString());
}
}
I have also retrieved all the vendors from Firestore:
Here is What I am trying to do:
I would like to add the products I fetched from Firestore to their respective vendors using this model. But I don't know how to make this if my data is from Firestore.
class VendorWithProductsModel{
VendorModel vendor;
List<ProductModel> products;
VendorWithProductsModel();
(I AM STUCK HERE)
}
Here is a Snippet of my Firestore code in retrieving products and vendors:
VendorModel _vendor = VendorModel();
List<VendorModel> _allVendors = [];
Future<VendorModel> getCertainVendor(docId) async{
await vendor.doc(docId).get().then((value){
_vendor = VendorModel.fromSnapshot(value);
});
return _vendor;
}
Future<List<VendorModel>> getAllVendors() async{
await vendor.get().then((value){
_allVendors.add(VendorModel.fromSnapshot(value));
});
return _allVendors;
}
In your product model, you need something like vendorId, where you can know which product belongs to which vendor.
After fetching all your products and vendors. Use both list, and loop through them, this is pseudo code for example:
List<VendorWithProductsModel> doMagic (List<ProductModel> productList, List<VendorModel> vendorList) {
List<VendorWithProductsModel> vendorsWithProducts =[];
vendorList.forEach((vendor){
VendorWithProductsModel _vendorAndProduct = VendorWithProductsModel();
_vendorAndProduct.vendor = vendor;
_vendorAndProduct.products =[];
_vendorAndProduct.products = productList.where((e)=> e.vendorId == vendor.id).toList();
vendorsWithProducts.add(_vendorAndProduct);
});
return vendorsWithProducts;
}

How to return Future<List<Object>> from List<DocuementReference>>?

I am trying to create a method to convert a List to Future<List>.
This is the method I created.
static Future<List<Product?>> fromProductRefList(
List<DocumentReference> ref) async {
List<Product> shopProductList = [];
ref.forEach((productRef) async {
final productDoc = productRef.get();
final product = await Product.fromDocument(await productDoc);
shopProductList.add(product!);
});
print('shopProductList: $shopProductList');
return shopProductList;
}
and called it in cubit,
void mapProductToState() async {
emit(state.copyWith(status: MyProductStatus.loadding));
final shop = _shopBloc.state.shop;
List<Product?> productList = [];
if (shop.shopProductRef.isNotEmpty) {
final productList = Product.fromProductRefList(shop.shopProductRef);
}
emit(state.copyWith(
shop: shop,
productList: productList,
status: MyProductStatus.loaded,
));
}
VScode shows no error but when I run the code, fromProductRefList return empty list. Seems like fromProductRefList did not wait for the Document actually get() from the database and just return.
When I add a second delay in fromProductRefList before returning the shopProductList, everything works as expected.
I have read another question on stackoverflow suggest using asyncMap() but I am not sure how to apply it in my case.
Edit:
When I add a delay, the method return without any issue. If not, it will return a empty list
static Future<List<Product?>> fromProductRefList(
List<DocumentReference> ref) async {
List<Product> shopProductList = [];
ref.forEach((productRef) async {
final productDoc = productRef.get();
final product = await Product.fromDocument(await productDoc);
shopProductList.add(product!);
});
await Future.delayed(const Duration(milliseconds: 500));
print('shopProductList: $shopProductList');
return shopProductList;
}
Thank you.
You are missing the await keyword, to actually wait for the call. It only compiles, because you also declare a new variable of name productList, shadowing the already existing one.
So this line:
final productList = Product.fromProductRefList(shop.shopProductRef);
should read:
productList = await Product.fromProductRefList(shop.shopProductRef);
In addition, this does not do what you think it does:
ref.forEach((productRef) async {
It does not wait for each call. Please use a normal for flow control structure and await the async call, not the forEach method. The forEach method will no wait for the Futures returned from the methods.

Firestore Flutter How to get a list all the documents and its data inside a collection?

I have been working to get a list of all the documents inside a firestore collection. I want to display all details of all documents inside a collection.
My document tree is ask follows-
'groups' COLLECTION----->Documents w 'groupID' as reference------>'tasks' COLLECTION------>Documents w 'taskId' as reference.
Now I want to get all documents and its details inside 'tasks' collection for a particular groupID.
Future<MyTask> getCurrentTask(String groupId) async {
MyTask retVal = MyTask();
try {
DocumentSnapshot _docSnapshot =
await _firestore.collection("groups").document(groupId).collection("tasks").get();
retVal.taskId = taskId;
retVal.taskName = _docSnapshot.data['taskName'];
retVal.dueTime = _docSnapshot.data['dueTime'];
retVal.member =_docSnapshot.data['member'];
retVal.completed = _docSnapshot.data['completed'];
} catch (e) {
print(e);
}
return retVal;
}
I tried this but it doesnt work as "The method 'get' isn't defined for the type 'CollectionReference'."
How to get around this please?
Simply do like this:
Firestore.instance.collection("groups").document(groupID).collection("task").snapshots().listen((event) {
retVal = event.documents.map((e) => MyTask.fromJson(e.data)).toList();
});
I assume your MyTask model already have fromJson method so do it like that. And change your retVal to List: List<MyTask> retVal = [];. It will get all of your document and also listen whether there's change's on the collection.

How to save a List of type map into firestore, and then read/write to the created maps?

I am trying to create a small app were the user can create flash cards. At first, I have them create the title, then all titles will be listed, once the user clicks on that title they'll be taken to a different screen where they can create the question and answer.
My Issue is that I created a List of type Map but can't figure out how to add and save to the lists that are created in the maps.
Model
class Cards {
//final List<String> question;
//final List<String> answer;
//final String title;
final String uid;
final List<Map<String, dynamic>> classes;
Cards({ this.uid, this.classes });
}
Home
This is where it starts, the elList.add only runs once when the user creates a title.
Cards indexData = snapshot.data;
List<Map<String, dynamic>> elList = [];
for (var i = 0; i < indexData.classes.length; i++) {
elList.add(indexData.classes[i]);
}
elList.add({
"title": title,
"question": [],
"answer": []
});
DatabaseService(uid: userId.uid).settingUserData(elList);
// Send the values to another screen where the user creates Q&A
MaterialPageRoute(
builder: (context) => ViewIndex(
questions: elList[index]["question"],
answers: elList[index]["answer"]
),
),
Service
This is where it gets saved to the db. My issue here is trying to figure out how to add on to the lists inside the map because this only adds to the list classes, which indexes all the maps. This runs when the title is created. Trying to figure out how to add to lists inside of map and save them? I tried something like: "classes[question]" and "classes.question", but none work.
Future settingUserData(List<Map<String, dynamic>> listCard) async {
return await _collref.document(uid).setData({
"classes": listCard
});
}
ViewIndex
This is where I receive them from the home file as params via the widget. This is where the user creates the questions and answers. Trying to add to them gives me an error, saying they are fixed-length. So here is where I also need to save them to the database, but as seen in my service file above, I don't know how to save these separately without having to create a whole new index to the list classes, which isn't what I want.
widget.answers.add("foo")
widget.questions.add("foo")
DatabaseService(uid: userId.uid).settingUserData();
I think the better solution for you is u define another class for your Classes as a model to become
class Cards {
//final List<String> question;
//final List<String> answer;
//final String title;
final String uid;
final List<YourClass> classes;
Cards(this.uid, this.classes);
}
class YourClass {
final Map<String, dynamic> yourObject;
YourClass(this.yourObject);
}
Then, initialized yourClass with the object you wanted, and assign it to your Cards.
List<YourClass> classes = List<YourClass>();
Map<String, dynamic> myItem = jsonDecode('{"hello": "dart","world": {"json": "blabla"}}');
// create your classItem
YourClass class1 = YourClass(myItem);
// add your classItem to a your classes list
classes.add(class1);
// add your classes to your card
Cards card = Cards('myUid', classes);
print(jsonEncode(card.classes[0].yourObject['hello']));
play around here: example
I can't believe I didn't notice this... But all I needed to do was to add the passed arguments from home to view index AND then move them to an array and loop through them
ViewIndex
List<dynamic> userAnswer = [];
List<dynamic> userQuestion = [];
for (var i = 0; i < widget.questions.length; i++) {
userQuestion.add(widget.questions[i]);
}
for (var i = 0; i < widget.answers.length; i++) {
userAnswer.add(widget.answers[i]);
}
DatabaseService(uid: userId.uid)
.updattingUserData(widget.index, userQuestion, userAnswer);
Service
Here I then used dot notation to read and write to that specific list in the maps. I also needed to pass the index to know, well, to know which one I was writing to. I made a separate function for this. The reason it was a fixed length was that I didn't have it in a list.
Future updattingUserData(int index, List<dynamic> question, List<dynamic> answer) async {
return await _collref.document(uid).updateData({
"classes.$index.questions": FieldValue.arrayUnion(question),
"classes.$index.answers": FieldValue.arrayUnion(answer)
});
}
Everything else is exactly the same as I have in my First post.

Flutter firebase_database get children

Hi I want to deserialise the snapshot from the realtime database to a Company object and add it to a _companies list.
But I keep getting an error...
This is what I have so far:
List<Company> _companies = [];
#override
void initState() {
// TODO: implement initState
super.initState();
getItems().then((list){
print("Now the list is here");
setState(() {
for (int i=0; i < list.length; i++) {
Map<String, dynamic> map = list[i];
Company company = new Company.fromMap(map);
_companies.add(company);
}
});
});
}
static Future<List> getItems( ) async {
Completer<List> completer = new Completer<List>();
FirebaseDatabase.instance
.reference()
.child("Companies")
.once()
.then( (DataSnapshot snapshot) {
List map = snapshot.value; //It fails here
completer.complete(map);
} );
return completer.future;
}
This is my Company class:
class Company {
String key;
String category;
String company_name;
String company_url;
String country;
String description;
String email;
String faq_url;
String instagram_url;
String logo_url_image;
String p_category;
String parent_company_ok;
String timestamp;
Company();
Company.fromSnapshot(DataSnapshot snapshot)
: key = snapshot.key,
category = snapshot.value['category'],
company_name = snapshot.value['company_name'],
company_url = snapshot.value['company_url'],
country = snapshot.value['country'],
description= snapshot.value['description'],
email = snapshot.value['email'],
faq_url = snapshot.value['faq_url'],
instagram_url = snapshot.value['instagram_url'],
logo_url_image = snapshot.value['logo_url_image'],
p_category = snapshot.value['p_category'],
parent_company_ok = snapshot.value['parent_company_ok'],
timestamp = snapshot.value['timestamp'];
}
How ever, it fails in getItems( ) on List map = snapshot.value;. With the exeption: _InternalLinkedHashMap<dynamic, dynamic>' is not a subtype of type 'List<dynamic>'
Also if anyone can provide the code for showing the children in a List widget I would be very happy :)
Here is my data structure in firebase realtime database:
Well as the error message says snapshot.value is not a List but it is a map so the line List map = snapshot.value; will always fail. Since you are reading Companies node tree from your database this will return a map of maps, something like Map<String, Map<dynamic,dynamic>> with all data from respective node. I will provide you a function that parse your Json data in Company object and i will left some comments in source code to try explain you the process. It's simple.
List<Company> _parseData(DataSnapshot dataSnapshot) {
List<Company> companyList =new List();
// here you replace List map = snapshot.value with... dataSnapshot.val()
Map<String, dynamic> mapOfMaps = Map.from( dataSnapshot.val() );
//actually dynamic here is another Map, String are the keys like -LXuQmyuF7E... or LXuSkMdJX...
//of your picture example and dynamic other Map
//in next lines we will interate in all values of your map
//remeber that the values are maps too, values has one map for each company.
//The values are Map<String, dynamic>
//in this case String are keys like category, company_name, country, etc...
//and dynamics data like string, int, float, etc
//parsing and adding each Company object to list
mapOfMaps.values.forEach( (value) {
companyList.add(
//here you'll not use fromSnapshot to parse data,
//i thing you got why we're not using fromSnapshot
Company.fromJson( Map.from(value) )
);
});
return companyList;
}
Note you will use Company.fromSnapshot(snapshot) when you read a specific company from
your db, something like ...
// just to illustrate
var snapshot = await FirebaseDatabase.instance
.reference()
.child("Companies")
.child(companyId).once();
Company.fromSnapshot( snapshot );
because in this case the snapshot.value is a single map.
Well another thing is, take a look at you initState method in your statefull widget, i don't know if is a good
approach call setState inside initState method even after a Future execution. This is just an advise.
snapshot.value returns a Map and you are using a List to store it. Maybe the easiest solution here is to use the var type to declare your map variable:
var map = snapshot.value;
but it's a good practice to set a type whenever you can to your variables. So, in this case the best option is to set it like this:
Map<dynamic, dynamic> map = snapshot.value;
Check dart's documentation to improve your skills in useing maps and list and how they work Lists docs Maps docs

Resources