1

I'm not exactly sure whats happened because my code was working fine but all of a sudden stopped working and I dont think I changed anything in relation to this. I've been stuck on this for two days and can't figure out what I'm doing wrong. The data being returned from the API is correct and it returns with a status code of 200

I believe the error is something to do with the PTWorkoutPlanItem.fromJson() function. I dont think it has anything to do with the List<AssignedUsers> or List<WorkoutPlanItem> models in the PTWorkoutPlanItem model as when I change these to dynamic, the error still occurs

The error says

[ERROR:flutter/lib/ui/ui_dart_state.cc(177)] Unhandled Exception: type 'String' is not a subtype of type 'Map<String, dynamic>'

code to get the data:

  getPlanData() async {
    var data = PTWorkoutPlanItem.fromJson(
        await get(url: 'pt/workouts/plan/' + planId.toString()));
  }

// Console logged the response here and it is correct, so this function works
Future<dynamic> get({url}) async {
  final response = await http.get(baseUrl + url, headers: headers);
  print(response.statusCode);

  dynamic res = json.decode(response.body);
  return res;
}

models

class PTWorkoutPlanItem {
  int id;
  String title;
  List<AssignedUsers> assignedUsers;
  List<WorkoutPlanItem> workouts;
  int ptId;

  PTWorkoutPlanItem(
      {this.id, this.title, this.assignedUsers, this.workouts, this.ptId});

  factory PTWorkoutPlanItem.fromJson(Map<String, dynamic> json) {
    return PTWorkoutPlanItem(
      id: json['id'],
      title: json['title'],
      assignedUsers: List.from(json['assignedUsers'])
          .map((item) => AssignedUsers.fromJson(item))
          .toList(),
      workouts: List.from(json['workouts'])
          .map((item) => WorkoutPlanItem.fromJson(item))
          .toList(),
      ptId: json['ptId'],
    );
  }
}

class AssignedUsers {
  int id;
  String title;

  AssignedUsers({this.id, this.title});

  factory AssignedUsers.fromJson(Map<String, dynamic> json) {
    return AssignedUsers(id: json['id'], title: json['title']);
  }
}

class WorkoutPlanItem {
  int id;
  String title;
  int duration;
  int sets;
  int rest;

  WorkoutPlanItem({this.id, this.title, this.duration, this.sets, this.rest});

  factory WorkoutPlanItem.fromJson(Map<String, dynamic> json) {
    return WorkoutPlanItem(
      id: json['id'],
      title: json['title'],
      duration: json['duration'],
      sets: json['sets'],
      rest: json['rest'],
    );
  }
}

this is what gets retuned in the API

{
    "id": 1,
    "title": "Pull day",
    "assignedUsers": [
        {
            "id": 1,
            "title": "josh"
        },
        {
            "id": 2,
            "title": "marus"
        }
    ],
    "workouts": [
        {
            "id": 4,
            "title": "Workout item 4",
            "duration": 10,
            "sets": 3,
            "rest": 3
        },
        {
            "id": 1,
            "title": "Workout item 1",
            "duration": 10,
            "sets": 3,
            "rest": 3
        }
    ],
    "ptId": 1
}

1 Answer 1

2

Try this. I have added a lot of type casts but the most important part is how I handle the List<dynamic> and cast each element to Map<String, dynamic>:

class PTWorkoutPlanItem {
  int id;
  String title;
  List<AssignedUsers> assignedUsers;
  List<WorkoutPlanItem> workouts;
  int ptId;

  PTWorkoutPlanItem(
      {this.id, this.title, this.assignedUsers, this.workouts, this.ptId});

  factory PTWorkoutPlanItem.fromJson(Map<String, dynamic> json) {
    return PTWorkoutPlanItem(
      id: json['id'] as int,
      title: json['title'] as String,
      assignedUsers: (json['assignedUsers'] as List<dynamic>)
          .cast<Map<String, dynamic>>()
          .map((item) => AssignedUsers.fromJson(item))
          .toList(),
      workouts: (json['workouts'] as List<dynamic>)
          .cast<Map<String, dynamic>>()
          .map((item) => WorkoutPlanItem.fromJson(item))
          .toList(),
      ptId: json['ptId'] as int,
    );
  }
}

class AssignedUsers {
  int id;
  String title;

  AssignedUsers({this.id, this.title});

  factory AssignedUsers.fromJson(Map<String, dynamic> json) {
    return AssignedUsers(id: json['id'] as int, title: json['title'] as String);
  }
}

class WorkoutPlanItem {
  int id;
  String title;
  int duration;
  int sets;
  int rest;

  WorkoutPlanItem({this.id, this.title, this.duration, this.sets, this.rest});

  factory WorkoutPlanItem.fromJson(Map<String, dynamic> json) {
    return WorkoutPlanItem(
      id: json['id'] as int,
      title: json['title'] as String,
      duration: json['duration'] as int,
      sets: json['sets'] as int,
      rest: json['rest'] as int,
    );
  }
}

Also, here is a Dart 2.12 compatible version where required as been added to the named parameters since they are all not allowed to be null and does not have any default value:

class PTWorkoutPlanItem {
  int id;
  String title;
  List<AssignedUsers> assignedUsers;
  List<WorkoutPlanItem> workouts;
  int ptId;

  PTWorkoutPlanItem(
      {required this.id,
      required this.title,
      required this.assignedUsers,
      required this.workouts,
      required this.ptId});

  factory PTWorkoutPlanItem.fromJson(Map<String, dynamic> json) {
    return PTWorkoutPlanItem(
      id: json['id'] as int,
      title: json['title'] as String,
      assignedUsers: (json['assignedUsers'] as List<dynamic>)
          .cast<Map<String, dynamic>>()
          .map((item) => AssignedUsers.fromJson(item))
          .toList(),
      workouts: (json['workouts'] as List<dynamic>)
          .cast<Map<String, dynamic>>()
          .map((item) => WorkoutPlanItem.fromJson(item))
          .toList(),
      ptId: json['ptId'] as int,
    );
  }
}

class AssignedUsers {
  int id;
  String title;

  AssignedUsers({required this.id, required this.title});

  factory AssignedUsers.fromJson(Map<String, dynamic> json) {
    return AssignedUsers(id: json['id'] as int, title: json['title'] as String);
  }
}

class WorkoutPlanItem {
  int id;
  String title;
  int duration;
  int sets;
  int rest;

  WorkoutPlanItem(
      {required this.id,
      required this.title,
      required this.duration,
      required this.sets,
      required this.rest});

  factory WorkoutPlanItem.fromJson(Map<String, dynamic> json) {
    return WorkoutPlanItem(
      id: json['id'] as int,
      title: json['title'] as String,
      duration: json['duration'] as int,
      sets: json['sets'] as int,
      rest: json['rest'] as int,
    );
  }
}

Update: Can you also try change your method into this:

Future<Map<String, dynamic>> get({url}) async {
  final response = await http.get(baseUrl + url, headers: headers);
  print(response.statusCode);

  final res = json.decode(response.body) as Map<String, dynamic>;
  return res;
}
Sign up to request clarification or add additional context in comments.

2 Comments

I'm still getting the same issue with both solutions
@chumberjosh Updated my answer with changes to your get method.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.