动态Future下FutureBuilder的snapshot.hasData失效问题求解
问题分析与解决方案
问题根源
你的getUserInfo是异步函数,但没有任何返回值,默认会返回Future<void>(本质是Future<Null>)。而FutureBuilder的snapshot.hasData只有当snapshot.data不为null时才会返回true——因为void类型对应的data是null,所以这个判断永远不成立,最终一直走到最后的CircularProgressIndicator。
改成else能运行,是因为此时只要连接完成且无错误,就会执行后续UI逻辑,跳过了hasData的校验,但这种写法确实不规范。
正确实现方案
你需要让getUserInfo返回一个非null的有效值,这样snapshot.hasData才能正常触发。这里提供两种可选思路:
思路1:返回布尔值标识操作完成
修改getUserInfo的返回类型为Future<bool>,并在方法末尾返回true(表示操作成功完成):
late final Future<bool> myFuture; @override void initState() { myFuture = getUserInfo(); super.initState(); } Future<bool> getUserInfo() async { SharedPreferences preferences = await SharedPreferences.getInstance(); bool check = preferences.containsKey('$userID,imageURL'); if(check) { userName = preferences.getString('$userID,name') ?? "No name found"; userWeight = preferences.getString('$userID,weight') ?? "no weight found"; userHeight = preferences.getString('$userID,height') ?? "no Height found"; userGender = preferences.getString('$userID,genderType') ?? "no gender Type found"; birthDateInString = preferences.getString('$userID,dateOfBirth') ?? "no date found"; imageURL = preferences.getString('$userID,imageURL') ?? "no image found"; } else { await downloadValues(); } // 返回非null值,让hasData生效 return true; }
同时给FutureBuilder指定泛型类型,让类型更安全:
Widget userValues(){ return FutureBuilder<bool>( future: myFuture, builder: (BuildContext context, AsyncSnapshot<bool> snapshot){ if(snapshot.connectionState == ConnectionState.waiting){ return const CircularProgressIndicator(); } else if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasError) { return Center( child: Text( '${snapshot.error} occurred', style: const TextStyle(fontSize: 18), ), ); } // 此时hasData会为true,因为返回了非null的bool值 else if (snapshot.hasData){ return Column( children: [ Container(), Padding( padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), child: ListTile( title: Text( "$userName", textAlign: TextAlign.center, style: GoogleFonts.mouseMemoirs( fontSize: 30, color: Colors.black ), ), ), ), ], ); } } return const Center(child: CircularProgressIndicator()); }, ); }
思路2:返回用户信息实体类(更规范)
如果希望代码更面向对象,可以定义一个UserInfo类,让getUserInfo返回这个类的实例,避免依赖全局变量:
class UserInfo { final String name; final String weight; final String height; final String gender; final String birthDate; final String imageUrl; UserInfo({ required this.name, required this.weight, required this.height, required this.gender, required this.birthDate, required this.imageUrl, }); }
修改getUserInfo方法:
late final Future<UserInfo> myFuture; @override void initState() { myFuture = getUserInfo(); super.initState(); } Future<UserInfo> getUserInfo() async { SharedPreferences preferences = await SharedPreferences.getInstance(); bool check = preferences.containsKey('$userID,imageURL'); if(check) { return UserInfo( name: preferences.getString('$userID,name') ?? "No name found", weight: preferences.getString('$userID,weight') ?? "no weight found", height: preferences.getString('$userID,height') ?? "no Height found", gender: preferences.getString('$userID,genderType') ?? "no gender Type found", birthDate: preferences.getString('$userID,dateOfBirth') ?? "no date found", imageUrl: preferences.getString('$userID,imageURL') ?? "no image found", ); } else { await downloadValues(); // 下载完成后读取最新数据返回 return UserInfo( name: preferences.getString('$userID,name') ?? "No name found", weight: preferences.getString('$userID,weight') ?? "no weight found", height: preferences.getString('$userID,height') ?? "no Height found", gender: preferences.getString('$userID,genderType') ?? "no gender Type found", birthDate: preferences.getString('$userID,dateOfBirth') ?? "no date found", imageUrl: preferences.getString('$userID,imageURL') ?? "no image found", ); } }
对应的FutureBuilder:
Widget userValues(){ return FutureBuilder<UserInfo>( future: myFuture, builder: (BuildContext context, AsyncSnapshot<UserInfo> snapshot){ if(snapshot.connectionState == ConnectionState.waiting){ return const CircularProgressIndicator(); } else if (snapshot.connectionState == ConnectionState.done) { if (snapshot.hasError) { return Center( child: Text( '${snapshot.error} occurred', style: const TextStyle(fontSize: 18), ), ); } else if (snapshot.hasData){ return Column( children: [ Container(), Padding( padding: const EdgeInsets.fromLTRB(0, 0, 0, 0), child: ListTile( title: Text( snapshot.data!.name, textAlign: TextAlign.center, style: GoogleFonts.mouseMemoirs( fontSize: 30, color: Colors.black ), ), ), ), ], ); } } return const Center(child: CircularProgressIndicator()); }, ); }
总结
核心问题是异步方法没有返回非null值,导致hasData校验失败。只要让Future返回一个明确的非null结果,就能正常使用snapshot.hasData进行判断,代码也更规范易维护。
内容的提问来源于stack exchange,提问作者taha khamis
相关产品推荐
相关产品推荐

