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.
Related
I have a specific situation, so I need to put some data into a model with FutureProvider but I also need to have some additional fields that I want to change during the app, these fields need to be reactive. So this is my StateNotifier class:
class AirportNotifier extends StateNotifier<List<Airport>> {
AirportNotifier(super.state);
void updateIsActive(String value, int index) {
state[index].isActive = value;
}
}
My base class:
class Airport {
final String? name;
final String? description;
//final Uint8List? imageBytes;
String? isActive = "ve";
Airport({
this.name,
this.description,
});
factory Airport.fromJson(Map<String, dynamic> json) {
return Airport(name: json['name'], description: json['description']
//imageBytes: base64.decode(json['icon']),
);
}
}
Here I'm trying to create StateNotifier provider
final checkProvider =
StateNotifierProvider<AirportNotifier, List<Airport>>((ref) {
return AirportNotifier();
});
So, one way is to not use FutureProvider, in that way solution will be to send data like argument here
AirportNotifier(---);
But I have problem with converting Future<List> to List, every single solution on SO is just some basic things, I just need to convert this to List or to combine StateNotifier with FutureProvider on some way
Using Flutter and Dart, lets say I have this class:
#JsonSerializable()
class User {
#JsonKey(nullable: true, required: false)
final String name;
#JsonKey(nullable: true, required: false)
final int age;
User({
this.name,
this.age,
});
factory User.fromJson(Map<String, dynamic> json) => _$AddressFromJson(json);
Map<String, dynamic> toJson() => _$AddressToJson(this);
#override
String toString() {
return 'User ${toJson().toString()}';
}
}
In my code I'm trying to create a new instance of this class to be sent to /update-user endpoint on the server. My goal is to send an object that contains just the properties I would like the server to update. let's say only update age.
Using
final dto = new UpdateUserRequest(
age: 34
);
results in this json representation: {name: null, age: 34}, which will override the already existing name on the server.
I also tried 'json merging' 2 objects, the one I already have with the name, and the new dto that updates age:
final combined = UpdateUserRequest.fromJson({
...(dtoWithAge.toJson()),
...(existingUserWithName.toJson()),
});
but no matter how I play around with these, they end up overriding each other.
So, is there anyway to get a json/DTO instance of the class, that only contains the properties and values I want the server to update? (trying to achieve something very similar to javascript)
I don't think there's a pre-implemented solution for that. Assuming I understood what you're trying to accomplish, how about adding a copyWith method to User?
User copyWith({
String name,
int age,
}) => User(
name: name ?? this.name,
age: age ?? this.age,
);
You'd use it like this:
final existingUser = User(name: 'John');
final updatedUser = existingUser.copyWith(age: 25);
sendUpdateRequestWith(updatedUser);
For you convenience, there's a plugin to generate it.
vscode: https://marketplace.visualstudio.com/items?itemName=BendixMa.dart-data-class-generator
IDEA/Android Studio: https://plugins.jetbrains.com/plugin/12429-dart-data-class
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 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 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>));