My objective is create a singleton class that handles the web-server requests and pass a generic type (which is my data model) as a parameter to decode and assign to my data model. My partially completed code as bellow, help would much appreciate.
class Network{
Future<someDataModel> _getRequest(T) async {
print("entered");
final response = await client
.get("http://api.themoviedb.org/3/movie/popular?api_key=$_apiKey");
print(response.body.toString());
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
return T.fromJson(json.decode(response.body));
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
In the same class I access the getRequest with a public method as bellow. Therefore, through this method my intention is to pass my data model to the get request as a generic type parameter and let the decoding part happen. partially completed code as bellow.
getAllList(){
return _getRequest(dataModel);
}
The solution below is suitable for generic objects as well as List of generic objects (from a JSON list response).
First, you need to have a function that checks the type of the generic object and returns the result of the corresponding fromJson
call:
class Generic {
/// If T is a List, K is the subtype of the list.
static T fromJson<T, K>(dynamic json) {
if (json is Iterable) {
return _fromJsonList<K>(json) as T;
} else if (T == LoginDetails) {
return LoginDetails.fromJson(json) as T;
} else if (T == UserDetails) {
return UserDetails.fromJson(json) as T;
} else if (T == Message) {
return Message.fromJson(json) as T;
} else if (T == bool || T == String || T == int || T == double) { // primitives
return json;
} else {
throw Exception("Unknown class");
}
}
static List<K> _fromJsonList<K>(List<dynamic> jsonList) {
return jsonList?.map<K>((dynamic json) => fromJson<K, void>(json))?.toList();
}
}
And then your function would end up looking like so:
class Network {
/// If T is a List, K is the subtype of the list.
Future<T> _getRequest<T, K>() async {
print("entered");
final response = await client
.get("http://api.themoviedb.org/3/movie/popular?api_key=$_apiKey");
print(response.body.toString());
if (response.statusCode == 200) {
// If the call to the server was successful, parse the JSON
return Generic.fromJson<T, K>(json.decode(response.body));
} else {
// If that call was not successful, throw an error.
throw Exception('Failed to load post');
}
}
If for example you would expect the response to be a Message, you would call _getRequest<Message, void>()
. If you would expect a list of Messages, you would call _getRequest<List<Message>, Message>()
.
Additionally, if you have a response wrapper with a generic data object as it is often the case, you can configure it like so:
class Wrapper<T, K> {
bool? isSuccess;
T? data;
Wrapper({
this.isSuccess,
this.data,
});
factory Wrapper.fromJson(Map<String, dynamic> json) => _$WrapperFromJson(json);
Map<String, dynamic> toJson() => _$WrapperToJson(this);
}
Wrapper<T, K> _$WrapperFromJson<T, K>(Map<String, dynamic> json) {
return Wrapper<T, K>(
isSuccess: json['isSuccess'] as bool?,
data: json['data'] == null ? null : Generic.fromJson<T, K>(json['data']),
);
}
Map<String, dynamic> _$WrapperToJson(Wrapper wrapper) => {
"isSuccess": wrapper.isSuccess,
"data": wrapper.data,
};
And then use it like so:
Wrapper<T, K>.fromJson(json.decode(response.body))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With