我正在开发一个依赖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;
}
}
答案 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
工作演示
完整代码
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;