Flutter JSON解析问题:页面无数据展示求助
解决方案
1. 定义对应JSON结构的数据模型类
首先需要创建匹配JSON结构的Dart模型,确保能正确解析嵌套的数组与对象:
// 通用标签项模型(适配multiDeviceSupport、mainFeatures、benefits的子项) class FeatureItem { final String label; FeatureItem({required this.label}); factory FeatureItem.fromJson(Map<String, dynamic> json) { return FeatureItem( label: json['label'] ?? '', ); } } // App详情核心模型 class AppDetails { final String systemOverview; final List<FeatureItem> multiDeviceSupport; final List<FeatureItem> mainFeatures; final List<FeatureItem> benefits; AppDetails({ required this.systemOverview, required this.multiDeviceSupport, required this.mainFeatures, required this.benefits, }); factory AppDetails.fromJson(Map<String, dynamic> json) { return AppDetails( systemOverview: json['systemOverview'] ?? '', multiDeviceSupport: (json['multiDeviceSupport'] as List) .map((item) => FeatureItem.fromJson(item)) .toList(), mainFeatures: (json['mainFeatures'] as List) .map((item) => FeatureItem.fromJson(item)) .toList(), benefits: (json['benefits'] as List) .map((item) => FeatureItem.fromJson(item)) .toList(), ); } } // 顶层App信息模型 class AppInfo { final String name; final String category; final String subcategory; final String description; final List<AppDetails> appDetails; AppInfo({ required this.name, required this.category, required this.subcategory, required this.description, required this.appDetails, }); factory AppInfo.fromJson(Map<String, dynamic> json) { return AppInfo( name: json['name'] ?? '', category: json['category'] ?? '', subcategory: json['subcategory'] ?? '', description: json['description'] ?? '', appDetails: (json['appDetails'] as List) .map((item) => AppDetails.fromJson(item)) .toList(), ); } }
2. 修复TestScreen页面:添加数据加载与正确UI展示
原代码核心问题是无数据加载逻辑且UI布局错误,以下是完整修复后的代码:
import 'dart:convert'; import 'package:flutter/material.dart'; class TestScreen extends StatefulWidget { const TestScreen({Key? key}) : super(key: key); @override State<TestScreen> createState() => _TestScreenState(); } class _TestScreenState extends State<TestScreen> { AppInfo? appInfo; @override void initState() { super.initState(); _loadAndParseJson(); } // 加载并解析JSON数据(模拟本地字符串,实际可替换为网络/asset加载) void _loadAndParseJson() { final jsonString = ''' { "name": "TestingApp", "category": "Production", "subcategory": "Productivity", "imageUrl": "Testing-Banner.jpg", "logo": "PI.png", "description": "Testing is an application for easy & effective Inspection", "appDetails": [{ "systemOverview": "https:url.com", "multiDeviceSupport": [{ "label": "Multi-Device" }, { "label": "Multi-Lingual" }, { "label": "Multi-Database" } ], "mainFeatures": [{ "label": "Testing" }, { "label": "Ease" }, { "label": "Select failure " }, { "label": "Add comments & take evidence Pictures" }, { "label": "Send results to central system" }, { "label": "Search/view " } ], "benefits": [{ "label": "Easy & quick solution " }, { "label": "Go paperless " }, { "label": "Lower costs" }, { "label": "Improve quality/safety" }, { "label": "Configurable on hand-held devices and tablets" }, { "label": "Electronic notifications to corresponding personnel’s" } ] }] } '''; final jsonData = jsonDecode(jsonString); setState(() { appInfo = AppInfo.fromJson(jsonData); }); } // 通用模块展示组件:标题+列表项 Widget buildSection(String title, List<FeatureItem> items) { return Container( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.2), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 12), Column( children: items.map((item) { return Padding( padding: const EdgeInsets.symmetric(vertical: 4), child: Row( children: [ const Icon(Icons.check_circle, color: Colors.green, size: 18), const SizedBox(width: 8), Expanded(child: Text(item.label)), ], ), ); }).toList(), ), ], ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( elevation: 0, backgroundColor: Colors.grey[100], // 替换为你的kBackgroundColor title: const Text( 'Home', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), ), body: appInfo == null ? const Center(child: CircularProgressIndicator()) : SingleChildScrollView( padding: const EdgeInsets.symmetric(vertical: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ // 展示App基础信息 Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( appInfo!.name, style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold), ), const SizedBox(height: 8), Text('${appInfo!.category} > ${appInfo!.subcategory}'), const SizedBox(height: 12), Text(appInfo!.description), ], ), ), const SizedBox(height: 20), // 展示各个详情模块 if (appInfo!.appDetails.isNotEmpty) ...[ buildSection('Multi Device Support', appInfo!.appDetails[0].multiDeviceSupport), buildSection('Main Features', appInfo!.appDetails[0].mainFeatures), buildSection('Benefits', appInfo!.appDetails[0].benefits), // 展示系统概览 Container( margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(8), boxShadow: [ BoxShadow( color: Colors.grey.withOpacity(0.2), blurRadius: 4, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'System Overview', style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 12), Text(appInfo!.appDetails[0].systemOverview), ], ), ), ], ], ), ), ); } }
3. 关键问题修复点
- 缺失数据加载:原代码
details列表始终为空,新增_loadAndParseJson方法在页面初始化时解析JSON并更新状态。 - UI布局错误:Scaffold的body直接使用
Expanded无效,改为SingleChildScrollView包裹Column,确保内容可滚动且布局合法。 - 模型类缺失:没有对应JSON结构的模型导致无法解析嵌套数据,新增三个模型类实现JSON到Dart对象的转换。
- 展示逻辑错误:原代码直接打印对象无法展示列表内容,新增通用
buildSection组件,将每个模块的标题与列表项清晰展示。
4. 扩展建议
- 若JSON来自网络,可将
_loadAndParseJson中的字符串替换为http包的网络请求逻辑。 - 若JSON来自本地asset,需在
pubspec.yaml中配置asset路径,使用rootBundle.loadString加载。 - 可根据需求调整
buildSection的样式,比如修改容器颜色、图标、间距等。
内容的提问来源于stack exchange,提问作者talal habib
相关产品推荐
相关产品推荐

