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

Flutter/Dart:如何将JSON数据按格式存入并展示在List Tile中?

解决Flutter中JSON数据在单个ListTile展示及优化存储问题

问题根源

现有实现将每个事件的四个字段拆分为独立字符串存入缓存,导致读取后每个字段对应一个ListTile,丢失了字段与事件的关联关系,同时存储方案效率低下。


1. 重构存储逻辑,保留事件完整结构

将整个事件列表序列化为JSON字符串存储,确保每个事件的字段关联关系不丢失。

修改获取并存储的代码:

onTap: () async {
  var newMessage = await ReadCache.getString(key: 'cache1');

  var response = await http.get(
    Uri.parse('http://192.168.1.8:8080/HongLeong/MENU_REQUEST.do?_dc=1658076505340&reset_context=1&table_id=25510&id_MenuAction=3&path=%3Cp%20class%3D%22x-window-header-path%22%3ELoss%20Event%3C%2Fp%3E&ViewType=MENU_REQUEST&gui_open_popup=1&id_Window=17&activeWindowId=mw_17&noOrigUserDate=true&LocalDate=20220718&LocalTime=00482500&TimeZone=Asia%2FShanghai&UserDate=0&UserTime=0&server_name=OPRISK_DATACOLLECTOR&key_id_list=&cell_context_id=0&id_Desktop=100252&operation_key=1000184&operation_sub_num=-1&is_json=1&is_popup=0&is_search_window=0&ccsfw_conf_by_user=0&is_batch=0&previousToken=1658069547560&historyToken=1658076505339&historyUrl=1'),
    headers: {HttpHeaders.cookieHeader: newMessage},
  );

  LossEventResponseModel lossEventResponseModel =
      LossEventResponseModel.fromJson(jsonDecode(response.body));

  final listNode = lossEventResponseModel.response.genericListAnswer.listNode;

  List<Map<String, dynamic>> incidentList = [
    for (final json in listNode.map((x) => x.toJson()))
      {
        'Code': json['field'][0]['field_value'],
        'Description': json['field'][1]['field_value'],
        'Organisation Unit': json['field'][46]['field_value'],
        'Date Reported': json['field'][18]['field_value'],
      }
  ];

  // 将整个事件列表序列化为JSON字符串存储
  String incidentListJson = jsonEncode(incidentList);
  await WriteCache.setString(key: 'cache4', value: incidentListJson);

  Navigator.of(context).push(
      MaterialPageRoute(builder: (context) => LossEvent()));
}

2. 修改读取与展示逻辑,单个ListTile展示完整事件

读取缓存的JSON字符串并解析为事件列表,每个事件对应一个ListTile,在Tile内组合展示所有字段。

修改读取展示的代码:

body: Column(
  children: [
    Expanded(
      child: FutureBuilder(
        future: ReadCache.getString(key: 'cache4'),
        builder: (context, snapshot) {
          if (snapshot.hasData && snapshot.data!.isNotEmpty) {
            // 解析JSON为事件列表
            List<Map<String, dynamic>> incidentList =
                List<Map<String, dynamic>>.from(jsonDecode(snapshot.data!));
            
            return ListView.builder(
                itemCount: incidentList.length,
                itemBuilder: (context, index) {
                  final incident = incidentList[index];
                  return Padding(
                    padding: const EdgeInsets.all(5.0),
                    child: ListTile(
                      tileColor: Colors.blueGrey[200],
                      shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(10)),
                      // Title显示事件编码,Subtitle组合展示其他字段
                      title: Text(
                        incident['Code'] ?? 'No Code',
                        style: GoogleFonts.poppins(
                          textStyle: const TextStyle(
                            fontWeight: FontWeight.bold,
                            fontSize: 20,
                          ),
                        ),
                      ),
                      subtitle: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                            'Description: ${incident['Description'] ?? 'No Description'}',
                            style: GoogleFonts.poppins(fontSize: 16),
                          ),
                          Text(
                            'Organisation Unit: ${incident['Organisation Unit'] ?? 'No Unit'}',
                            style: GoogleFonts.poppins(fontSize: 16),
                          ),
                          Text(
                            'Date Reported: ${incident['Date Reported'] ?? 'No Date'}',
                            style: GoogleFonts.poppins(fontSize: 16),
                          ),
                        ],
                      ),
                    ),
                  );
                });
          } else {
            return const Text("No Data");
          }
        },
      ),
    ),
  ],
),

3. 进阶优化:使用自定义模型类(类型安全)

为避免Map带来的类型不安全问题,创建Incident模型类:

class Incident {
  final String? code;
  final String? description;
  final String? organisationUnit;
  final String? dateReported;

  Incident({
    this.code,
    this.description,
    this.organisationUnit,
    this.dateReported,
  });

  // 从Map转换为Incident实例
  factory Incident.fromMap(Map<String, dynamic> map) {
    return Incident(
      code: map['Code'],
      description: map['Description'],
      organisationUnit: map['Organisation Unit'],
      dateReported: map['Date Reported'],
    );
  }

  // 转换为Map用于序列化
  Map<String, dynamic> toMap() {
    return {
      'Code': code,
      'Description': description,
      'Organisation Unit': organisationUnit,
      'Date Reported': dateReported,
    };
  }
}

适配模型类的存储与读取逻辑

  • 存储时:将incidentList转换为Incident实例列表再序列化
    List<Incident> incidents = incidentList.map((map) => Incident.fromMap(map)).toList();
    String incidentListJson = jsonEncode(incidents.map((e) => e.toMap()).toList());
    await WriteCache.setString(key: 'cache4', value: incidentListJson);
    
  • 读取时:解析后转换为Incident实例列表
    List<Incident> incidents = List<Map<String, dynamic>>.from(jsonDecode(snapshot.data!))
        .map((map) => Incident.fromMap(map))
        .toList();
    

之后即可通过incident.code、incident.description等方式安全访问字段。

内容的提问来源于stack exchange,提问作者JoshuaaMarkk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 10:27:13