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"}
Related
[![enter image description here][1]][1]I have a class that has one embedded array as well as a couple of objects. I can't figure out how to create/update and read to and from Cloud Firestore. I'm using flutter.
class ArtistModal {
final String artistName;
final String artistImage;
final String role;
ArtistModal({
required this.artistName,
required this.artistImage,
required this.role,
});
I am trying to embedded artistModal in test modal. I want to read it as a list in UI side. I can't quite figure it out how?
class TestModal {
final String id;
final String venueName;
final String eventName;
List<ArtistModal> artistModal = <ArtistModal>[];
TestModal({
required this.venueName,
required this.id,
required this.eventName,
required this.artistModal,
});
factory TestModal.fromJson(Map<String, dynamic> json) {
return TestModal(
venueName: json['venueName'] ?? '',
eventName: json['eventName'] ?? '',
artistModal: List.from(json['artistModal'] ?? ''),
id: json['id'] ?? '');
}
Map<String, dynamic> toMap() {
return {
'venueName': venueName,
'eventName': eventName,
'artistModal': artistModal,
'id': id,
};
}
}
Edited
Alright. And after this can I use this to create/update and read data from Cloud Firestore.
Stream<List<TestModal>> fetchListOfTest() {
return _testCollection.snapshots().map((event) => event.docs
.map((e) => TestModal.fromJson(e.data() as Map<String, dynamic>))
.toList());
}
Future<void> setTest(TestModal testModal) {
var options = SetOptions(merge: true);
return _testCollection.doc(testModal.id).set(testModal.toMap(), options);
}
Since I do not have enough reputation to write a comment, I couldn't ask you what you want to achieve here exactly. I'm assuming that you want to generate a list of ArtistModal and store it in artistModal attribute of class TestModal. If that's the case, you can change your factory method to something like this:
factory TestModal.fromJson(Map<String, dynamic> json) {
return TestModal(
venueName: json['venueName'] ?? '',
eventName: json['eventName'] ?? '',
artistModal: json['artistModal'] != null ?
List.generate(json['artistModal'].length, (index) => ArtistModal(
artistName: json['artistModal'][index]['name'],
artistImage: json['artistModal'][index]['image'],
role: json['artistModal'][index]['role'],
)) : [],
id: json['id'] ?? '');
}
I am assuming that json['artistModal'] contains a list of Map, where the map contains data related to artist modal.
I couldn't transform my fetched the data,
But no errors showing, I have added a print statement to find error where it occurs
Future<void> fetchAndSetProduct() async {
final url =
Uri.https('shopda-83b00-default-rtdb.firebaseio.com', '/products');
try {
print("karan oneonofne ");
final response = await http.get(url);
final extractData = json.decode(response.body) as Map<String, dynamic>;
final List<Product> loadedProduct = [];
extractData.forEach((prodId, prodData) {
loadedProduct.add(Product(
id: prodId,
title: prodData['title'],
description: prodData['description'],
price: prodData['price'],
isFavourite: prodData['isFavourite'],
imageUrl: prodData['imageUrl']));
});
print(loadedProduct[1]);
_items = loadedProduct;
notifyListeners();
} catch (error) {
throw (error);
} finally {
print('object');
}
}
then I changed like this
Still, I couldn't get data.I think I couldn't change HTML to Jason
You're trying to fetch data from your Realtime Database by using the direct link.
This requires you to sign in and that is the HTML page it returns.
You should use the firebase_database package to fetch the data from the database.
You should update your fetchAndSetProduct method to this:
Future<void> fetchAndSetProduct() async {
try {
...
final DataSnapshot dataSnapshot = await FirebaseDatabase.instance.reference().child('products').once();
final extractData = json.decode(dataSnapshot.value) as Map<String, dynamic>;
...
} catch (error) {
...
} finally {
...
}
}
I found it Please change HTML file Json by simply
final url =
Uri.https('shopda-83b00-default-rtdb.firebaseio.com', '/products.json');
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;
}
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>;
So im trying to stream data from firestore but when printing the data I get:
I/flutter ( 8356): Closure: () => Map<String, dynamic> from Function 'data':.
I am using this code to fetch the data:
void messagesStream() async {
Stream collectionStream = _firestore.collection('messages').snapshots();
await for (var snapshot in collectionStream) {
for (var message in snapshot.docs) {
print(message.data());
}
}
When new data is added to the messages collection I get the Closure message so it is interacting with the databse.
What I want is it to print out the contents of the new document within the collection.
Any help is appreciated.
That's not the way you're supposed to iterate the results of a Stream. If you have a Stream and you want to process its results, you're supposed to use listen() to receive the results asynchronously.
Stream collectionStream = _firestore.collection('messages').snapshots();
collectionStream.listen((QuerySnapshot querySnapshot) {
querySnapshot.documents.forEach((document) => print(document.data()));
}
See also: Firestore collection query as stream in flutter
You might also want to review the documentation to learn how to query Firestore in Flutter.
void getMessages() async {
final messages= await _firestore.collection('messages').get();
for(var message in messages.docs){
print(message.data());
}
this is working check this and call getMessages() wherever you wana call
I encountered the same issue with pretty much your exact same code (sans your Stream variable). My suggestion is to delete the Stream var altogether (I tested the code below and got it to print the data from the Firestore database) :
void messagesStream() async {
await for (var snapshot in _firestore.collection('messages').snapshots()) {
for (var message in snapshot.docs) {
print(message.data());
}
}
}
Alternatively, try addding QuerySnapShot as the data type for your Stream variable (untested):
Stream<QuerySnapshot> collectionStream = _firestore.collection('messages').snapshots();
You could also replace the entire method by creating a new Stateless Widget (MessagesStream) that returns a StreamBuilder:
class MessagesStream extends StatelessWidget {
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _firestore.collection('messages').snapshots(),
builder: (context, snapshot) {
final messages = snapshot.data.docs;
for (var message in messages) {
print(message.data());
}
...and call it wherever you see fit while you test:
class _ChatScreenState extends State<ChatScreen> { (...)
body: Column(children: <Widget> [
//Just an example.
MessageStream(),
],
),
(...)
}
***Be sure you make the _fireStore (which should be a FirebaseFirestore.instance) a global variable if you're going with the Stateless Widget route.
I received this error while trying to throw a custom error class InkError:
You meed add toMap the class.
final response = await http.post(url, body: body, headers: headers);
final json = jsonDecode(response.body);
if (response.statusCode == HttpStatus.ok) {
return json;
} else {
throw InkError(
code: 0,
message: json['message'],
statusCode: response.statusCode,
).toMap();
InkError
class InkError {
/// Error code.
final int code;
/// Error message.
final String message;
/// HTTP Status Code
final int? statusCode;
const InkError({
required this.code,
required this.message,
this.statusCode,
});
factory InkError.fromJSON(Map<String, dynamic> json) => InkError(
code: json['code'] as int,
message: json['message'] as String,
statusCode: json['statusCode'],
);
Map<String, dynamic> toMap() {
return {
'code': code,
'message': message,
'statusCode': statusCode,
};
}
#override
String toString() {
return toMap().toString();
}
}