Flutter解析本地数据库JSON数据失败,报错‘An error has occurred!’
Flutter JSON解析失败排查与解决
问题场景
在Flutter项目中尝试从本地数据库接口获取数据并解析JSON:通过fetchInstitute方法请求接口,使用compute函数在独立isolate中调用parseInstitute方法将返回的JSON转换为Institute列表,但运行时界面显示“An error has occurred!”。
相关代码
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; Future<List<Institute>> fetchInstitute(http.Client client) async { final response = await client .get(Uri.parse('http://192.168.1.5/tkydatabase/fun/anem.php')); // Use the compute function to run parseInstitute in a separate isolate. return compute(parseInstitute, response.body); } List<Institute> parseInstitute(String responseBody) { final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>(); return parsed.map<Institute>((json) => Institute.fromJson(json)).toList(); } void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { const appTitle = 'Isolate Demo'; return const MaterialApp( title: appTitle, home: MyHomePage(title: appTitle), ); } } class MyHomePage extends StatelessWidget { const MyHomePage({super.key, required this.title}); final String title; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text(title), ), body: FutureBuilder<List<Institute>>( future: fetchInstitute(http.Client()), builder: (context, snapshot) { if (snapshot.hasError) { return const Center( child: Text('An error has occurred!'), ); } else if (snapshot.hasData) { return InstitutesList(institute: snapshot.data!); } else { return const Center( child: CircularProgressIndicator(), ); } }, ), ); } } class InstitutesList extends StatelessWidget { const InstitutesList({super.key, required this.institute}); final List<Institute> institute; @override Widget build(BuildContext context) { return GridView.builder( gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: 2, ), itemCount: institute.length, itemBuilder: (context, index) { return Text(institute[index].instituteName); }, ); } } // 假设的Institute模型类(需确认实际实现) class Institute { final String instituteName; Institute({required this.instituteName}); factory Institute.fromJson(Map<String, dynamic> json) { return Institute( instituteName: json['instituteName'], ); } }
排查与解决步骤
1. 先获取具体错误信息
当前错误提示过于通用,修改FutureBuilder的错误处理逻辑,打印具体错误内容:
if (snapshot.hasError) { return Center( child: Text('错误详情: ${snapshot.error}'), ); }
运行后即可看到具体报错(比如类型转换错误、字段不存在、JSON格式错误等),针对性解决。
2. 检查接口返回的JSON格式
常见问题:接口返回的不是直接的数组,而是包含数组的对象(例如{"code":200,"data":[{"institute_name":"xxx"},...]})。此时直接jsonDecode(responseBody).cast<Map<String, dynamic>>()会报错,需先取出数组部分:
List<Institute> parseInstitute(String responseBody) { final data = jsonDecode(responseBody)['data']; // 根据实际字段名调整 if (data == null) throw Exception('接口返回数据格式错误'); final parsed = data.cast<Map<String, dynamic>>(); return parsed.map<Institute>((json) => Institute.fromJson(json)).toList(); }
3. 校验Institute.fromJson实现
- 确保字段名与接口返回的JSON字段完全匹配(注意大小写、下划线/驼峰差异,例如接口返回
institute_name,模型里不能直接用instituteName,需手动映射):
factory Institute.fromJson(Map<String, dynamic> json) { return Institute( instituteName: json['institute_name'] ?? '', // 加默认值避免空值报错 ); }
- 处理空值:如果接口可能返回null字段,需添加默认值或非空判断,避免类型转换异常。
4. 检查网络请求合法性
- 确认设备与服务器处于同一局域网,
192.168.1.5可正常访问。 - 在
fetchInstitute中添加状态码校验,排除请求失败的情况:
Future<List<Institute>> fetchInstitute(http.Client client) async { final response = await client .get(Uri.parse('http://192.168.1.5/tkydatabase/fun/anem.php')); if (response.statusCode != 200) { throw Exception('请求失败,状态码: ${response.statusCode}'); } return compute(parseInstitute, response.body); }
5. 确认parseInstitute的函数类型
compute要求传入的函数是顶级函数或静态函数,当前代码中parseInstitute是顶级函数,符合要求;如果后续将其移入类中,需改为static方法。
内容的提问来源于stack exchange,提问作者Ayoub Benameur
相关产品推荐
相关产品推荐

