You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter从Django DRF取数遇类型错误:预期List<dynamic>实际为_JsonMap

问题描述

我在Flutter中编写了List类型转换代码,但运行应用时出现错误:Expected a value of type List<dynamic> but got one type _JsonMap。数据从Django REST Framework以JSON格式获取,后端服务能正常启动,但界面无法展示数据,仅在中心组件显示该错误信息。

我的代码如下:

// ignore_for_file: dead_code, prefer_const_constructors, prefer_const_literals_to_create_immutables, deprecated_member_use, avoid_print, non_constant_identifier_names

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


Future<List<Data>> fetchData() async {
  var url = Uri.parse('http://127.0.0.1:8000/api/?format=json');
  final response = await http.get(url);
  if (response.statusCode == 200) {
    // final jSon = "[${response.body}]";
    List jsonResponse = (json.decode(response.body) as List<dynamic>);
    return jsonResponse.map((data) => Data.fromJson(data)).toList();
  } else {
    throw Exception('Unexpected error occured!');
  }
}



class Data {
  final int ID;
  final String visitor_name;
  final String visit_date;
  final String time_in;
  final String time_out;
  final String department;
  final String visitee_name;
  final String address;
  final String phone;
  final String purpose_of_visit;

  Data({required this.ID, required this.visitor_name, required this.visit_date, required this.time_in, required this.time_out, required this.department, required this.visitee_name, required this.address, required this.phone, required this.purpose_of_visit});

  factory Data.fromJson(Map<String, dynamic> json) {
    return Data(
      ID: json['ID'],
      visitor_name: json['visitor_name'],
      visit_date: json['visit_date'],
      time_in: json['time_in'],
      time_out: json['time_out'],
      department: json['department'],
      visitee_name: json['visitee_name'],
      address: json['address'],
      phone: json['phone'],
      purpose_of_visit: json['purpose_of_visit'],

    );
  }
}



class ViewRecord extends StatefulWidget {
  const ViewRecord({super.key});

  @override
  State<ViewRecord> createState() => _ViewRecordState();
}

class _ViewRecordState extends State<ViewRecord> {

  @override
  Widget build(BuildContext context) {
    return FutureBuilder<List<Data>>(
      future: fetchData(),
      builder: (context, snapshot) {
        if (snapshot.hasData) {
          return SingleChildScrollView(
              scrollDirection: Axis.vertical,
              child: SingleChildScrollView(
                scrollDirection: Axis.horizontal,
                child: DataTable(
                  decoration: BoxDecoration(
                    
                  ),
                  border: TableBorder.all( style: BorderStyle.solid, width: 2, borderRadius: BorderRadius.circular(5)),
                  columnSpacing: 50,
                  dataRowHeight: 50,
                  dividerThickness: 2,
                  headingRowColor: MaterialStateProperty.resolveWith((states) => Colors.lightBlue[50]),
                  columns: const <DataColumn>[
                          DataColumn(label: Text('ID')),
                          DataColumn(label: Text('Visitor\'s Name')),
                          DataColumn(label: Text('Visit Date')),
                          DataColumn(label: Text('Time In')),
                          DataColumn(label: Text('Time Out')),
                          DataColumn(label: Text('Department')),
                          DataColumn(label: Text('Visitee')),
                          DataColumn(label: Text('Address')),
                          DataColumn(label: Text('Phone')),
                          DataColumn(label: Text('Purpose of Visit')),
                  ], 
                 rows: List.generate(
                  snapshot.data!.length, 
                  (index) {
                    var data =  snapshot.data![index];
                    return DataRow(cells: [
                          DataCell(
                            Text(data.ID.toString())
                          ),
                          DataCell(
                            Text(data.visitor_name.toString())
                          ),
                          DataCell(
                            Text(data.visit_date.toString())
                          ),
                          DataCell(
                            Text(data.time_in.toString())
                          ),
                          DataCell(
                            Text(data.time_out.toString())
                          ),
                          DataCell(
                            Text(data.department.toString())
                          ),
                          DataCell(
                            Text(data.visitee_name.toString())
                          ),
                          DataCell(
                            Text(data.address.toString())
                          ),
                          DataCell(
                            Text(data.phone.toString())
                          ),
                          DataCell(
                            Text(data.purpose_of_visit.toString())
                          ),
                       ]
                    );
                  }
                  
                  ).toList(),
                  
                 ),
              ),
            );
          


        }
        else if (snapshot.hasError) {
          return Text(snapshot.error.toString());
        }
        return const CircularProgressIndicator();

      },
      
      );
  }
}
解决方案

这个错误的核心原因是后端返回的JSON并非直接的数组(List),而是一个包含数组的对象(_JsonMap)。Django REST Framework默认的列表接口响应通常会包含results字段(比如分页结构),或者你的API返回的顶层结构本身就是对象而非数组。

具体修复步骤:

  1. 确认后端JSON结构
    先打印后端返回的原始数据,确认结构:

    print(response.body);
    

    常见的Django REST Framework列表响应格式如下:

    {
      "count": 2,
      "next": null,
      "previous": null,
      "results": [
        {"ID": 1, "visitor_name": "示例访客1", ...},
        {"ID": 2, "visitor_name": "示例访客2", ...}
      ]
    }
    
  2. 修改fetchData的解析逻辑
    根据实际的JSON结构,先解析为Map,再取出对应的列表字段。比如针对上面的默认格式,修改代码如下:

    Future<List<Data>> fetchData() async {
      var url = Uri.parse('http://127.0.0.1:8000/api/?format=json');
      final response = await http.get(url);
      if (response.statusCode == 200) {
        // 先将响应解析为Map
        Map<String, dynamic> responseMap = json.decode(response.body) as Map<String, dynamic>;
        // 取出results对应的数组
        List jsonResponse = responseMap['results'] as List<dynamic>;
        return jsonResponse.map((data) => Data.fromJson(data)).toList();
      } else {
        throw Exception('Unexpected error occured!');
      }
    }
    

    如果你的API返回的是自定义结构,只需将responseMap['results']中的results替换为实际的列表字段名即可。

  3. 排查API地址
    若确认后端返回的就是单个对象,那可能是请求了详情接口而非列表接口,需要检查API地址是否正确。

内容的提问来源于stack exchange,提问作者Jonathan Kwabena Ewenam Kudiab

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.27 15:29:51