Flutter解析后端JSON用户资料数据时获取Null值如何解决
问题根因
解析后全为Null是多个代码逻辑错误共同导致的,核心问题点如下:
_loadProfileModel返回值类型不匹配:方法声明返回Future<ProfileModel>,实际返回的是原始接口响应的Map类型数据,FutureBuilder拿到的snapshot.data根本不是解析后的模型实例,类型不匹配无法取值。- JSON解析层级错误:日志能打印出完整返回值但解析为null,通常是漏取接口返回的嵌套
data字段,或是后端返回的字段名和模型定义的字段名不一致(比如后端返回first_name、firstName,模型写firstname,json_serializable默认严格匹配键名,匹配不到就返回null)。 - 变量作用域错误:无论是旧版代码还是简化版代码,都在
setState方法内部声明了ProfileModel profileModel = ...局部变量,这个局部变量和State类的全局profileModel属性完全无关,全局实例始终是初始化的空模型,所有字段默认都是null。 - FutureBuilder状态逻辑写反:原代码中
ConnectionState.waiting(加载中状态)返回Error组件,其余状态返回加载指示器,逻辑完全颠倒,加载阶段直接抛错。 - 重复触发网络请求:直接把
_loadProfileModel()写在FutureBuilder的future参数中,每次页面重绘都会重新发起请求,无谓消耗性能还会导致状态错乱。
修复步骤
- 先确认接口返回结构:打印
/profile/getData接口的完整响应,确认个人资料字段是直接挂在响应根节点,还是在data嵌套字段下;如果后端字段名和模型字段名不一致,给对应字段加@JsonKey(name: "后端实际字段名")注解,例如:
@JsonKey(name: "firstName") String? firstname;
- 修正变量作用域:给全局
profileModel赋值时,不要在变量前加类型声明,避免创建同名局部变量覆盖全局属性。 - 修正Future返回值:
_loadProfileModel必须返回解析完成的ProfileModel实例,不能返回原始响应Map。 - 调整FutureBuilder状态判断逻辑,对应好加载、错误、成功三种状态的组件返回。
- 提前在
initState中缓存请求Future对象,不要直接在FutureBuilder内调用请求方法,避免build触发重复请求。 - 修改完模型类或JsonKey注解后,重新执行序列化生成命令:
flutter pub run build_runner build --delete-conflicting-outputs,确保生成的profile_model.g.dart是最新版本。
修复后可直接复用的代码
class _SettingsProfileState extends State<SettingsProfile> { final _formKey = GlobalKey<FormState>(); final networkHandler = NetworkHandler(); ProfileModel? profileModel; // 缓存future避免重复请求 late Future<ProfileModel> profileFuture; bool hasProfile = false; TextEditingController firstnameController = TextEditingController(); TextEditingController lastnameController = TextEditingController(); TextEditingController birthdayController = TextEditingController(); TextEditingController weightController = TextEditingController(); @override void initState() { super.initState(); checkProfile(); } void checkProfile() async { var response = await networkHandler.get('/profile/checkProfile'); hasProfile = response['status'] == true; if (hasProfile) { profileFuture = _loadProfileModel(); } setState(() {}); } Future<ProfileModel> _loadProfileModel() async { var response = await networkHandler.get("/profile/getData"); // 根据实际接口结构调整:如果字段嵌套在data下就取response["data"],否则直接传response final Map<String, dynamic> profileData = response["data"]; return ProfileModel.fromJson(profileData); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Mon profil')), body: hasProfile ? showProfile() : emptyProfile(), ); } Widget showProfile() { return FutureBuilder<ProfileModel>( future: profileFuture, builder: (BuildContext context, AsyncSnapshot<ProfileModel> snapshot) { // 加载状态 if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } // 请求错误 if (snapshot.hasError) { return Center(child: Text('加载失败:${snapshot.error}')); } // 加载成功 if (snapshot.hasData) { profileModel = snapshot.data; return Card( elevation: 5, margin: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), child: Container( padding: const EdgeInsets.all(8), child: Row( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: Container( padding: const EdgeInsets.only(bottom: 8), child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Padding( padding: const EdgeInsets.only(left: 8, right: 8), child: Text( profileModel?.firstname ?? '', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold), ), ), Padding( padding: const EdgeInsets.only(left: 8, right: 8), child: Text(profileModel?.lastname ?? ''), ), Padding( padding: const EdgeInsets.only(left: 8, right: 8), child: Text(profileModel?.weight ?? ''), ) ], ), )) ], ), ), ); } return const Center(child: Text('暂无数据')); }, ); } Widget emptyProfile() { // 原有未填写资料页逻辑 return const Center(child: Text('请先完善个人资料')); } }
内容的提问来源于stack exchange,提问作者flutterby
相关产品推荐
相关产品推荐

