Flutter data serialization and deserialization with firestore db - flutter

I ran into this weird error in Flutter. I'm using a custom data model:
class ScanData {
final String userID;
final String companyID;
final String scanID;
final String deviceID;
final String model;
final Map result;
final DateTime dateTime;
final GeoPoint geoPoint;
ScanData(
{this.userID,
this.companyID,
this.scanID,
this.deviceID,
this.model,
this.result,
this.geoPoint,
this.dateTime});
factory ScanData.fromMap(Map data) {
return ScanData(
userID: data['userID'] ?? '',
companyID: data['companyID'] ?? '',
scanID: data['scanID'] ?? '',
deviceID: data['deviceID'] ?? '',
model: data['model'] ?? '',
result: data['result'] ?? {},
dateTime: data['dateTime'] as DateTime ?? DateTime.now(),
geoPoint: data['geoPoint'] as GeoPoint ?? ['77', '28'],
);
}
}
Created a method in Collection class to retrieve data from firestore:
class Collection<T> {
final FirebaseFirestore _db = FirebaseFirestore.instance;
final String path;
CollectionReference ref;
Collection({this.path}) {
ref = _db.collection(path);
}
Future<List<T>> getData() async {
var snapshots = await ref.get();
return snapshots.docs
.map((doc) => Global.models[T](doc.data) as T)
.toList();
}
}
Calling it in widget, this works if I don't use the data model and instead call the collection directly, but it's much easier since I don't want to create it everywhere I go and helps with type checking and intellisense...
FutureBuilder(
future: Collection<ScanData>(path: 'scans').getData(),
builder: (context, AsyncSnapshot<List<ScanData>> snapshot) {
if (!snapshot.hasData || snapshot.hasError) {
return Container(
alignment: Alignment.center,
child: CircularProgressIndicator());
} else {
return _buildScansList(snapshot.data);
}
},
)
Globals:
class Global {
static final Map models = {
ScanData: (data) => ScanData.fromMap(data),
};
}
The error (occurs in Globals fromMap):
Exception has occurred.
_TypeError (type '() => Map<String, dynamic>' is not a subtype of type 'Map<dynamic, dynamic>')
I tried putting the following in the ScanData model
factory ScanData.fromMap(Map<String,dynamic> data) {...}
but this gives another error:
Exception has occurred.
_TypeError (type '() => Map<String, dynamic>' is not a subtype of type 'Map<String, dynamic>')
:| Any tips appreciated.

In the method getData() of Collection class, doc.data is not property, it's a method, so just add ().

Related

How to assign List<dynamic> to List<Khana>, where Khana is a model class in a parameter in Flutter-Firestore?

Actually, I am trying to get data from firebase and I am suffering from the Error:
Expected a value of type List < Khana >, but got one of type 'List< dynamic >'
I am getting data from the firebase, my fetchData function is:
Future<void> fetchAndSetOrder() async {
try {
await collectionRef.get().then((querySnapshot) {
for (var result in querySnapshot.docs) {
debugPrint("${result.runtimeType}=> ${result.data()}");
Orders newOrder = Orders.fromFirestore(result);
debugPrint("\n new order : $newOrder");
// _Order.add(newOrder);
debugPrint("new order added");
// _Order.add(Orders.fromMap(result as Map));
}
});
} catch (e) {
debugPrint("Error during Fetch:- $e");
}
}
and the Orders.fromFirestore constructor is:
factory Orders.fromFirestore(DocumentSnapshot<Object?> snapshot) {
final data = snapshot.data() as LinkedHashMap<String, dynamic>;
debugPrint("Inside From Firestore Function");
return Orders(
khana: data['khana'], // here is the error...
orderNumber: data['orderNumber'],
userId: data['userId'],
paymentCash: data['paymentCash'],
dateTime: data['dateTime'],
);
}
Orders class has:
class Orders{
List<Khana> khana; // this is another ModelClass
String userId;
int orderNumber;
DateTime dateTime;
bool paymentCash;
Orders({
required this.khana,
required this.userId,
required this.orderNumber,
required this.dateTime,
required this.paymentCash,
});
}
so, the issue is how can I read List from the firestore as a List ? Any other possible way to solve this issue.
My Khana Model is:
import 'dart:convert';
class Khana {
String mealName;
int id;
int price;
int quantity;
Khana({
required this.mealName,
required this.price,
required this.quantity,
required this.id,
});
Map<String, dynamic> toMap() {
final result = <String, dynamic>{};
result.addAll({'mealName': mealName});
result.addAll({'id': id});
result.addAll({'price': price});
result.addAll({'quantity': quantity});
return result;
}
factory Khana.fromMap(Map<String, dynamic> map) {
return Khana(
mealName: map['mealName'] ?? '',
id: map['id']?.toInt() ?? 0,
price: map['price']?.toInt() ?? 0,
quantity: map['quantity']?.toInt() ?? 0,
);
}
String toJson() => json.encode(toMap());
factory Khana.fromJson(String source) => Khana.fromMap(json.decode(source));
}
I am trying to read a List from the firestore snapshot.data(), it says it's return data type is List, and I want this list to be assigned to the List of my own ModelClass (i.e Khana), and I am not able to do that.
I even tried
factory Orders.fromFirestore(DocumentSnapshot<Object?> snapshot) {
final data = snapshot.data() as LinkedHashMap<String, dynamic>;
debugPrint("Inside From Firestore Function");
return Orders(
khana: data['khana'] as List<Khana>,
orderNumber: data['orderNumber'],
userId: data['userId'],
paymentCash: data['paymentCash'],
dateTime: data['dateTime'],
);
}
but got the same issue :(
Change your khana to this
khana: List<Khana>.from(data['khana'].map((x)=>Khana.fromJson(x)));

Flutter Firebase Iterate through all documents that matches field value and inserting field from each one into a List of String

I'm trying to get afield value into a list of string from documents in the firebase that match the WHERE of a field value from a value of my own.
(I wanna show images above each other to create a full picture depending on the data, using two fields to be exact and then creating a list of images to show as positioned inside a Stack)
my code:
Implant Class:
class Implant {
final String id;
final String pid;
final String aptid;
late String type;
late double? sizew;
late int? sizel;
late String? positionQ;
late int? positionN;
Implant(this.id, this.pid, this.aptid, this.type, this.sizew, this.sizel,
this.positionQ, this.positionN);
Map<String, dynamic> toJson() => {
'ID': id,
'PID': pid,
'APTID': aptid,
'TYPE': type,
'SIZEW': sizew,
'SIZEL': sizel,
'POSITIONQ': positionQ,
'POSITIONN': positionN,
};
factory Implant.fromJson(Map<String, dynamic> json) {
return Implant(
json['ID'],
json['PID'],
json['APTID'],
json['TYPE'],
json['SIZEW'],
json['SIZEL'],
json['POSITIONQ'],
json['POSITIONN'],
);
}
static Map<String, dynamic> toMap(Implant implant) => {
'ID': implant.id,
'PID': implant.pid,
'APTID': implant.aptid,
'TYPE': implant.type,
'SIZEW': implant.sizew,
'SIZEL': implant.sizel,
'POSITIONQ': implant.positionQ,
'POSITIONN': implant.positionN,
};
static String encode(List<Implant> implant) => json.encode(
implant
.map<Map<String, dynamic>>((implant) => Implant.toMap(implant))
.toList(),
);
static List<Implant> decode(String implants) =>
(json.decode(implants) as List<dynamic>)
.map<Implant>((implant) => Implant.fromJson(implant))
.toList();
}
Future of list of string function:
static Future<List<String>> getImplants(Patient patient) async {
List<String> result = ['assets/images/teeth/empty.png'];
var collection = FirebaseFirestore.instance.collection('implants');
var docSnapshot = await collection.doc(patient.id).get();
if (docSnapshot.exists) {
Map<String, dynamic> data = docSnapshot.data()!;
result.add(data['POSITIONQ'] + data['POSITIONN']);
}
return result;
}
How I translate them into Stack and Positioned:
static Future<void> showPatientImplants(BuildContext context, Patient patient,
{bool spaces = true}) async {
List<String> myImplants;
myImplants = await getImplants(patient);
List<Widget> x = [Image.asset('assets/images/teeth/empty.png')];
myImplants.forEach((element) {
x.add(Image.asset(element));
});
return await showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
scrollable: true,
content: SingleChildScrollView(
child: GestureDetector(
onTap: () {
log(myImplants.toString());
},
child: Stack(
children: x,
),
),
),
);
});
}
The only problem I'm facing -I think- is that the Future<List> function doesn't get me the values I need.
I tried using other functions I found on SO and google, nothing worked.
I guess I can try using the stream and Listview.builder just to get the files names but it seems like exactly how it shouldn't be done.
any help is appreciated.
Never mind Solved it using the following code:
static Future<List<String>> getImplants(Patient patient) async {
List<String> result = ['assets/images/teeth/empty.png'];
log('ID:${patient.id}');
var collection = FirebaseFirestore.instance
.collection('implants')
.where('PID', isEqualTo: patient.id);
var docSnapshot = await collection.get();
for (var element in docSnapshot.docs) {
if (element.exists) {
Map<String, dynamic> data = element.data();
result.add(data['POSITIONQ'].toString() + data['POSITIONN'].toString());
}
}
return result;
}
but if anyone has a better idea I appreciate it along with everyone who has a similar problem.

Flutter realtime database 9.0.6 List<Object?>' is not a subtype of type 'Map<dynamic, dynamic>'

After update Firebase Realtime Database to 9.0.X I cant converet snapshot.value to Map<dynamic, dynamic> or Map<String, dynamic>
final FirebaseDatabase database = FirebaseDatabase.instance;
database.setPersistenceEnabled(true);
itemRef = database.reference().child('_child1').child(_remoteConfigService.getStringLang);
itemRef.once().then((event) {
final _data1 = Map<String, dynamic>.from(event.snapshot.value as dynamic).forEach((key, value) =>
_notes.add(Item.fromRTDB(value)));
});
My Model Class:
class Item {
String key;
String code;
String spn;
Item({
required this.key,
required this.code,
required this.spn});
factory Item.fromRTDB(Map<String, dynamic> data) {
return Item(
key: data['key'] ?? '01',
code: data['code'] ?? 'A drink',
spn: data['spn'] ?? 'Beer');
}
toJson() {
return {
"key": key,
"code": code,
"spn": spn,
};
}
}
In all options I get error:
List<Object?>' is not a subtype of type 'Map<dynamic, dynamic>'
Seems like the keys in the child are all numbers so it's already a List
Try this:
final _data1 = List.from(event.snapshot.value as dynamic).forEach((key, value) =>
_notes.add(Item.fromRTDB(value)));
Future<Null> getStudies() async {
_data.clear();
DatabaseReference ref = FirebaseDatabase.instance.ref("etudes");
Query query = ref.orderByChild("type").equalTo(1);
// Get the Stream
Stream<DatabaseEvent> stream = query.onValue;
// Subscribe to the stream!
stream.listen((DatabaseEvent event) {
print('Event Type: ${event.type}'); // DatabaseEventType.value;
print('Snapshot: ${event.snapshot}'); // DataSnapshot
if(event.snapshot.exists) {
Map<String, dynamic> data = jsonDecode(jsonEncode(event.snapshot.value)) as Map<String, dynamic>;
data.forEach((key, value) async {
_data.add(Etude.fromJson(value));
});
notifyListeners();
} else {
//print("snapshot does not exists");
_loading = false;
notifyListeners();
}
});
notifyListeners();
return null;
}

Why am I getting a _TypeError when trying to create a list?

I have the following JSON that is getting returned from an API call:
{
"categories": {
"mortgage": "Mortgage",
"haircutsClothing": "Haircuts & Clothing",
"homeRepairMaintenance": "Home Repair & Maintenance"
},
"other": {...}
}
And then I have this class acting as a model for the JSON data:
class APIData {
final Map<String, dynamic> parsedJson;
APIData({required this.parsedJson});
factory APIData.fromJson(Map<String, dynamic> parsedJson) {
List<Category> categories = parsedJson['categories']
.map((i) => Category.fromJson(i))
.toList();
return APIData(parsedJson: parsedJson);
}
}
class Category {
final String key;
final String category;
Category({required this.key, required this.category});
factory Category.fromJson(Map<String, dynamic> parsedJson) {
return Category(key: parsedJson['key'], category: parsedJson['value']);
}
}
When I run that, I get this error:
_TypeError (type '(dynamic) => Category' is not a subtype of type '(String, dynamic) => MapEntry<dynamic, dynamic>' of 'transform')
What am I doing wrong that is causing this error?
method .map on Map object has to return Map object too.
try this
final categoriesMap = parsedJson['categories'] as Map;
final List<Category> categories =
categoriesMap.entries
.map((e) => Category(key: e.key, category: e.value))
.toList();
entries from a Map returns an Iterable of Map Entry. then you can iterate through it and use key and value properties.

Flutter error: type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List<dynamic>' in type cast

I am trying to fetch News data from an API in JSON format and display it in my app.
News Class:
class News {
final String title;
final String desc;
final String imgURL;
final String url;
News(
{required this.title,
required this.desc,
required this.imgURL,
required this.url});
factory News.fromJSON(Map<String, dynamic> json) {
return News(
title: json["title"],
desc: json["description"],
imgURL: json["image_url"],
url: json["url"]);
}
}
News object getter:
Future<List<News>>? futureData;
Future<List<News>> getNews() async {
final response = await http.get(Uri.parse(
'https://api.stockdata.org/v1/news/all?&filter_entities=true&language=en&api_token=api_token&countries=in'));
if (response.statusCode == 200) {
List jsonResponse = json.decode(response.body);
return jsonResponse.map((data) => News.fromJSON(data)).toList();
} else {
throw Exception('Unexpected error occurred!');
}
}
FutureBuilder to display in the app:
FutureBuilder<List<News>>(
future: futureData,
builder: (context, snapshot) {
if (snapshot.hasData) {
print(snapshot);
List<News> data = snapshot.requireData;
return ListView.builder(
itemCount: data.length,
itemBuilder: (BuildContext context, int index) {
return Container(),
);
});
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
// By default show a loading spinner.
return Center(child: CircularProgressIndicator());
}),
I keep getting the error:
type '_InternalLinkedHashMap<String, dynamic>' is not a subtype of type 'List' in type cast
The response you receive from the api is of JsonObject similar to map but in the code you are trying to parse it as a list check how it looks here
your News.fromJSON() is written such that its taking only one object where as the data you are getting is of array.
factory News.fromJSON(Map<String, dynamic> json) {
return News(
title: json["title"],
desc: json["description"],
imgURL: json["image_url"],
url: json["url"]);
}
Currently your model class can only take one object not a list.
you can go through this