Resolving JSON Parsing Error in Flutter: List<dynamic> is not a subtype of type Map<String, dynamic>

Nov 24, 2025 · Programming · 26 views · 7.8

Keywords: Flutter | JSON Parsing | Type Error | API Integration | Dart Programming

Abstract: This technical article provides an in-depth analysis of the common JSON parsing error 'List<dynamic> is not a subtype of type Map<String, dynamic>' in Flutter development. Using JSON Placeholder API as an example, it explores the differences between JSON arrays and objects, presents complete model class definitions, proper asynchronous data fetching methods, and correct usage of FutureBuilder widget. The article also covers debugging techniques and best practices to help developers avoid similar issues.

Problem Analysis

In Flutter application development, handling JSON data is a common requirement. When fetching data from APIs and parsing it, developers may encounter type conversion errors. The specific error message is: type 'List<dynamic>' is not a subtype of type 'Map<String, dynamic>'. The core issue here is data type mismatch.

Let's analyze this problem through a concrete example. Suppose we're building an application that reads user data from JSON Placeholder API. The API endpoint returns a JSON array rather than a single JSON object. This means when we decode the response body using json.decode(response.body), we get data of type List<dynamic>, not the expected Map<String, dynamic>.

Root Cause of the Error

In the provided code example, the fetchInfo function directly calls Users.fromJson(jsonresponse), where jsonresponse is the list obtained from decoding the API response. However, the Users.fromJson factory constructor expects to receive a Map<String, dynamic> parameter, which causes the type mismatch error.

The /users endpoint of JSON Placeholder returns an array of users with the following data structure:

[
  {
    "id": 1,
    "name": "Leanne Graham",
    "username": "Bret",
    "email": "Sincere@april.biz",
    "address": {
      "street": "Kulas Light",
      "suite": "Apt. 556",
      "city": "Gwenborough",
      "zipcode": "92998-3874",
      "geo": {
        "lat": "-37.3159",
        "lng": "81.1496"
      }
    },
    "phone": "1-770-736-8031 x56442",
    "website": "hildegard.org",
    "company": {
      "name": "Romaguera-Crona",
      "catchPhrase": "Multi-layered client-server neural-net",
      "bs": "harness real-time e-markets"
    }
  },
  // More user objects...
]

Solution Approaches

To resolve this issue, we need to properly handle JSON arrays. There are two main approaches:

Approach 1: Fetch Single User Object

If we only need data from the first user, we can modify the fetchInfo function:

Future<Users> fetchInfo() async {
  final response = await http.get(Uri.parse(jsonplaceholder));
  final jsonresponse = json.decode(response.body);
  
  // Get the first element from the array
  return Users.fromJson(jsonresponse[0]);
}

This approach is straightforward and suitable for scenarios requiring only single user data.

Approach 2: Handle User List

A more common scenario is handling all user data. In this case, we should modify the code to support user lists:

// Modify model class to support list parsing
class UserList {
  final List<Users> users;
  
  UserList({this.users});
  
  factory UserList.fromJson(List<dynamic> parsedJson) {
    List<Users> users = parsedJson.map((i)=> Users.fromJson(i)).toList();
    return UserList(users: users);
  }
}

// Update fetchInfo function
Future<UserList> fetchInfo() async {
  final response = await http.get(Uri.parse(jsonplaceholder));
  final jsonresponse = json.decode(response.body);
  
  return UserList.fromJson(jsonresponse);
}

Complete Implementation Example

Here's the complete corrected code implementation:

import 'package:flutter/material.dart';
import 'dart:convert';
import 'dart:async';
import 'package:http/http.dart' as http;

final String jsonplaceholder = "http://jsonplaceholder.typicode.com/users/";

// User model class
class Users {
  final int userID;
  final String name;
  final String username;
  final String email;
  final Address address;

  Users({this.userID, this.name, this.username, this.email, this.address});

  factory Users.fromJson(Map<String, dynamic> usersjson) => Users(
    userID: usersjson["id"],
    name: usersjson["name"],
    username: usersjson["username"],
    email: usersjson["email"],
    address: Address.fromJson(usersjson["address"])
  );
}

// Address model class
class Address {
  final String street;
  final String suite;
  final String city;
  final String zipcode;

  Address({this.street, this.suite, this.city, this.zipcode});

  factory Address.fromJson(Map<String, dynamic> addjson) {
    return Address(
      street: addjson["street"],
      suite: addjson["suite"],
      city: addjson["city"],
      zipcode: addjson["zipcode"]
    );
  }
}

// User list model class
class UserList {
  final List<Users> users;
  
  UserList({this.users});
  
  factory UserList.fromJson(List<dynamic> parsedJson) {
    List<Users> users = parsedJson.map((i)=> Users.fromJson(i)).toList();
    return UserList(users: users);
  }
}

// Fetch user list
Future<UserList> fetchUsers() async {
  final response = await http.get(Uri.parse(jsonplaceholder));
  
  if (response.statusCode == 200) {
    final jsonresponse = json.decode(response.body);
    return UserList.fromJson(jsonresponse);
  } else {
    throw Exception('Failed to load users');
  }
}

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("User List"),
      ),
      body: FutureBuilder<UserList>(
        future: fetchUsers(),
        builder: (context, snapshot) {
          if (snapshot.hasData) {
            return ListView.builder(
              itemCount: snapshot.data.users.length,
              itemBuilder: (context, index) {
                Users user = snapshot.data.users[index];
                return ListTile(
                  title: Text(user.name),
                  subtitle: Text(user.email),
                  leading: CircleAvatar(
                    child: Text(user.username[0]),
                  ),
                );
              },
            );
          } else if (snapshot.hasError) {
            return Center(
              child: Text("Error: ${snapshot.error}"),
            );
          }
          
          return Center(
            child: CircularProgressIndicator(),
          );
        },
      ),
    );
  }
}

Debugging Techniques and Best Practices

When dealing with JSON parsing issues, the following debugging techniques and best practices can assist developers:

1. Check API Response Structure
Before writing parsing code, use tools like Postman or browser to inspect the actual API response structure. Confirm whether it returns an array or an object.

2. Use Type-Safe JSON Parsing
Consider using libraries like json_serializable or built_value for type-safe JSON serialization.

3. Add Error Handling
Implement proper error handling during parsing, including network errors, parsing errors, and data type errors.

4. Validate Data Integrity
When parsing JSON, check for the existence of required fields to avoid runtime errors due to missing fields.

5. Use Debugging Tools
Leverage Flutter's debugging tools and print statements to inspect intermediate data states and ensure data types match expectations.

Conclusion

The List<dynamic> is not a subtype of type Map<String, dynamic> error is a common JSON parsing issue in Flutter development. By understanding the differences between JSON arrays and objects and adopting correct parsing strategies, developers can effectively resolve this problem. The solutions provided in this article cover multiple levels from simple fixes to complete implementations, helping developers build robust Flutter applications.

Remember that good error handling and data type validation are crucial for building high-quality applications. In practical development, it's recommended to use type-safe JSON parsing libraries to reduce the occurrence of such runtime errors.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.