Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass a generic type as a parameter to a Future in flutter

Tags:

flutter

dart

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);
 }
like image 240
danu Avatar asked May 23 '19 09:05

danu


1 Answers

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))
like image 79
Ovidiu Avatar answered Sep 29 '22 20:09

Ovidiu