Get Map<String, dynamic> from Map<dynamic, dynamic> flutter - firebase

I have the code below :
Map<dynamic, dynamic> result = snapshot.value;
Map<String, dynamic> data = Map<String, dynamic>();
for (dynamic type in result.keys) {
data[type.toString()] = result[type];
}
print(data);
print(data.runtimeType);
but data type is _InternalLinkedHashMap<String, dynamic> and i am not able to read its value, despite the ugly hack i have done above.
The direct cast also doesn't work : snapshot.value as Map<String, dynamic> throws the error : '_InternalLinkedHashMap<Object?, Object?>' is not a subtype of type 'Map<String, dynamic>'
I need to have a Map<String, dynamic> type to be able to create my custom class Object.
snapshot.value has a type of dynamic, but it's a json object that returns the result of a Realtime Database query and there is no documentation about how to retrieve the value into a Flutter object.
I have tried this answer but i can't use it as jsonDecode() takes a String as a parameter.

When I tried Tom's answer I got:
The argument type 'Object?' can't be assigned to the parameter type 'Map<dynamic, dynamic>'
To solve this, I had to do:
Map<String, dynamic>.from(snapshot.value as Map);
In fact, since all values in my database node are booleans, I was able to do this:
Map<String, bool>.from(snapshot.value as Map);

This recent Firecast video solved my problem at 27min.
I also had an non explicit Flutter error about another nested HashMap in my data model object that could not be converted directly.
The solution is to use Map<String, dynamic>.from(snapshot.value) and this for every nested Map you have in your data model object
EDIT : See Frank's answer below that is the correct one for the latest version of the firebase_database package.

I tested your code using DartPad:
void main() {
//Custom json
Map<dynamic, dynamic> json = {
"window": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
}
};
Map<dynamic, dynamic> result = json;
Map<String, dynamic> data = Map<String, dynamic>();
for (dynamic type in result.keys) {
data[type.toString()] = result[type];
}
print(data);
print(data['window']);
print(data['window']['title']);
}
Print 1:
{window: {title: Sample Konfabulator Widget, name: main_window, width: 500, height: 500}}
Print 2:
{title: Sample Konfabulator Widget, name: main_window, width: 500, height: 500}
Print 3:
Sample Konfabulator Widget
I don't understand the problem

This will recursively convert a Map<dynamic, dynamic> to Map<String, dynamic>:
Map<String, dynamic> dynamicMapToString(Map<dynamic, dynamic> data) {
List<dynamic> _convertList(List<dynamic> src) {
List<dynamic> dst = [];
for (int i = 0; i < src.length; ++i) {
if (src[i] is Map<dynamic, dynamic>) {
dst.add(dynamicMapToString(src[i]));
} else if (src[i] is List<dynamic>) {
dst.add(_convertList(src[i]));
} else {
dst.add(src[i]);
}
}
return dst;
}
Map<String, dynamic> retval = {};
for (dynamic key in data.keys) {
if (data[key] is Map<dynamic, dynamic>) {
retval[key.toString()] = dynamicMapToString(data[key]);
} else if (data[key] is List<dynamic>) {
retval[key.toString()] = _convertList(data[key]);
} else {
retval[key.toString()] = data[key];
}
}
return retval;
}

Related

how to get value from map in firebase database flutter

i have a json structure like this
{
"-My6relBpWvPaY_I4JvN": {
"idUser": "4dca8440-a37d-11ec-9c66-9b8f61be17f0",
"message": "777777"
}
}
and I wanna use fromJson to save to map,
here is my model:
class Message {
final String userId;
final String message;
const Message({
#required this.userId,
#required this.message,
});
static Message fromJson(Map<String, dynamic> json) => Message(
userId: json['idUser'],
message: json['message'],
);
Map<String, dynamic> toJson() => {
'idUser': userId,
'message': message,
};
}
right now I always get null from the help from Frank,
final data = Map<String, dynamic>.from(snapshot.value as Map);
Message message = Message.fromJson(data);
print('message value${message.message}'); // here i got null
please tell me where is wrong.
---------- Update ----------
Hi Frank, firstly thanks for your reply, I will get a stream from firebase RTDB, since the data structure is as displayed above, with the data I got from following method,
final Map<String, dynamic> data = Map<String, dynamic>.from(snapshot.value as Map);
the key from this data will be {"-My6relBpWvPaY_I4JvN"}, and value is
{
"idUser": "4dca8440-a37d-11ec-9c66-9b8f61be17f0",
"message": "777777"
}
so in this case I need to update the method you showed
//**instead** snapshot.children.forEach((msgSnapshot) {
data.values.forEach((msgSnapshot) {
final Map<String, dynamic> messageData = Map<String, dynamic>.from(msgSnapshot as Map);
//i am not sure whether I need to use msgSnapshot.value as Map, since msgSnaphot is already the right Map<String, dynamic> that I can put to fromJson
Message message = Message.fromJson(messageData);
})
Do I understand correctly? thanks for further info!!
I have it setup like this in my case witch works just fine:
static Message model({required Map map}) => Message(
messageID: map[kModelMessengerID],
owner: map[kModelMessageOwner],
type: map[kModelMessageType],
msg: map[kModelMessageMsg],
membersIDs: (map[kModelMessageMembersIDs] as List<dynamic>)
.map((e) => e.toString())
.toList(),
createdAt: map[kModelMessageCreatedAt] == null
? DateTime.now()
: (map[kModelMessageCreatedAt] as Timestamp).toDate(),
);
and with how I pass data in to it:
final ref = FirebaseDatabase.instance.ref();
final snapshot = await ref.child(...).get();
if (snapshot.exists) {
var m = Message.model(map: snapshot.value as Map))
} else {
print('No data available.');
}
The code in your question doesn't show how snapshot is initialized, but my guess is that it contains a list of messages. Even a list of 1 messages is still a list, so you have to handle that in your code by iterating of the children property of the snapshot:
snapshot.children.forEach((msgSnapshot) {
final data = Map<String, dynamic>.from(msgSnapshot.value as Map);
Message message = Message.fromJson(data);
print('message value${message.message}');
})
Your JSON should look like this:
{"userId": "4dca8440-a37d-11ec-9c66-9b8f61be17f0", "message": "777777"}

Error: Expected a value of type 'int', but got one of type 'String'; flutter

I am trying to fetch quizzes data from realtime database using http mathod "GET".
The data is being retreived but it's not showing up in listview and when i print the length of my list it's 0. And this is what the error is being shown in my terminal: Error: Expected a value of type 'int', but got one of type 'String'
I can't figure out what the problem is. Please help me solve this problem because i am trying for around 5 days but can't solve it.
Thank you.
These are my codes for fetching the data in lists.
Future<void> getList() async {
list.clear();
final url = Uri.parse(
'https://testisnotesttheisthelearningapp-default-rtdb.firebaseio.com/Schools/${widget.uid}/ResultsFolder/${widget.id}.json');
final response = await get(url);
print(response.body);
var map = json.decode(response.body) as Map<String, dynamic>;
// print(map);
if (map != null) {
map.forEach((key, value) {
print(value['Name']);
var temp = Result(
value['Percent'],
(value['choice'] as List<dynamic>)
.map((e) => Model(e['index'], e['question'], []))
.toList(),
value['Score'],
(value['question'] as List<dynamic>).map((e) => e).toList(),
value['Name']);
list.add(temp);
});
}
}
This is my data from real-time database in JSON format:
{
"Name" : "Miets Digital",
"Percent" : 100,
"Score" : 2,
"choice" : [ "Correct", "Correct" ],
"question" : [ {
"question" : "WHo is Elon Musk?"
}, {
"question" : "How did Elon musk got rich?"
} ]
}
Try this:-
Future<void> getList() async {
list.clear();
final url = Uri.parse(
'https://testisnotesttheisthelearningapp-default-rtdb.firebaseio.com/Schools/${widget.uid}/ResultsFolder/${widget.id}.json');
final response = await get(url);
print(response.body);
var map = json.decode(response.body) as Map<String, dynamic>;
// print(map);
if (map != null) {
print(map['Name']);
var temp = Result(
map['Percent'],
(map['choice'] as List<dynamic>)
.map((e) => Model(e['index'], e['question'], []))
.toList(),
map['Score'],
(map['question'] as List<dynamic>).map((e) => e).toList(),
map['Name']);
list.add(temp);
}
}
forEach() loop goes through each key value pair in a map. The variables key and value gives you the key and the value the loop is currently working on. If you want to access the values of a map you will have to use the syntax map['key_name'].
This also explains the error, as your Result() constructor is expecting an integer but is getting a string value.
It's not a good practice to parse JSON maps. You just call over the model class
Try to follow the below code
Future<QuizModelResponse> getList() async {
// here write your code
var map = json.decode(response.body);
return QuizModelResponse.fromJsonMap(map);
}
List Response class
class QuizModelResponse {
List<QuizItemModel> content;
QuizModelResponse.fromJsonMap(dynamic data) :
content = List<QuizItemModel>.from(
data.map((it) => QuizItemModel.fromJson(it)));
}
Model class
class QuizItemModel {
String name;
int percent;
int score;
List<String> choice;
List<Question> question;
QuizItemModel(
{this.name, this.percent, this.score, this.choice, this.question});
QuizItemModel.fromJson(Map<String, dynamic> json) {
name = json['Name'];
percent = json['Percent'];
score = json['Score'];
choice = json['choice'].cast<String>();
if (json['question'] != null) {
question = new List<Question>();
json['question'].forEach((v) {
question.add(new Question.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['Name'] = this.name;
data['Percent'] = this.percent;
data['Score'] = this.score;
data['choice'] = this.choice;
if (this.question != null) {
data['question'] = this.question.map((v) => v.toJson()).toList();
}
return data;
}
}
class Question {
String question;
Question({this.question});
Question.fromJson(Map<String, dynamic> json) {
question = json['question'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['question'] = this.question;
return data;
}
}

Flutter Assigning QuerySnapshot to Map<dynamic, dynamic>

I am trying to fetch some menu item details such as menu name, slogan text icon, and verify data from firebase firestore using flutter but in the services of database getMenus() function, I am facing an error.
I have the following class model
import 'dart:convert';
MenuModel medicalTestModelFromJson(String str) => MenuModel.fromJson(json.decode(str));
String medicalTestModelToJson(MenuModel data) => json.encode(data.toJson());
class MenuModel {
MenuModel({
required this.title,
required this.slogan,
required this.icon,
required this.verify,
});
String title;
String slogan;
String icon;
bool verify;
factory MenuModel.fromJson(Map<String, dynamic> json) => MenuModel(
title: json["title"],
slogan: json["slogan"],
icon: json["icon"],
verify: json["verify"],
);
Map<String, dynamic> toJson() => {
"title": title,
"slogan": slogan,
"icon": icon,
"verify": verify,
};
}
And the following is the menu collection services from firebase firestore
class MenuServices{
static Future<List<MenuModel>> getMenus() async {
QuerySnapshot menuSnapshot = await FirebaseFirestore.instance.collection('homeItems').where("verify", isEqualTo: true).get();
List<MenuModel> menus =[];
Map<dynamic, dynamic> values = menuSnapshot.docs; //(***My Error is Exactly in this line which menuSnapshot.docs is not recognizing***)
values.forEach((key, values) {
menus.add(MenuModel.fromJson(values));
});
return menus;
}
}
And the error is
A value of type 'List<QueryDocumentSnapshot<Map<String, dynamic>>>' can't be assigned to a variable of type 'Map<dynamic, dynamic>
Change it to this:
class MenuServices{
static Future<List<MenuModel>> getMenus() async {
QuerySnapshot menuSnapshot = await FirebaseFirestore.instance.collection('homeItems').where("verify", isEqualTo: true).get();
List<MenuModel> menus = menuSnapshot.docs.map(
(e)=> MenuModel.fromJson(e.data() as Map<String, dynamic>)).toList();
return menus;
}
}
I used this and it worked:
void getLeituras(QuerySnapshot snapshot){
List leituras = [];
Leitura leitura;
snapshot.docs.forEach((e) {
leitura = Leitura();
leitura = Leitura.fromJson(e.data as Map<String, dynamic>);
leituras.add(leitura);
});
}

Flutter & Firestore - How to create a class object from a nested map from Firestore

I am trying to map a firestore document snapshot to a custom object that I have made but am getting this error upon doing so:
E/flutter ( 6555): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: type '(dynamic) => TestMap' is not a subtype of type '(String, dynamic) => MapEntry<dynamic, dynamic>' of 'transform'
From my understanding it has something to do with the way that I am parsing the nested map 'testMap'. Can any Flutter/Firebase/Dart experts out there help me understand what I am doing wrong and how to fix this.
So my data within Firestore looks like this:
The two class objects to parse this data into a usable object like this:
class Categories {
final String category;
final String imageurl;
List testMap = [];
Categories(this.category, this.imageurl);
Map<String, dynamic> toMap() => {
"category": this.category,
"imageurl": this.imageurl,
"testMap": this.testMap
};
Categories.fromMap(Map<String, dynamic> map)
: category = map["category"],
imageurl = map["imageurl"],
testMap = map["testMap"].map((map) { // THE ERROR IS HERE (map)
return TestMap.fromMap(map);
}).toList();
}
class TestMap {
final String v1;
final String v2;
TestMap(this.v1, this.v2);
Map<String, dynamic> toMap() => {
"v1": this.v1,
"v2": this.v2,
};
TestMap.fromMap(Map<dynamic, dynamic> map)
: v1 = map["v1"],
v2 = map["v2"];
}
Finally I get the data from Firestore like this:
fromFirestore() {
final FirebaseAuth auth = FirebaseAuth.instance;
Categories categoryObject;
CollectionReference categoriesCollection =
FirebaseFirestore.instance.collection('categories');
categoriesCollection.doc('categories').get()
// ignore: missing_return
.then((docSnapshot) {
for (var i = 1; i <= docSnapshot.data().length; i++) {
print(docSnapshot.data()[i.toString()]['testMap'].toString());
categoryObject = Categories.fromMap(docSnapshot.data()[i.toString()]);
}
});
}
I know that firestore maps to Map<String, dynamic> but the error seems to want this subtype of type '(String, dynamic)' instead of Map<String, dynamic> ... perhaps something to do with how my classes are parsing the maps?
Thanks a lot!
You might need to cast to TestMap
testMap = map["testMap"].map((map) { // THE ERROR IS HERE (map)
return TestMap.fromMap(map);
}).toList().cast<TestMap>;

Flutter How to group lists from data in a SQLite database?

I am newbie, and I am using package grouped_list to group the list with data from the SQLite database, grouped_list works fine with an existing list just like in its example, but when I replace it equals list with data retrieved from SQLite database then it doesn't work and I get error:
The following NoSuchMethodError was thrown building GroupedListView<dynamic, String>(dirty, state: _GroupedListViewState<dynamic, String>#c2ba2):
Class 'DrinkDatabase' has no instance method '[]'.
Receiver: Instance of 'DrinkDatabase'
Tried calling: []("drinkGroup")
Below is my code. If you need more information please let me know, please help, thank you!
GroupedListView<dynamic, String>(
shrinkWrap: true,
elements: homeController.drinkList,
groupBy: (element) => element['drinkGroup'],
groupComparator: (value1, value2) => value2.compareTo(value1),
itemComparator: (item1, item2) =>
item1['drinkDateTime'].compareTo(item2['drinkDateTime']),
order: GroupedListOrder.DESC,
useStickyGroupSeparators: true,
groupSeparatorBuilder: (String value) =>
Container(),
itemBuilder: (c, element) {
return Container();
},
)
This is how I declare the list:
List drinkList = List<DrinkDatabase>();
And class DrinkDatabase:
class DrinkDatabase {
int drinkId;
int drinkVolume;
String drinkGroup;
String drinkCategory;
String drinkDateTime;
DrinkDatabase({
this.drinkId,
this.drinkVolume,
this.drinkGroup,
this.drinkCategory,
this.drinkDateTime,
});
Map<String, dynamic> toMap() {
return {
"drinkId": drinkId,
"drinkVolume": drinkVolume,
"drinkGroup": drinkGroup,
"drinkCategory": drinkCategory,
"drinkDateTime": drinkDateTime,
};
}
#override
String toString() {
return 'drinkTable('
'"drinkId": ${this.drinkId}, '
'"drinkVolume": ${this.drinkVolume}, '
'"drinkGroup": ${this.drinkGroup}, '
'"drinkCategory": ${this.drinkCategory}, '
'"drinkDateTime": ${this.drinkDateTime}, ';
}
}
What I did and it worked for me
The way to declare the list
List drinkList = [];
Then replace the line
elements: homeController.drinkList,
with
elements: drinkList,
To build the list from the database query result list
Future<void> getSampleList() async {
drinkList.clear();
List<Map<String, dynamic>> result =
await DrinkDatabaseHelper.instance.queryAll();
print('result= $result');
var map = new Map<String, dynamic>();
for (int i = 0; i < result.length; i++) {
map['drinkId'] = result[i]['drinkId'].toString();
map['drinkVolume'] = result[i]['drinkVolume'].toString();
map['drinkGroup'] = result[i]['drinkGroup'];
map['drinkCategory'] = result[i]['drinkCategory'];
map['drinkDateTime'] = result[i]['drinkDateTime'];
drinkList.add(Map<dynamic, String>.from(map));
}
In DrinkDatabaseHelper
Future<List<Map<String, dynamic>>> queryAll() async {
Database db = await instance.database;
return await db.query(_tableName);
}
If you have more questions let me know

Resources