0

In the excellent Flutter book by Raywenderlich, Chapter 13 is dedicated to obtaining Responses from an api using libraries Chopper and JsonConverter.

The code in Github is here

He also proposes the use of a response wrapper like functional programming, of the type Success/Error.

The ModelConverter from Response to Success/Error wrapper, applies to the APIRecipeQuery model and only uses one method in one line final recipeQuery = APIRecipeQuery.fromJson(mapData);. It seems quite logical to make a generic convert, since it is a very useful class.

import 'dart:convert';
import 'package:chopper/chopper.dart';

import 'model_response.dart';
import 'recipe_model.dart';

class ModelConverter implements Converter {
  @override
  Request convertRequest(Request request) {
    // Add a header to the request that says you have a request type of application/json using jsonHeaders.
    // These constants are part of Chopper.
    final req = applyHeader(
      request,
      contentTypeKey,
      jsonHeaders,
      override: false,
    );

    return encodeJson(req);
  }

  @override
  Response<BodyType> convertResponse<BodyType, InnerType>(Response response) {
    return decodeJson<BodyType, InnerType>(response);
  }

  Request encodeJson(Request request) {
    final contentType = request.headers[contentTypeKey];
    // Confirm contentType is of type application/json.
    if (contentType != null && contentType.contains(jsonHeaders)) {
      return request.copyWith(body: json.encode(request.body));
    }
    return request;
  }

  Response decodeJson<BodyType, InnerType>(Response response) {
    final contentType = response.headers[contentTypeKey];
    var body = response.body;
    if (contentType != null && contentType.contains(jsonHeaders)) {
      body = utf8.decode(response.bodyBytes);
    }
    try {
      final mapData = json.decode(body);
      if (mapData['status'] != null) {
        return response.copyWith<BodyType>(
            body: Error(Exception(mapData['status'])) as BodyType);
      }
/*
The only line is next
*/
      final recipeQuery = APIRecipeQuery.fromJson(mapData);
      return response.copyWith<BodyType>(
          body: Success(recipeQuery) as BodyType);
    } catch (e) {
      chopperLogger.warning(e);
      return response.copyWith<BodyType>(body: Error(e) as BodyType);
    }
  }

So, I have tried by passing the model in the constructor as a parameter:

class ModelConverter <T extends JsonConverter> implements Converter {
  final T model;
  ModelConverter ({@required this.model});

and I invoke it in recipe_service.dart with converter: ModelConverter(model: APIRecipeQuery), but I don't know how to reference the model statically, and can't access the method model.fromJson

Next, I have tried passing just the function converter:

class ModelConverter implements Converter {
  Function fromJson;
  ModelConverter ({@ required this.fromJson});

with a getter in the API, and in recipe_service.dart with converter: ModelConverter(fromJson: APIRecipeQuery.fjConverter)

class APIRecipeQuery {
  static Function get fjConverter => _ $ APIRecipeQueryFromJson;

But I can't get it to work.

What would be the best approach to make the ModelConverter generic? Thnks in advance.

Santi
  • 491
  • 6
  • 14

1 Answers1

-1

Solved in this post

model_converter.dart

. . .
typedef CreateModelFromJson = dynamic Function(Map<String, dynamic> json);

class ModelConverter<Model> implements Converter {
  final CreateModelFromJson fromJson;

  ModelConverter({@required this.fromJson});
. . .
  final query = fromJson(mapData) as Model;
. . .

and recipe_service.dart

. . .
      converter: ModelConverter<APIRecipeQuery>(
        fromJson: (json) => APIRecipeQuery.fromJson(json),
      ),
. . .
Santi
  • 491
  • 6
  • 14