skip to Main Content

I am querying an API endpoint (on my Node API, which I can modify) and I expect the result to be of a certain complex type. I see no real way to share types between Node and a Flutter frontend, so I define types myself like so:

enum QuestionType {
  text,
  recording,
  goal,
}

class Question {
  final String question;
  final String? subtitle;
  // final QuestionType type;
  final int? timeInSeconds;
  final int? askGoalAfterDays;
  final bool userSubmitted;

  Question(
      {required this.question,
      // required this.type,
      this.subtitle,
      this.timeInSeconds,
      this.askGoalAfterDays,
      this.userSubmitted = false});
}

I hit an endpoint and get a list of these types. The response body is:

[{
  "id": 93,
  "createdAt": "2023-03-04T02:35:24.514Z",
  "updatedAt": "2023-03-04T02:36:36.000Z",
  "question": "...",
  "subtitle": null,
  "type": "Gratitude",
  "timeInSeconds": 20,
  "askGoalAfterDays": null,
  "userSubmitted": false
}, {
  "id": 3,
  "createdAt": "2023-03-04T02:35:24.546Z",
  "updatedAt": "2023-03-04T02:35:24.546Z",
  "question": "...",
  "subtitle": null,
  "type": "Rating",
  "timeInSeconds": null,
  "askGoalAfterDays": null,
  "userSubmitted": false
}, {
  "id": 2,
  "createdAt": "2023-03-04T02:35:24.548Z",
  "updatedAt": "2023-03-04T02:36:36.000Z",
  "question": "...",
  "subtitle": null,
  "type": "Goal",
  "timeInSeconds": 20,
  "askGoalAfterDays": 0,
  "userSubmitted": false
}]

I call the API and attempt to parse the content like so:

static Future<List<QuestionType>> getDaytime() async {
  // ...

  var response = await http.get(url);

  var res = jsonDecode(response.body) as List<QuestionType>; <-- ERROR

  if (response.statusCode == 200) {
    return [res[0], res[1]];
  } else {
    throw AdException(response.statusCode.toString());
  }
}

However when I try to parse the response I get the following error:

Unhandled Exception: type 'List<dynamic>' is not a subtype of type 'List<QuestionType>' in type cast

A few questions:

Why am I getting this error?
Is this the best way to parse and type content received from an API? There is obviously code duplication between my frontend and backend types, which seems wasteful.

2

Answers


  1. You can do this:

    Create a factory constructor to convert from json to class instance:

    factory Question.fromJson(Map<String, dynamic> json) {
      return Question(
      question: json['question'] as String,
      subtitle: json['subtitle'] as String,
      timeInSeconds: json['timeInSeconds'] as int,
      askGoalAfterDays: json['askGoalAfterDays'] as int, 
      userSubmitted: json['userSubmitted'] as bool);
    }
    

    And then you can parse your response body to json and then map to List:

    final parsed = jsonDecode(response.body).cast<Map<String, dynamic>>();
    List<Question> myList = parsed.map<Question>((json) => Question.fromJson(json)).toList();
    
    Login or Signup to reply.
  2. I dont know if this can help you but I’ll give it a try:

    You should try to place a fromJson(Map<String,dynamic>) method in your Question class.

    So basically your class will look something like this:

    enum QuestionType {
      text,
      recording,
      goal,
    }
    
    class Question {
       final String question;
       final String? subtitle;
       // final QuestionType type;
       final int? timeInSeconds;
       final int? askGoalAfterDays;
       final bool userSubmitted;
    
       Question({
          required this.question,
          // required this.type,
          this.subtitle,
          this.timeInSeconds,
          this.askGoalAfterDays,
          this.userSubmitted = false
       });
    
       Question fromJson(Map<String,dynamic> json){
          return Question(
             question: json['question'],
             subtitle: json['subtitle'],
             .......
             
          );          
       }
    }
    

    Now instead of doing var res = jsonEncode(response.body);, you should cycle the element inside response body and put it inside a new array and return it as response like this:

    static Future<List<Question>> getDaytime() async {
       // ...
    
       var response = await http.get(url);
    
       if (response.statusCode == 200) {
          List<Question> res = [];
    
          for(var quest in response.body){
             res.add(Question.fromJson(quest));
          }
    
          return res;
       } else {
          throw AdException(response.statusCode.toString());
       }
    }
    

    Hope it helps! 😀

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search