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
Related
I make an API request and get data. I have created models in which the received values are stored. I'm using the gallery variable to store a list of photo links. Tell me, how can I access an element from this list of the gallery variable in order to display a photo by link?
model
class User {
final int id;
final List<PhotoModel>? gallery;
User({
required this.id,
this.gallery,
});
factory User.fromJson(Map<String, dynamic> json) {
List<PhotoModel> ph = [];
json['gallery'].forEach((v) {
ph.add(PhotoModel.fromJson(v));
});
return User(
id: json['id'] as int,
gallery: ph,
);
}
}
model2
class PhotoModel {
final String url;
PhotoModel({required this.url});
factory PhotoModel.fromJson(Map<String, dynamic> json) {
return PhotoModel(
url: json['url'],
);
}
}
Here is what I get
You print out user.gallary which is a list of PhotoModel instances. So your output makes sense it seems so that your gallery contains one PhotoModel.
If you want a specific output for each PhotoModel you have several options:
Override the toString() in the PhotoModel class and return the url
Change your print statement so it directly prints the url instead of the object itself. print(widget.state.user!.gallery.map((p) => p.url));
Another alternative is to add an toJson() method to your class which works the other way around like fromJson() and returns a map. If you call this method and print the map every attribute of the model will be visible.
I am setting up my model classes to confirm to the docs for sqflite which suggest including a named constructor to convert to/from Maps to better handling of data between the classes and the DB. Every example I can find is very simple, with class properties all being simple data types.
Using the constructor and method shown below, converting to/from Map is quite simple when dealing with a class such as this.
class Human{
final String name;
final String height;
Final String weight;
Human({this.name, this.height, this.weight});
}
However, when you have a class where one of the fields is a bit more complex, I do not understand how to structure things within the named constructor and xxx method to return the map of data that I 'believe' I should get.
class Human{
final String name;
final String height;
Final String weight;
List<Child> children = [];
Human({this.name, this.height, this.weight, this.children});
}
Human({this.name, this.height, this.weight, this.children});
Human.fromMap(Map<String, dynamic> map)
: name = map['name'],
height = map['height'],
weight = map['weight'],
children = map['children'];
Map<String, dynamic> toMap() {
return {
'name': name,
'height': height,
'weight': weight,
'children': children,
};
}
The List children is the part I am struggling with. I believe you have to get each Child object ALSO converted to a map within the parent map, but am losing the battle here.
Is my approach way off here? Is there some other method I should be using to accomplish this?
Any assistance would be much appreciated.
Here I am explaining the following
How to convert a model object into Map to use with sqlite
How to convert a Map object from sqlite into a model class.
How to parse JSON reponse properly in flutter
How to convert a model object into JSON
All of the above questions has same answer. Dart has great support for these operations. Here I am going to illustrate it with a detailed example.
class DoctorList{
final List<Doctor> doctorList;
DoctorList({this.doctorList});
factory DoctorList.fromMap(Map<String, dynamic> json) {
return DoctorList(
doctorList: json['doctorList'] != null
? (json['doctorList'] as List).map((i) => Doctor.fromJson(i)).toList()
: null,
);
}
Map<String, dynamic> toMap() {
final Map<String, dynamic> data = Map<String, dynamic>();
if (this.doctorList != null) {
data['doctorList'] = this.doctorList.map((v) => v.toMap()).toList();
}
return data;
}
}
The above DoctorList class has a member which holds a list of 'Doctor' objects..
And see how I parsed the doctorList.
doctorList: json['doctorList'] != null
? (json['doctorList'] as List).map((i) => Doctor.fromMap(i)).toList()
: null,
You may wonder, how the Doctor class may look like. Here you go
class Doctor {
final String doCode;
final String doctorName;
Doctor({this.doCode, this.doctorName});
factory Doctor.fromMap(Map<String, dynamic> json) {
return Doctor(
doCode: json['doCode'],
doctorName: json['doctorName'],
);
}
Map<String, dynamic> toMap() {
final Map<String, dynamic> data = Map<String, dynamic>();
data['doCode'] = this.doCode;
data['doctorName'] = this.doctorName;
return data;
}
}
That's all. Hope you got the idea. Cheers!
I am new in Flutter, this is my first project in Flutter. I want to do a project that once data are loaded from API, they're cached in the device. Next time it can be loaded very fast even if my device is offline. I here that can using Dio package with dio cache manager package for caching server's json response. And then using cache image package to cache images. Anyone can give me some example how to write the code? Thanks in advance
Another best way for caching in flutter is the use of the hive. And it retrieves data faster than Sqflite and Shared preferences
For example, you can check this GitHub repo:https://github.com/shashiben/Anime-details
This will show how to cache the rest API data into the hive and for the next time it will show data from the hive.
I think this answer helped you
Yeah i'd recommend the sqflite package for flutter, i've just figured out how to use it to solve this same issue! I learnt it using this YouTube video: https://youtu.be/1BwjNEKD8g8.
Try shared preferences https://pub.dev/packages/shared_preferences,
Just Decode your response as string and save to sharePreference,
and Encode that String to Object when you need.
import 'package:shared_preferences/shared_preferences.dart';
//storing response as string
SharedPreferences sharedPref = await SharedPreferences.getInstance();
Map decode_options = jsonDecode(jsonString);
String user = jsonEncode(User.fromJson(decode_options));
sharedPref.setString('user', user);//storing
//getting string and converting into Object
SharedPreferences sharedPref = await SharedPreferences.getInstance();
Map userMap = jsonDecode(sharedPref.getString('user')); //retriving
var user = User.fromJson(userMap);
class User {
final String name;
final String age;
User({this.name, this.age});
factory User.fromJson(Map<String, dynamic> parsedJson) {
return new User(
name: parsedJson['name'] ?? "",
age: parsedJson['age'] ?? "");
}
Map<String, dynamic> toJson() {
return {
"name": this.name,
"age": this.age
};
}
}
I know you can parse a json string then check if a value is of specific type. I wonder is there is a convention in Flutter to do that?
Example:
"{"id":1,"some_key":100}"
might be also
"{"id":1,"some_key":"GOOD"}"
Right now my objects is:
class someClass {
int id,
int some_key
}
I will have to change the some key to a String type I guess and then in the parsing check if the type is not string to convert the some_key to string? or is there a way do that?
Just make that variable dynamic in the class for parsing whatever data you recieve and when you are going to use it. You can just check it by using
someKey.runtimeType
to check the type and use by whatever way you want.
Your class will look something like this
class SomeClass {
int id;
dynamic someKey;
SomeClass({this.id, this.someKey});
SomeClass.fromJson(Map<String, dynamic> json) {
id = json['id'];
someKey = json['some_key'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['id'] = this.id;
data['some_key'] = this.someKey;
return data;
}
}
and where you want to parse the data use this
SomeClass someclass = SomeClass.fromJson(jsonDecode(<YOUR JSON STRING HERE>));
I am using same model to parse 2 json responses,
In one response an attribute user is string type, and in the other response user is an object.
How could I parse in this situation? I tried,
CampaignProductDetails.fromJson(Map<String, dynamic> json) {
user= json['user'];
}
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = new Map<String, dynamic>();
data['user'] = this.user;
return data;
}
But it shows _typeError when user is an object. How should I sort it out? Do I need to create a new model for this single attribute?
You can use is operator in Dart
CampaignProductDetails.fromJson(Map<String, dynamic> json) {
user= json['user'] == null ? null : (json['user'] is String ? json['user'] : this.user;
}
as an alternative, In Dart every object has a runtimeType instance member which returns type of object at runtime ( I wouldn't advice to use it on production, somewhere read that it is only for debugging purpose).