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;
}
Related
I am trying to load all of my product data from firestore. I have a data schema:
class SingleProduct with ChangeNotifier {
static const EVENT = "event";
static const IMGURL = "imgUrl";
static const NAME = "name";
static const PRICE = "price";
//Private Variables
String _event;
String _imgUrl;
String _name;
double _price;
// getters
String get event => _event;
String get imgUrl => _imgUrl;
String get name => _name;
double get price => _price;
SingleProduct.fromSnapshot(DocumentSnapshot snapshot) {
_event = snapshot.data()[EVENT];
_imgUrl = snapshot.data()[IMGURL];
_name = snapshot.data()[NAME];
_price = snapshot.data()[PRICE];}
}
I have then created a class to map all the data received to a list of products:
class ProductServices {
FirebaseFirestore _firestore = FirebaseFirestore.instance;
String collection = 'products';
Future<List<SingleProduct>> getAllProducts() async =>
_firestore.collection(collection).get().then((snap) {
print(snap.docs.length); // returns 11 products as expected
List<SingleProduct> allProducts = [];
snap.docs.map((snapshot) =>
allProducts.add(SingleProduct.fromSnapshot(snapshot)));
print(allProducts.length); //returns 0 so I think my map isn't working
return allProducts;
});
}
The Firestore query returns 11 query snapshots as expected but I then try and add them to a list using my product schema but the the results show the list has 0 elements. Any suggestions how to map the results of my fire base query to a list?
Use forEach():
snap.docs.forEach((snapshot) => allProducts.add(SingleProduct.fromSnapshot(snapshot)));
Explanation time: [tl;dr Your lambda wasn't executed at all]
map() is a lazy function intended for transforming elements in a list, which works only if the result is going to be used.
In your code, the result of map() is nowhere used later (eg. assigned to a variable), which is why the lambda within it is not called (why would it transform if the transformation is not used ahead?)
Also, it's not apt for your use-case.
To demonstrate its laziness, try running this code in DartPad:
void main() {
List<String> list = ["a", "b", "c"];
List<String> anotherList = [];
var mappingOutput = list.map((element) { anotherList.add(element); return element + "X"; }).toList();
print(list);
print(mappingOutput);
print(anotherList);
}
Notice that the result of map() is to be given back to a variable mandatorily, which pushes the laziness aside and executes it.
anotherList will be filled.
I am going to be working with sqlflite package in Flutter, and I have been reading a lot about it. However, everywhere I find the example being shown for the single model, that is
SingleModel{
String data1, data2;
....
}
I have followed
Store list of objects in sqlite
Sqlflite operation in Flutter
these as well, but that did not quite resolve it. My main requirements is to work with the data like this:
user: {
'id': unique_key,
'name': 'abc',
'courses': [
{ 'coursename': 'some_coursename'},
{ 'coursename': 'some_other_coursename'}
]
}
I have designed my model correctly,
UserModel Class
class UserModel{
int id;
String name;
List<Course> _courses;
UserModel.set(Map data){
this.id = data['id'];
this.name = data['name'];
if(data['courses'] != null)
this.courses = [data['courses']];
}
// append more courses
void setMorCourses(Map course){
// checking for duplicate errors
if(!this.courses.contains(course['course']))
this.courses.add(course['course'])
}
}
Now, I am confused on how to use the Nested Model with sqlflite. I believe I need to work in the DatabaseHelper class.
Course Class
class CourseModel{
String coursename;
CourseModel.set(Map data){
this.coursename = data['coursename'];
}
}
I am trying to create an Attendance App, so I want to get the courses in which the students are registered. The Student's class looks something like this:
class Student {
final String name;
final String email;
final String number;
final String regNo;
List<CourseIDAndInstructorID> courses;
Student({this.name, this.email, this.number, this.regNo});
}
The List named courses contains the document-ID of the instructor whose course it is, and the document-ID of the course document.(As one student would obviously be taking classes from different instructors)
Now using these two fields, I want to get the documents of the courses, create an Object of the custom class Course and then add this object to a List, that would be returned so that it can be displayed on the GUI.
But I am getting this Exception, whereas I clearly have data in the object.
The image of the Exception Message can be seen here
final CollectionReference _instructorCollection =
Firestore.instance.collection('instructors');
final CollectionReference _studentCollection =
Firestore.instance.collection('students');
Stream<List<Course>> _getStudentCourses() async* {
List<Course> courseList;
DocumentSnapshot stdDoc = await _studentCollection.document(uid).get();
Student _student = new Student.fromSnapshot(stdDoc);
if (_student.courses.length == 0) {
return; //TODO add appropriate return type
} else {
int len = _student.courses.length;
while (len != 0) {
len--;
DocumentSnapshot snapshot = await _instructorCollection
.document(_student.courses[len].instructorUID)
.collection('courses')
.document(_student.courses[len].courseID)
.get();
print(snapshot.data);
Course course = new Course.fromSnapshot(snapshot);
courseList.add(course);
yield courseList;
}
}
}
Can someone tell me, what I'm doing wrong or how I can do this?
If something is missing from the context of the question, kindly tell me so that I can add that.
You may need to create the List object instead of just a pointer.
List<Course> courseList = new List();
source
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.
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