This may sound stupid but I am confused. How are you supposed to save data to Firestore?
Is it supposed to be converted to/from JSON before adding and retrieving? Or is it supposed to be saved as a map, like:
({'sugars': sugars, 'name': name, 'strength': strength})
Is it different for real-time DB?
I have seen people adding the following to their model classes:
final FieldModel field;
final int number;
final String id;
TransactionModel({
required this.field,
required this.number,
this.id = '',
});
/// this conversion to JSON
factory TransactionModel.fromJson(String id, Map<String, dynamic> json) =>
TransactionModel(
field: FieldModel.fromJson(json['field']['id'], json['field']),
id: id,
number: json['number'],
);
My question is: Why do they convert it to JSON? Is it always required? Is this for Firestore or Realtime Database?
You can find all the details about fetching and storing data in Cloud Firestore here.
Firestore stores data within "documents", which are contained within "collections". Documents can also contain nested collections. For example, our users would each have their own "document" stored inside the "Users" collection. The collection method allows us to reference a collection within our code.
To add an entry to Firestore you need to pass a Map<String, dynamic> to either the add() function of the collection or set() function of the document.
You can do something like this to send data.
await FirebaseFirestore.instance.collection('users').add({
'full_name': fullName, // John Doe
'company': company, // Stokes and Sons
'age': age // 42
});
Or, you can alternatively do:
await FirebaseFirestore.instance.collection('users').add(
userModel.toJson(),
);
where, toJson() is
Map<String, dynamic> toJson() => {
"full_name": fullName,
"company": company,
"age": age,
};
To read a collection or document once, call the Query.get or DocumentReference.get methods.
class GetUserName extends StatelessWidget {
final String documentId;
GetUserName(this.documentId);
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder<DocumentSnapshot>(
future: users.doc(documentId).get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {
return Text("Document does not exist");
}
if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
return Text("Full Name: ${data['full_name']} ${data['last_name']}");
}
return Text("loading");
},
);
}
}
As to answer your question:
My question is: Why do they convert it to json? Is it always required? Is this for Firestore or Realtime Database?
JSON (JavaScript Object Notation) is a lightweight format that is used
for data interchanging. It is based on a subset of JavaScript language
(the way objects are built-in JavaScript). As stated in the MDN, some
JavaScript is not JSON, and some JSON is not JavaScript.
An example of where this is used is web services responses. In the
'old' days, web services used XML as their primary data format for
transmitting back data, but since JSON appeared (The JSON format is
specified in RFC 4627 by Douglas Crockford), it has been the preferred
format because it is much more lightweight
You can refer to this answer for more information on JSON.
To sum up, JSON or Maps are used for data transfers as this provides the data in a well-structured way (key-value pair). And it's easy to read compared to other data transfer formats like CSV.
Related
Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed last month.
Improve this question
I have following code:
I use cloud firestore as database
UserModel:
class DbUser {
String id;
final String authUserID;
final String userName;
final List<String>? itemsForSale;
final List<String>? itemFavourites;
final List<String>? bids;
DbUser(
{this.id = '',
required this.authUserID,
required this.userName,
this.itemsForSale,
this.itemFavourites,
this.bids});
Map<String, dynamic> toJson() => {
'id': id,
'authUserID': authUserID,
'userName': userName,
'itemsForSale': itemsForSale,
'itemFavourites': itemFavourites,
'bids': bids,
};
static DbUser fromJson(Map<String, dynamic> json) => DbUser(
id: json['id'],
authUserID: json['authUserID'],
userName: json['userName'],
itemsForSale: json['itemsForSale'] is Iterable
? List.from(json['itemsForSale'])
: null,
itemFavourites: json['itemFavourites'] is Iterable
? List.from(json['itemFavourites'])
: null,
bids: json['bids'] is Iterable ? List.from(json['bids']) : null,
);
}
Repository class
final _firestoreDB = FirebaseFirestore.instance;
Future<DbUser?> getDBUserByDBUserId({required String dbUserID}) async {
final docUser = _firestoreDB.collection('users').doc(dbUserID);
final snapshot = await docUser.get();
if (snapshot.exists) {
return DbUser.fromJson(snapshot.data()!);
}
return null;
}
snapshot.exists returns false.
I do not understand why?
my snapshot returns null but I do not see why it does that, could somebody please help me?
Thank you
Space
found my problem, there was a space before my Id that had been retreived, I must have accedentally put it there when creating the database...
Looking at the screenshot of the document you shared, the document ID in the second column is different from the value of authUserID in the third column. So it seems like you added the document by calling add, which means that Firestore generates a unique ID for you.
You then create a reference to the document with this code:
_firestoreDB.collection('users').doc(dbUserID)
But here you are specifying dbUserID as the document ID, which doesn't match what you did when you created the document. Firestore now looks for a document with an ID that is the same as the user ID, which doesn't exist and thus gives you a snapshot where exists is false.
If you want to find the document for the user in your current structure, you can use a query to do so:
Future<DbUser?> getDBUserByDBUserId({required String dbUserID}) async {
final query = _firestoreDB.collection('users').where('authUserID', isEqualTo: dbUserID)
final snapshot = await query.get();
if (snapshot.size > 0) {
return DbUser.fromJson(snapshot.docs[0]!.data()!);
}
return null;
}
But a better solution might be to actually store the user document under its user ID. You can specify the document ID as shown in the documentation on setting a document. So here you'd call .document('theuserid').set(...) instead of add(...).
I am trying to get the JSON response from the server and output it to the console.
Future<String> login() async {
var response = await http.get(
Uri.encodeFull("https://etrans.herokuapp.com/test/2"),
headers: {"Accept": "application/json"});
this.setState(() {
data = json.decode(response.body);
});
print(data[0].name);
return "Success!";
}
Unhandled Exception: type '_InternalLinkedHashMap<String, dynamic>' is
not a subtype of type 'List
What could be the reason?
Here are 2 common ways this could go wrong:
If your response is a json array like
[
{
key1: value1,
key2: value2,
key3: value3,
},
{
key1: value1,
key2: value2,
key3: value3,
},
.....
]
Then, we use data[0]["name"], not data[0].name
Unless we cast to an object that has the name property, we cannot use data[0].name
We cast like this data = json.decode(response.body).cast<ObjectName>();
ObjectName can be whatever object you want (Inbuilt or Custom). But make sure it has the name property
If your response is a JSON object like
{
dataKey: [
{
key1: value1,
key2: value2,
key3: value3,
}
]
}
Then json.decode will return a Map, not a List
Map<String, dynamic> map = json.decode(response.body);
List<dynamic> data = map["dataKey"];
print(data[0]["name"]);
You can use
new Map<String, dynamic>.from(snapshot.value);
Easiest way (one dimensional):
Map<String, dynamic> data = new Map<String, dynamic>.from(json.decode(response.body));
print(data['name']);
You are trying to case an Instance of InternalLinkedHashMap which is not possible.
You should Serialize and deserialize it back to Map<String, dynamic>.
InternalLinkedHashMap<String, dynamic> invalidMap;
final validMap =
json.decode(json.encode(invalidMap)) as Map<String, dynamic>;
You have to convert the runtimeType of data from _InternalLinkedHashMap to an actual List.
One way is to use the List.from.
final _data = List<dynamic>.from(
data.map<dynamic>(
(dynamic item) => item,
),
);
If you need work with generic fields has a workaround:
class DicData
{
int tot;
List<Map<String, dynamic>> fields;
DicData({
this.tot,
this.fields
});
factory DicData.fromJson(Map<String, dynamic> parsedJson) {
return DicData(
tot: parsedJson['tot'],
//The magic....
fields : parsedJson["fields"] = (parsedJson['fields'] as List)
?.map((e) => e == null ? null : Map<String, dynamic>.from(e))
?.toList()
);
}
}
You can get this error if you are using retrofit.dart and declare the wrong return type for your annotated methods:
#GET("/search")
Future<List<SearchResults>> getResults();
// wrong! search results contains a List but the actual type returned by that endpoint is SearchResults
vs
#GET("/search")
Future<SearchResults> getResults();
// correct for this endpoint - SearchResults is a composite with field for the list of the actual results
This worked for me:
Create a List Data
Use Map to decode the JSON file
Use the List object Data to fetch the name of the JSON files
With the help of index and the list object I have printed the items dynamically from the JSON file
setState(){
Map<String, dynamic> map = json.decode(response.body);
Data = map["name"];
}
// for printing
Data[index]['name1'].toString(),
Seems like this error could pop up depending on various developer faults.
In my case, I was using an EasyLocalization key but without defining it under asset/lang/en_US.json file.
If your working with Firebase Cloud,make sure that you're not trying to add multiple data with same DocumentID;
firestore.collection('user').document(UNIQUEID).setData(educandos[0].toJson()).
To convert from _InternalLinkedHashMap<String, dynamic> to Map<String, double> I used
Map<String,double>.from(json['rates'])
I had the same error using json_annotation, json_serializable, build_runner. It occurs when calling the ClassName.fromJson() method for a class that had a class property (example: class User has a property class Address).
As a solution, I modified the generated *.g.dart files of each class, by changing Map<String, dynamic>) to Map<dynamic, dynamic>) in everywhere there is a deep conversion inside the method _$*FromJson
The only problem is that you have to change it again every time you regenerate your files.
I am trying to save the list data into the GetX Storage i.e. coming from firestore in my application.
But somehow, It's not executing as expected. Every time I got the error saying: Unhandled Exception: Converting object to an encodable object failed: Instance of 'DocumentData'
Below, List variable that I have created:
RxList<DocumentData> todoList = <DocumentData>[].obs;
Below, My function where I am trying to write the values in GetX Storage.
//Function to get the all todos from firestore
getAllTodosFromFirestore() async {
await todos.where('uid', isEqualTo: uid).get().then((value) {
Get.find<TodoController>().todoList.value = value.docs.map((e) => DocumentData.fromJson(e.data() as Map<String,dynamic>)).toList();
GetStoragePref.instance.writeValues('todos_list',Get.find<TodoController>().todoList);
});
return Get.find<TodoController>().todoList;
}
Below, My DocumentData model class.
class DocumentData {
DocumentData({
required this.uid,
required this.timer,
required this.itemName,
required this.isSelectedItem,
});
String uid;
Timestamp timer;
String itemName;
bool isSelectedItem;
factory DocumentData.fromJson(Map<String, dynamic> json) => DocumentData(
uid: json["uid"],
timer: json["timer"],
itemName: json["itemName"],
isSelectedItem: json["isSelectedItem"],
);
Map<String, dynamic> toJson() => {
"uid": uid,
"timer": timer,
"itemName": itemName,
"isSelectedItem": isSelectedItem,
};
}
Can anyone please help me to resolve this?
You can't store custom types (DocumentData in your case) directly on GetStorage. You can save the raw JSON (in your case it should be somewhere inside the value.docs) & when getting the data from GetStorage, parse the raw JSON to DocumentData again.
i just wanted to ask since this thing got me confused, i am still beginner with OOP i started it with Java and now working with it in flutter, so basically when i use a model in flutter, am i using it to fetch data from an api or a web server, am i right? let's say it's like select .. from .. in SQL, is that right? for example here i have this model of location
import './location_fact.dart';
class Location {
final String name;
final String url;
final List<LocationFact> facts;
Location({this.name, this.url, this.facts});
}
so basically in final name and final url i am specifying which data to get from the api or the web server ( in the example i am just giving fake data which just data i am giving it manually without a third party api or web server ) so when i use these i am just like using select name, url from "apĂ®" ? is that the deal here? and when i am using the Location({this.name, this.url, this.facts}) am i specifying which data this model will take as a parameter ? and when i am using final am i like referring to the data that it won't be fetched again once it's fetched? and when i am using final list <LocationFact> facts; am i specifying that this data is going to take the facts only from the list or what? i know this is overwhelming but i am really beginner with dart and flutter generally, i appreciate anyone's help and thank you.
I think you're reading too much magic into the word "model". In the original "MVC" triad, there are Models (places to stash data), Views (basically Widgets in Flutter), and Controllers (generally buried in Widgets in Flutter, but can be and should be pulled out to testable and reusable logic). Does that help?
First of all if you fetching data from API it will return data in json format as json Object or json List after fetching data from API you can use json data or you can convert json Object to Plain Dart Object
To convert json data to Plain Dart Object you have to specify your model class.
Here is an example to design a model class
class Location {
String name;
String url;
List<Facts> facts;
Location({this.name, this.url, this.facts});
Location.fromJson(Map<String, dynamic> json) {
name = json['name'];
url = json['url'];
if (json['facts'] != null) {
facts = new List<Facts>();
json['facts'].forEach((v) {
facts.add(new Facts.fromJson(v));
});
}
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['name'] = this.name;
data['url'] = this.url;
if (this.facts != null) {
data['facts'] = this.facts.map((v) => v.toJson()).toList();
}
return data;
}
}
class Facts {
String locationFact;
Facts({this.locationFact});
Facts.fromJson(Map<String, dynamic> json) {
locationFact = json['locationFact'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['locationFact'] = this.locationFact;
return data;
}
}
Here Location.fromJson() is an factory method to convert your json object to Plain Dart Object
For reference you should take a tour into the Official Documentation
Fetch Data From Network Flutter Documentation
i have a Firestore like the following :
My goal is to map each entry from the collection of days -> 'uid' -> collection to a datamodel called "Day" and return it as a stream to Provider.
Every entry in this collection in a date, which is holding a reference to a transfer.
This is the transfer storage :
This is the Day Datamodel :
class Day {
Day({this.date, this.transfers});
final String date;
final List<Transfer> transfers;
}
This is the code i tried (which is currently missing the deserializing of the List of Transfers, but i was lost at this point) :
Stream<List<Day>> get dayTransferData {
return daysCollection.document('QeG5SgSoYPXCpPkU86Z9ReShr2j2').collection('').snapshots().map(_brewListFromSnapshot);
}
List<Day> _brewListFromSnapshot(QuerySnapshot snapshot) {
return snapshot.documents.map((doc) {
return Day(
date: doc.documentID,
transfers: doc.data.values,
);
}).toList();
}
I searched on stackoverflow but everyone was referencing the collection by name.
How do i get the data of the collection and map it in the correct way ?
User class with JSON convert: JSON and serialization
class User {
String name;
int age;
User({
this.name,
this.age,
});
factory User.fromJson(String str) => User.fromMap(json.decode(str));
String toJson() => json.encode(toMap());
factory User.fromMap(Map<String, dynamic> json) => User(
name: json["name"],
age: json["age"],
);
Map<String, dynamic> toMap() => {
"name": name,
"age": age,
};
}
Create user list :
users = jsonList.map((json) => User.fromJson(json)).toList();