Flutter JSON: type 'List<dynamic>' is not a subtype of type 'String'

Issue

I’m building an app to return JSON data from an API. The error occurs when parsing the data for RouteLine.

route_table.dart

class RouteTable {
  int? deliveryPeriod = 0;
  String? driverId = "";
  String? processId = "";
  String? routeId = "";
  RouteLine? routeLines;
  int? status = 0;
  String? vanId = "";

 factory RouteTable.fromJson(Map<String, dynamic> json) {
    return RouteTable(
        deliveryPeriod: json['deliveryPeriod'],
        driverId: json['driverId'],
        processId: json['processId'], 
        routeId: json['routeId'],
        routeLines: json['routeLines'] == null
            ? null
            : RouteLine.fromJson(jsonDecode(json['routeLines'])),
        status: json['status'],
        vanId: json['vanId']);
}

route_line.dart

class RouteLine {
  String? city = "";
  String? custAccount = "";
  String? custName = "";
  String? invoiceId = "";
  DateTime? deliveryDate = initDateTime();
  int? productType = 0;
  String? routeId = "";
  String? salesId = "";
  double? volume = 0.0;
  double? weight = 0.0;

 factory RouteLine.fromJson(Map<String, dynamic> json) {
    return new RouteLine(
      city: json['city'] as String,
      custAccount: json['custAccount'] as String,
      custName: json['custName'] as String,
      invoiceId: json['invoiceId'] as String,
      deliveryDate: json['deliveryDate'] as DateTime,
      productType: json['productType'] as int,
      routeId: json['routeId'] as String,
      salesId: json['salesId'] as String,
      volume: json['volume'] as double,
      weight: json['weight'] as double,
    );
}

main.dart file where I’m trying to display the results.

import 'dart:io';
import 'package:flutter/material.dart';
import 'route_line.dart';
import 'route_table.dart';
import 'networking.dart';

void main() {
  HttpOverrides.global = new MyHttpOverrides();
  runApp(MyApp());
}



class MyApp extends StatelessWidget {
// to set the root of app.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.green,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'API Demo Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key? key, this.title = ""}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late Future<RouteTable> futureRouteTable;
  @override
  void initState() {
    super.initState();
    futureRouteTable = fetchData();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: Text("Flutter - API Implementation"),
      ),
      body: Center(
          child: // build list view & manage states
              FutureBuilder<RouteTable>(
        future: futureRouteTable,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.done) {
            final RouteTable? posts = snapshot.data;
            if (snapshot.data != null) {
              return Text(snapshot.data!.routeId.toString());
            }
            return Text('No Data');
          } else {
            return Center(
              child: CircularProgressIndicator(),
            );
          }
        },
      )),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {},
        label: Icon(Icons.cancel),
        backgroundColor: Colors.green,
      ),
    );
  }

Future<RouteTable> fetchData() async {
  final response =
      await http.get(Uri.parse('https://10.0.2.2:7038/api/route/1?siteId=1'));

  try {
    if (response.statusCode == 200) {
      // If the server did return a 200 OK response,
      // then parse the JSON.
      return RouteTable.fromJson(jsonDecode(response.body));
    } else {
      // If the server did not return a 200 OK response,
      // then throw an exception.
      throw Exception('Failed to load route');
    }
  } catch (e) {
    print(e);
  }
  return new RouteTable(
      deliveryPeriod: 0,
      driverId: "",
      processId: "",
      routeId: "",
      routeLines: new RouteLine(
          city: "",
          custAccount: "",
          custName: "",
          invoiceId: "",
          deliveryDate: new DateTime(2022),
          productType: 0,
          routeId: "",
          salesId: "",
          volume: 0.0,
          weight: 0.0),
      status: 0,
      vanId: "");
}
}

The JSON data being returned

{
   "deliveryPeriod":1,
   "driverId":"",
   "processId":"PRO000492",
   "routeId":"ROU001858",
   "routeLines":[
      {
         "city":"Naxxar",
         "custAccount":"CUST010922",
         "custName":"",
         "deliveryDate":"2021-12-06T12:00:00",
         "invoiceId":"",
         "productType":0,
         "routeId":"ROU001858",
         "salesId":"SO002579",
         "volume":0.019448889,
         "weight":15.225435
      }
   ],
   "status":30,
   "vanId":"TFI 943"
}

I’m running into the following error when trying to parse RouteLine inside RouteTable.fromJson.

type 'List<dynamic>' is not a subtype of type 'String'

Tried without the jsonDecode and get a similar error.

type 'List<dynamic>' is not a subtype of type 'Map<String, dynamic>'

How do I turn it to a List? What am I missing?

Solution

You can use QuickType to convert JSON into Dart Models. Here is your dart data model which I generated through it.

import 'dart:convert';

class RouteTable {
    RouteTable({
        this.deliveryPeriod,
        this.driverId,
        this.processId,
        this.routeId,
        this.routeLines,
        this.status,
        this.vanId,
    });

    int deliveryPeriod;
    String driverId;
    String processId;
    String routeId;
    List<RouteLine> routeLines;
    int status;
    String vanId;

    factory RouteTable.fromJson(Map<String, dynamic> json) => RouteTable(
        deliveryPeriod: json["deliveryPeriod"],
        driverId: json["driverId"],
        processId: json["processId"],
        routeId: json["routeId"],
        routeLines: List<RouteLine>.from(json["routeLines"].map((x) => RouteLine.fromJson(x))),
        status: json["status"],
        vanId: json["vanId"],
    );

    Map<String, dynamic> toJson() => {
        "deliveryPeriod": deliveryPeriod,
        "driverId": driverId,
        "processId": processId,
        "routeId": routeId,
        "routeLines": List<dynamic>.from(routeLines.map((x) => x.toJson())),
        "status": status,
        "vanId": vanId,
    };
}

class RouteLine {
    RouteLine({
        this.city,
        this.custAccount,
        this.custName,
        this.deliveryDate,
        this.invoiceId,
        this.productType,
        this.routeId,
        this.salesId,
        this.volume,
        this.weight,
    });

    String city;
    String custAccount;
    String custName;
    DateTime deliveryDate;
    String invoiceId;
    int productType;
    String routeId;
    String salesId;
    double volume;
    double weight;

    factory RouteLine.fromJson(Map<String, dynamic> json) => RouteLine(
        city: json["city"],
        custAccount: json["custAccount"],
        custName: json["custName"],
        deliveryDate: DateTime.parse(json["deliveryDate"]),
        invoiceId: json["invoiceId"],
        productType: json["productType"],
        routeId: json["routeId"],
        salesId: json["salesId"],
        volume: json["volume"].toDouble(),
        weight: json["weight"].toDouble(),
    );

    Map<String, dynamic> toJson() => {
        "city": city,
        "custAccount": custAccount,
        "custName": custName,
        "deliveryDate": deliveryDate.toIso8601String(),
        "invoiceId": invoiceId,
        "productType": productType,
        "routeId": routeId,
        "salesId": salesId,
        "volume": volume,
        "weight": weight,
    };
}

Answered By – Muhammad Usama Siddiqui

Answer Checked By – Marilyn (FlutterFixes Volunteer)

Leave a Reply

Your email address will not be published.