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

动态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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 18:06:17