类型“ List <dynamic>”不是类型“ Map <String,dynamic>”的子类型

时间:2020-03-22 21:23:29

标签: flutter dart

我正在开发一个依赖API REST调用的flutter应用程序.API的响应有点复杂。 调用API(例如api / products)时,我可以从日志中看到响应: 但是我有这个错误:

类型'List <动态>'不是类型'Map <字符串,动态>'的子类型

我在互联网上看到了所有问题/答案,没有任何结果。 我已经尝试过使用像https://my-json-server.typicode.com/typicode/demo/posts这样的简单RestAPI来工作。 但这不是我的情况 API响应示例:

[
   {
      "id":1,
      "tenant":{
         "id":1,
         "code":"company",
         "name":"company"
      },
      "type":{
         "code":"activity",
         "name":"Activité"
      },
      "subType":{
         "code":"ticket",
         "name":"Ticket"
      },
      "inventoryType":{
         "code":"external_source",
         "name":"Source externe"
      },
      "externalReference":"CAL6970",
      "externalSystem":{
         "code":"koedia",
         "name":"Koedia"
      },
      "active":true,
      "durationDays":12,
      "durationNights":14,
      "durationHours":9,
      "durationMinutes":10,
      "supplier":{
         "id":1,
         "tenant":{
            "id":1,
            "code":"company",
            "name":"company"
         },
         "name":"Jancarthier"
      },
      "group":null,
      "subGroup":null,
      "name":"Hôtel Koulnoué Village",
      "translations":[
         {
            "id":1,
            "name":"Hôtel Koulnoué Village",
            "locale":"fr"
         },
         {
            "id":24,
            "name":"Hôtel Koulnoué Village",
            "locale":"en"
         }
      ],
      "vatPercentage":"0.00",
      "longitude":null,
      "latitude":null,
      "departureTime":null,
      "arrivalTime":null,
      "arrivalDayPlus":1,
      "stars":4,
      "localities":[
         {
            "id":41,
            "locality":{
               "id":34,
               "code":"ARM",
               "name":"Armenia"
            },
            "role":{
               "code":"stop",
               "name":"Escale"
            }
         },
         {
            "id":49,
            "locality":{
               "id":55,
               "code":"hossegor",
               "name":"Hossegor"
            },
            "role":{
               "code":"drop_off",
               "name":"Retour"
            }
         },
         {
            "id":50,
            "locality":{
               "id":55,
               "code":"hossegor",
               "name":"Hossegor"
            },
            "role":{
               "code":"localisation",
               "name":"Localisation"
            }
         }
      ]
   }
]

StackTrace:

I/flutter ( 2865): type 'List<dynamic>' is not a subtype of type 'Map<String, dynamic>'

[已更新] :返回ProductsResponse.fromJson(response)而不是响应 产品资料库:

import 'dart:async';
import 'package:day_experience/models/product/ProductResponse.dart';
import 'package:day_experience/networking/ApiProvider.dart';

class ProductRepository {
  ApiProvider _provider =  ApiProvider();

  Future<ProductsResponse> fetchProducts() async {

      final response = await _provider.getFromApi("products");
      // here line 11 where exception is thrown
      return ProductsResponse.fromJson(response);
  }


}

ProductsBloc:

import 'dart:async';

import 'package:day_experience/models/product/ProductResponse.dart';

import 'package:day_experience/networking/Response.dart';
import 'package:day_experience/repository/ProductRepository.dart';


class ProductBloc {
  ProductRepository _productRepository;
  StreamController _productListController;
  bool _isStreaming;
  StreamSink<Response<ProductsResponse>> get productListSink =>
      _productListController.sink;


  Stream<Response<ProductsResponse>> get productListStream =>
      _productListController.stream;

  ProductBloc() {
    _productListController = StreamController<Response<ProductsResponse>>();
    _productRepository = ProductRepository();
    _isStreaming = true;
    fetchProducts();

  }

  fetchProducts() async {
    productListSink.add(Response.loading('Getting Products.'));
    try {
     ProductsResponse productsResponse =
      await _productRepository.fetchProducts();
     if (_isStreaming) productListSink.add(Response.completed(productsResponse));
    } catch (e) {
      if (_isStreaming) productListSink.add(Response.error(e.toString()));
      print(e);
    }
  }

  dispose() {
    _isStreaming = false;
    _productListController?.close();
  }
}

响应:

class Response<T> {
  Status status;
  T data;
  String message;

  Response.loading(this.message) : status = Status.LOADING;
  Response.completed(this.data) : status = Status.COMPLETED;
  Response.error(this.message) : status = Status.ERROR;

  @override
  String toString() {
    return "Status : $status \n Message : $message \n Data : $data";
  }
}

enum Status { LOADING, COMPLETED, ERROR }

ApiProvider:

import 'package:day_experience/networking/CustomException.dart';
import 'package:http/http.dart' as http;
import 'dart:io';
import 'dart:convert';
import 'dart:async';

class ApiProvider {

  final String _baseApiUrl = "URL_API/";

  Future<dynamic> getFromApi(String url) async {
    var responseJson;
    try {
      final response = await http.get(Uri.encodeFull(_baseApiUrl + url),headers:{"Accept":"application/json"} );
      print(response);
      responseJson = _response(response);
    } on SocketException {
      throw FetchDataException('No Internet connection');
    }
    return responseJson;
  }

  dynamic _response(http.Response response) {
    switch (response.statusCode) {
      case 200:
        var responseJson = json.decode(response.body);

        print(responseJson);
        return responseJson;
      case 400:
        throw BadRequestException(response.body.toString());
      case 401:

      case 403:
        throw UnauthorisedException(response.body.toString());
      case 500:

      default:
        throw FetchDataException(
            'Error occured while Communication with Server with StatusCode : ${response.statusCode}');
    }
  }
}

型号:产品响应

import 'ExternalSystem.dart';
import './InventoryType.dart';
import './Localities.dart';
import 'SubType.dart';
import 'Supplier.dart';
import 'Tenant.dart';
import './Translation.dart';
import './Type.dart';

class ProductsResponse {
    final int id;
    final Tenant tenant;
    final Type type;
    final SubType subType;
    final InventoryType inventoryType;
    final String externalReference;
    final ExternalSystem externalSystem;
    final bool active;
    final int durationDays;
    final int durationNights;
    final int durationHours;
    final int durationMinutes;
    final Supplier supplier;
    final String name;
    final List<Translation> translations;
    final String vatPercentage;
    final int arrivalDayPlus;
    final int stars;
    final List<Localities> localities;
    final String group;
    final String subGroup;
    final double longitude;
    final double latitude;
    final String departureTime;
    final String arrivalTime;

    ProductsResponse({this.id, this.tenant , this.type, this.subType, this.inventoryType, this.externalReference, this.externalSystem, this.active, this.durationDays, this.durationNights, this.durationHours, this.durationMinutes, this.supplier, this.name, this.translations, this.vatPercentage, this.arrivalDayPlus, this.stars, this.localities, this.group, this.subGroup, this.longitude, this.latitude, this.departureTime, this.arrivalTime});

    factory ProductsResponse.fromJson(Map<String, dynamic> json) {
        return ProductsResponse(
            id: json['id'],
            tenant: json['tenant'] != null ? Tenant.fromJson(json['tenant']) : null,
            type: json['type'] != null ? Type.fromJson(json['type']) : null,
            subType: json['subType'] != null ? SubType.fromJson(json['subType']) : null,
            inventoryType: json['inventoryType'] != null ? InventoryType.fromJson(json['inventoryType']) : null,
            externalReference: json['externalReference'],
            externalSystem: json['externalSystem'] != null ? ExternalSystem.fromJson(json['externalSystem']) : null,
            active: json['active']?json['active']:null,
            durationDays: json['durationDays']?json['durationDays']:null,
            durationNights: json['durationNights']?json['durationNights']:null,
            durationHours: json['durationHours']?json['durationHours']:null,
            durationMinutes: json['durationMinutes']?json['durationMinutes']:null,
            supplier: json['supplier'] != null ? Supplier.fromJson(json['supplier']) : null,
            name: json['name']?json['name']:null,
            translations: json['translations'] != null ? (json['translations'] as List).map((i) => Translation.fromJson(i)).toList() : null,
            vatPercentage: json['vatPercentage']?json['vatPercentage']:null,
            arrivalDayPlus: json['arrivalDayPlus']?json['arrivalDayPlus']:null,
            stars: json['stars']?json['stars']:null,
            localities: json['localities'] != null ? (json['localities'] as List).map((i) => Localities.fromJson(i)).toList() : null,
            group: json['group'] != null ? json['group'] : null,
            subGroup: json['subGroup'] != null ? json['subGroup'] : null,
            longitude: json['longitude'] != null ? json['longitude'] : null,
            latitude: json['latitude'] != null ? json['latitude'] : null,
            departureTime: json['departureTime'] != null ? json['departureTime'] : null,
            arrivalTime: json['arrivalTime'] != null ? json['arrivalTime'] : null,
        );
    }

    Map<String, dynamic> toJson() {
        final Map<String, dynamic> data = new Map<String, dynamic>();
        data['id'] = this.id;
        data['externalReference'] = this.externalReference;
        data['active'] = this.active;
        data['durationDays'] = this.durationDays;
        data['durationNights'] = this.durationNights;
        data['durationHours'] = this.durationHours;
        data['durationMinutes'] = this.durationMinutes;
        data['name'] = this.name;
        data['vatPercentage'] = this.vatPercentage;
        data['arrivalDayPlus'] = this.arrivalDayPlus;
        data['stars'] = this.stars;
        if (this.tenant != null) {
            data['tenant'] = this.tenant.toJson();
        }
       if (this.type != null) {
            data['type'] = this.type.toJson();
        }
        if (this.subType != null) {
            data['subType'] = this.subType.toJson();
        }
        if (this.inventoryType != null) {
            data['inventoryType'] = this.inventoryType.toJson();
        }
        if (this.externalSystem != null) {
            data['externalSystem'] = this.externalSystem.toJson();
        }
        if (this.supplier != null) {
            data['supplier'] = this.supplier.toJson();
        }
        if (this.translations != null) {
            data['translations'] = this.translations.map((v) => v.toJson()).toList();
        }
        if (this.localities != null) {
            data['localities'] = this.localities.map((v) => v.toJson()).toList();
        }
        if (this.group != null) {
            data['group'] = this.group;
        }
        if (this.subGroup != null) {
            data['subGroup'] = this.subGroup;
        }
        if (this.longitude != null) {
            data['longitude'] = this.longitude;
        }
        if (this.latitude != null) {
            data['latitude'] = this.latitude;
        }
        if (this.departureTime != null) {
            data['departureTime'] = this.departureTime;
        }
        if (this.arrivalTime != null) {
            data['arrivalTime'] = this.arrivalTime;
        }
      print(data);

        return data;
    }
}

[已更新] :已删除的ProductsRepository(只能通过bloc访问)
产品视图

import 'package:day_experience/blocs/ProductBloc.dart';
import 'package:day_experience/models/product/ProductResponse.dart';
import 'package:day_experience/networking/Response.dart';
import 'package:day_experience/repository/ProductRepository.dart';
import 'package:day_experience/view/widget/Loading.dart';
import 'package:day_experience/view/widget/ProductList.dart';
import 'package:flutter/material.dart';
import 'package:day_experience/view/widget/Error.dart';


class ProductView extends StatefulWidget {
 @override
 _ProductViewState createState() => _ProductViewState();
}

class _ProductViewState extends State<ProductView> {
 ProductBloc _bloc = new ProductBloc();

 @override
 void initState() {
   super.initState();
   _bloc.fetchProducts();
 }

 @override
 Widget build(BuildContext context) {
   return Scaffold(
     appBar: AppBar(
       elevation: 0.0,
       automaticallyImplyLeading: false,
       title: Text('Products',
           style: TextStyle(color: Colors.white, fontSize: 20)),
       backgroundColor: Color(0xFF333333),
     ),
     backgroundColor: Color(0xFF333333),
     body: RefreshIndicator(
       onRefresh: () => _bloc.fetchProducts(),
       child: StreamBuilder<Response<ProductsResponse>>(
         stream: _bloc.productListStream,
         builder: (context, snapshot) {
           if (snapshot.hasData) {
             switch (snapshot.data.status) {
               case Status.LOADING:
                 return Loading(loadingMessage: snapshot.data.message);
                 break;
               case Status.COMPLETED:
                 return ProductList(productList:snapshot.data.data);
                 break;
               case Status.ERROR:
                 return Error(
                   errorMessage: snapshot.data.message,
                   onRetryPressed: () => _bloc.fetchProducts(),
                 );
                 break;
             }
           }
           return Container();
         },
       ),
     ),
   );
 }


}


Tenant示例对其他示例的相同逻辑(Translation,Type,SubType ..)

class Tenant {
   final int id;
   final String code;
   final String name;

   Tenant({this.id, this.code, this.name});

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

   Map<String, dynamic> toJson() {
       final Map<String, dynamic> data = new Map<String, dynamic>();
       data['id'] = this.id;
       data['code'] = this.code;
       data['name'] = this.name;
       return data;
   }
}

1 个答案:

答案 0 :(得分:1)

您可以在下面复制粘贴运行完整代码
因为您的json字符串产生的是var products = [ { id: '1', currency: { id: 1, name: 'Dollar' }, price: '100', }, { id: '4', currency: { id: 2, name: 'Euro' }, price: '300', }, { id: '6', currency: { id: 1, name: 'Dollar' }, price: '50', }, ]; subtotals = {}; products.map(x => { if(x.currency !== null){ subtotals[x.currency.name] === undefined ? (subtotals[x.currency.name] = parseInt(x.price)) : (subtotals[x.currency.name] += parseInt(x.price)); } }); 而不是List<ProductResponse>
在您的代码中,您可以将ProductResponse直接返回为response.body并使用String进行解析

代码段

productsResponseFromJson

工作演示

enter image description here

完整代码

List<ProductsResponse> productsResponseFromJson(String str) =>
List<ProductsResponse>.from(
    json.decode(str).map((x) => ProductsResponse.fromJson(x)));

Future<List<ProductsResponse>> fetchProducts() async {
  ApiProvider _provider = ApiProvider();
  String response = await _provider.getFromApi("products");
  // here line 11 where exception is thrown
  return productsResponseFromJson(response);
  //return ProductsResponse.fromJson(response);
}

Future<String> getFromApi(String url) async {

String _response(http.Response response) {
    switch (response.statusCode) {
      case 200:
        print(response.body);
        //var responseJson = jsonDecode(response.body);

        //print(responseJson);
        return response.body;