I'm working on my project in flutter and I'm stuck in kind of a dumb problem. I'd like to retrieve a List of maps from Firebase, but i can't convert the document in an object format.
So every checkout document is identified by the user email. As a consequence every document will contain a list of checkout orders as displayed in the picture.
This is the Document format in firebase
class UserCheckoutOrderList extends Equatable {
final String checkoutOrderDate;
final Map<dynamic, dynamic> customerAddress;
final String customerName;
final String customerPhone;
final String deliveryFee;
final String subTotal;
final String total;
final List<Product> products;
const UserCheckoutOrderList(
{required this.checkoutOrderDate,
required this.customerAddress,
required this.customerName,
required this.customerPhone,
required this.deliveryFee,
required this.subTotal,
required this.total,
required this.products});
static UserCheckoutOrderList fromSnapshot(List<DocumentSnapshot> snapshots) {
final ordersList = snapshots.forEach((snapshot) {
return UserCheckoutOrderList(
checkoutOrderDate: snapshot['checkoutOrderDate'],
customerAddress: snapshot['customerAddress'],
customerName: snapshot['customerName'],
customerPhone: snapshot['customerPhone'],
deliveryFee: snapshot['deliveryFee'],
subTotal: snapshot['subTotal'],
total: snapshot['total'],
products: snapshot['products']);
});
return ordersList;
}
#override
List<Object?> get props => [];
}
This is what I've tried so far for the model.
I highly recommend to always have an id on any document. This is how to handle data:
import 'package:equatable/equatable.dart';
import 'package:flutter/foundation.dart';
#immutable
class UserCheckoutOrderListEntity extends Equatable{
final String id;
final String checkoutOrderDate;
final Map customerAddress;
final String customerName;
final String customerPhone;
final String deliveryFee;
final String subTotal;
final String total;
final List products;
const UserCheckoutOrderListEntity({
required this.id,
required this.checkoutOrderDate,
required this.customerAddress,
required this.customerName,
required this.customerPhone,
required this.deliveryFee,
required this.subTotal,
required this.total,
required this.products});
Map<String, dynamic> toMap() {
return {
'id': id,
'checkoutOrderDate': checkoutOrderDate,
'customerAddress': customerAddress,
'customerName': customerName,
'customerPhone': customerPhone,
'deliveryFee': deliveryFee,
'subTotal': subTotal,
'total': total,
'products': products,
};
}
factory UserCheckoutOrderListEntity.fromMap(Map<String, dynamic> map) {
return UserCheckoutOrderListEntity(
id: map['id'] as String,
checkoutOrderDate: map['checkoutOrderDate'] as String,
customerAddress: map['customerAddress'] as Map,
customerName: map['customerName'] as String,
customerPhone: map['customerPhone'] as String,
deliveryFee: map['deliveryFee'] as String,
subTotal: map['subTotal'] as String,
total: map['total'] as String,
products: map['products'] as List,
);
}
#override
List<Object?> get props => [id];
}
Then in your widget:
class CheckOut extends StatelessWidget {
const CheckOut({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return FutureBuilder<List<UserCheckoutOrderListEntity>>(
future: FirebaseFirestore.instance
.collection("orderCheckOut")
.get()
.then((query) => query.docs
.map((map) => UserCheckoutOrderListEntity.fromMap(map.data()))
.toList()),
builder: (context, snapshot) {
return Container();
});
}
}
Related
I am new to flutter and I am a bit confused about Riverpod and have wasted a few days on this issue which is probably really easy. I have a Model, Provider and Service created with Riverpod which I will share below. I have a widget that takes a Map and an API that is structured
{
"job": [
{"feild1": "data",..},
{"feild2": "data",..},
{"feild3": "data",..}
]
}
It is being mapped as List how can I change that to Map for a child widget I have created.
This is my Provider:
final jobsDataProvider = FutureProvider<List<JobsModel>>((ref) async {
return ref.watch(jobsProvider).getJobs();
});
This is my model:
class JobsModel {
final String jobid;
final String from_state;
final String from_suburb;
final String to_state;
final String to_suburb;
final String travel_time;
final String date;
final String distance;
final String status;
final String map;
JobsModel({
required this.jobid,
required this.from_state,
required this.from_suburb,
required this.to_state,
required this.to_suburb,
required this.travel_time,
required this.date,
required this.distance,
required this.status,
required this.map,
});
factory JobsModel.fromJson(Map<String, dynamic> json) {
return JobsModel(
jobid: json['jobid'],
from_state: json['from']['state'],
from_suburb: json['from']['suburb'],
to_state: json['to']['state'],
to_suburb: json['to']['suburb'],
travel_time: json['travel_time'],
date: json['date'],
distance: json['distance'],
status: json['status'],
map: json['map'],
);
}
}
This is my service:
class ApiServices {
String endpoint = 'https://localhost:3000/jobs';
Future<List<JobsModel>> getJobs() async {
Response response = await get(Uri.parse(endpoint));
if (response.statusCode == 200) {
final List result = jsonDecode(response.body)['jobs'];
return result.map(((e) => JobsModel.fromJson(e))).toList();
} else {
throw Exception(response.reasonPhrase);
}
}
}
final jobsProvider = Provider<ApiServices>((ref) => ApiServices());
My child widget takes a Map<String, dynamic> how can I make this work so I can map multiple widgets from the returned api call into a row.
Thanks heaps all.
I have an array of elements that come from api and I get and error from api =>
The operator '[]' isn't defined for the type of 'Country'
Response from api looks like this:
{"success":true,"list":[{"id":2,"createdAt":"2022-11-11T15:25:31.680Z","updatedAt":"2022-11-11T15:25:31.680Z","name":"Afghanistan"}]}
This is the type of an element inside list:
class Country {
final int id;
final String createdAt;
final String updatedAt;
final String name;
const Country({
required this.id,
required this.createdAt,
required this.updatedAt,
required this.name
});
}
This is my widget:
class MyWidget extends StatefulWidget {
const MyWidget({super.key});
#override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
List<Country> countries = [];
Future<void> getCountries() async {
try {
final response = await _apiService.getCountries();
countries = response['list']; // [{"id": 2, "createdAt:""...}]
} catch (e) {
log(e.toString());
rethrow;
}
}
#override
void initState() {
getCountries();
super.initState();
}
#override
Widget build(BuildContext context) {
return Container();
}
}
And if I try to call this, IDE lights me this error in country['name'] =>
final List countriesWithNames = countries.map((country) => country['name']).toList();
Or when I try to get an element from the list, like this => countries[index]['name']
response['list'] returns list of map.You need to convert into model class.
You can use this model class
class Country {
final int id;
final String createdAt;
final String updatedAt;
final String name;
const Country({
required this.id,
required this.createdAt,
required this.updatedAt,
required this.name,
});
Map<String, dynamic> toMap() {
final result = <String, dynamic>{};
result.addAll({'id': id});
result.addAll({'createdAt': createdAt});
result.addAll({'updatedAt': updatedAt});
result.addAll({'name': name});
return result;
}
factory Country.fromMap(Map<String, dynamic> map) {
return Country(
id: map['id']?.toInt() ?? 0,
createdAt: map['createdAt'] ?? '',
updatedAt: map['updatedAt'] ?? '',
name: map['name'] ?? '',
);
}
String toJson() => json.encode(toMap());
factory Country.fromJson(String source) =>
Country.fromMap(json.decode(source));
}
And getting from local json string
final data = response["list"] as List?;
List<Country> countries =
data?.map((e) => Country.fromMap(e)).toList() ?? [];
print(countries);
I'm developing an app that manages glossary food items. I can't figure out what is the mistake I've done. Here's my code...
First, I've created a class called 'Product' that contains all the required fields like Product name, Product Image, item Type and quantity etc
The model :
class Product {
String id;
final String productName;
final String itemType;
final String productImage;
final String purchasedDate;
final String expiryDate;
final String quantity;
Product({
this.id = '',
required this.productName,
required this.productImage,
required this.itemType,
required this.purchasedDate,
required this.expiryDate,
required this.quantity,
});
Map<String, dynamic> toJson() => {
'id': id,
'ProductImage': productImage,
'ProductName': productName,
'PurchasedDate': purchasedDate,
'ExpiryDate': expiryDate,
'ItemType': itemType,
'Quantity': quantity,
};
static Product fromJson(Map<String, dynamic> json) => Product(
productName: json['ProductName'],
productImage: json['productImage'],
itemType: json['ItemType'],
purchasedDate: json['purchasedDate'],
expiryDate: json['ExpiryDate'],
quantity: json['Quantity']);
}
The code for implementation :
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:expire_x/utils/product.dart';
import 'package:flutter/material.dart';
class HomePageTwo extends StatefulWidget {
const HomePageTwo({Key? key}) : super(key: key);
#override
State<HomePageTwo> createState() => _HomePageTwoState();
}
class _HomePageTwoState extends State<HomePageTwo> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("home page"),
),
body: StreamBuilder<List<Product>>(
stream: readProduct(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong> ${snapshot.error}");
} else if (snapshot.hasData) {
final product = snapshot.data!;
return ListView(
children: product.map(buildProduct).toList(),
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
),
);
}
Widget buildProduct(Product product) => ListTile(
title: Text(product.productName),
);
Stream<List<Product>> readProduct() => FirebaseFirestore.instance
.collection('Products')
.snapshots()
.map((snapshot) =>
snapshot.docs.map((doc) => Product.fromJson(doc.data())).toList());
Future CreateProduct() async {
final docProduct = FirebaseFirestore.instance.collection('Products').doc();
final product = Product(
id: docProduct.id,
productImage: 'ddd',
productName: "productName",
purchasedDate: "purchasedDate",
expiryDate: "expiryDate",
itemType: "itemType",
quantity: "quantity",
);
final json = product.toJson();
await docProduct.set(json);
}
}
The json response from firebase that I wanted :
json response
And the error :
type 'Null' is not a subtype of type 'String'
In the emulator
I'm new to flutter. Can anyone please help me?
This error occurs when you add required keyword in the constructor. And one of the value appears to be null.
To overcome this
Make sure that all the fields in all the documents is filled.
OR
Avoid using required use null operator instead ? if you are not sure the documents to be filled everytime.
Like:
class Product {
String? id;
String? productName;
String? itemType;
String? productImage;
String? purchasedDate;
String? expiryDate;
String? quantity;
Product({
this.id = '',
this.productName,
this.productImage,
this.itemType,
this.purchasedDate,
this.expiryDate,
this.quantity,
});
Typo mistake in your model class
static Product fromJson(Map<String, dynamic> json) => Product(
productName: json['ProductName'],
productImage: json['ProductImage'], // It should be 'ProductImage'
itemType: json['ItemType'],
purchasedDate: json['PurchasedDate'], // and this should be 'PurchasedDate'
expiryDate: json['ExpiryDate'],
quantity: json['Quantity']);
I am trying to retrieve data from Firebase Realtime Database into a list in Flutter using a model.I don't get snapshot.data how to get data.value. I have read several other posts about using Firebase with Flutter but have not found a clear answer.
Model class screen:
import 'package:firebase_database/firebase_database.dart';
class DataModel {
final String id;
final String name;
final String price;
final String qty;
DataModel(
{required this.id,
required this.name,
required this.price,
required this.qty});
DataModel.fromSnapshot(DataSnapshot snapshot)
: id = snapshot.key.toString(),
name = (snapshot.value as Map<String, dynamic>?)?['productName'] ?? '',
price =
(snapshot.value as Map<String, dynamic>?)?['productPrice'] ?? '',
qty = (snapshot.value as Map<String, dynamic>?)?['qty'] ?? '';
toJson() {
return {
"productName": name,
"productPrice": price,
"qty": qty,
};
}
}
Database service with Firebase query:
import 'package:firebase_database/firebase_database.dart';
import 'package:money_management/data_json_model.dart';
class DatabaseService {
static List<DataModel> getData() {
Query needsSnapshot =
FirebaseDatabase.instance.ref("Money Management").orderByKey();
// print(needsSnapshot); // to debug and see if data is returned
List<DataModel> needs = [];
Map<dynamic, dynamic> values = needsSnapshot.onValue as Map;
values.forEach((key, values) {
needs.add(DataModel.fromSnapshot(values));
});
return needs;
}
}
ListView Page:
import 'package:flutter/material.dart';
import 'package:money_management/data_json_model.dart';
import 'database_service.dart';
class ListScreen extends StatefulWidget {
const ListScreen({Key? key}) : super(key: key);
#override
State<ListScreen> createState() => _ListScreenState();
}
class _ListScreenState extends State<ListScreen> {
List<DataModel> _needs = [];
#override
void initState() {
super.initState();
_setupNeeds();
}
_setupNeeds() async {
List<DataModel> needs = DatabaseService.getData();
setState(() {
_needs = needs;
});
}
#override
Widget build(BuildContext context) {
return RefreshIndicator(
onRefresh: () => _setupNeeds(),
child: ListView.builder(
itemCount: _needs.length,
itemBuilder: (BuildContext context, int index) {
DataModel need = _needs[index];
return Column(
children: [
Text(need.id),
Text(need.name),
Text(need.price),
Text(need.qty),
],
);
}),
);
}
}
Try make the method getData() asynchronous and call get() of FirebaseDatabase instead:
class DatabaseService {
static Future<List<dynamic>> getData() async {
final snapshot = await FirebaseDatabase.instance
.ref("Money Management")
.orderByKey()
.get();
// print(snapshot); // to debug and see if data is returned
List<DataModel> needs = [];
Map<dynamic, dynamic> values = snapshot.value as Map;
values.forEach((key, values) {
needs.add(DataModel.fromSnapshot(values));
});
return needs;
}
}
you can receive data as Map<String,dynamic> in your DataModel like this:
class DataModel {
late String id;
late String name;
late String price;
late String qty;
DataModel({
required this.id,
required this.name,
required this.price,
required this.qty
});
DataModel.fromSnapshot(DataSnapshot snapshot){
Map<String, dynamic> myData= Map<String,dynamic>.from(snapshot.value as
Map);
id = snapshot.key.toString();
name = myData["productName"].toString() ?? '';
price =myData["productPrice"].toString() ?? '';
qty = myData["qty"].toString() ?? '';
}
Map<String,dynamic> toJson() {
return {
"productName": name,
"productPrice": price,
"qty": qty,
};
}
}
Hello I have try to add List in my model and add data to my homepage but I have this error only when I add " suite=widget.todo.suite; in the initstate :
suite=widget.todo.suite; => type 'List<dynamic>' is not a subtype of type 'List<String>'
If I use an other data model as "id" who is a string or "isDone" who is a bool I have no error. But my "suite" data have error
I don't understand.
-------------------homepage--------------
class Add_suite extends StatefulWidget {
final Todo todo;
const Add_suite({Key key, #required this.todo}) : super(key: key);
#override
_Add_suiteState createState() => _Add_suiteState();
}
class _Add_suiteState extends State<Add_suite> {
final _formKey = GlobalKey<FormState>();
String title;
String description;
List<String> suite =[""];
List<String> stringList = [];
#override
void initState() {
super.initState();
Firebase.initializeApp().whenComplete(() {
print("completed");
setState(() {});
});
suite=widget.todo.suite;
title = widget.todo.title;
description = widget.todo.description;
}
...
}
-------------------model--------------
class Todo {
DateTime date;
String title;
String id;
String description;
List suite;
bool isDone;
Todo({
#required this.date,
#required this.title,
this.description = '',
this.suite,
this.id,
this.isDone = false,
});
static Todo fromJson(Map<String, dynamic> json) => Todo(
date: Utils.toDateTime(json['createdTime']),
title: json['title'],
description: json['description'],
suite: json['suite'],
id: json['id'],
isDone: json['isDone'],
);
Map<String, dynamic> toJson() => {
'date': Utils.fromDateTimeToJson(date),
'title': title,
'description': description,
'suite': suite,
'id': id,
'isDone': isDone,
};
}
Edit: if I change list suite par list suite
my Streambuilder return error. If I use list I have no error on streambuilder but an other error with dynamic type
StreamBuilder<List<Todo>>(
stream: FirebaseFirestore.instance
.collection('first_stories')
.orderBy("date", descending: true)
.snapshots()
.transform(Utils.transformer(Todo.fromJson)),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Center(child: CircularProgressIndicator());
default:
if (snapshot.hasError) {
return buildText('Erreur');
} else {
final todos = snapshot.data;
final provider = Provider.of<TodosProvider>(context);
provider.setTodos(todos);
return
TodoListWidget();
}
}
},
),
In Todo class, you need to specify your List type :
List<String> suite;
By default, if you not specify type, it's dynamic type :
List suite; is egal to List<dynamic> suite;
If you can't specify a type in Todo class, you can cast your dynamic List when you set it in suite :
suite = widget.todo.suite as List<String>;
EDIT
You also have to convert your data in fromJson function. Try this :
suite: List<String>.from(json['suite']),