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

Flutter异步函数竞态问题:Shared Preferences加载滞后致数据不准

解决SharedPreferences加载滞后导致UI构建异常的问题

你的核心问题是异步加载的本地数据赶不上UI初始化构建,导致计算逻辑使用了未就绪的数据。以下是两种直接可行的解决方案:

方案一:合并双数据源的加载Future,用FutureBuilder统一等待

将API请求和SharedPreferences读取合并为一个异步任务,通过FutureBuilder等待所有数据就绪后再构建ListTile,确保计算completedPercent时有完整的本地数据。

class _CityListState extends State<CityList> {
  late Future<void> _loadAllData;
  List<City>? _cityDataFromApi;
  List<int>? _localMissionStatus;

  @override
  void initState() {
    super.initState();
    // 初始化时启动合并加载任务
    _loadAllData = _initDataSource();
  }

  Future<void> _initDataSource() async {
    // 并行加载双数据源,提升加载效率
    final apiTask = _fetchCityApiData();
    final localTask = Player.loadMissionStatus();

    // 等待两个任务都完成
    final results = await Future.wait([apiTask, localTask]);
    setState(() {
      _cityDataFromApi = results[0] as List<City>;
      _localMissionStatus = results[1] as List<int>;
    });
  }

  // 你的API请求方法
  Future<List<City>> _fetchCityApiData() {
    return YourApiService.getCityList();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FutureBuilder<void>(
        future: _loadAllData,
        builder: (context, snapshot) {
          if (snapshot.connectionState == ConnectionState.waiting) {
            // 加载中占位UI
            return const Center(child: CircularProgressIndicator());
          } else if (snapshot.hasError) {
            // 错误处理UI
            return Center(child: Text('加载失败:${snapshot.error}'));
          } else {
            // 双数据就绪,构建ListTile
            return ListView.builder(
              itemCount: _cityDataFromApi?.length ?? 0,
              itemBuilder: (context, index) {
                final city = _cityDataFromApi![index];
                // 此时_localMissionStatus已完全加载,计算结果准确
                final completedPercent = _calcCompletedPercent(city, _localMissionStatus!);
                return ListTile(
                  title: Text(city.name),
                  subtitle: Text('完成率:$completedPercent%'),
                );
              },
            );
          }
        },
      ),
    );
  }

  // 完成率计算逻辑
  int _calcCompletedPercent(City city, List<int> missionStatus) {
    // 你的计算实现
    return /* 最终计算值 */;
  }
}

方案二:用单独的FutureBuilder处理本地数据(嵌套场景)

如果API和本地数据的加载逻辑需要分离,也可以在API的FutureBuilder内部嵌套一个针对SharedPreferences的FutureBuilder,确保本地数据加载完成后再渲染ListTile:

// 仅展示核心build逻辑部分
@override
Widget build(BuildContext context) {
  return Scaffold(
    body: FutureBuilder<List<City>>(
      future: _fetchCityApiData(),
      builder: (context, apiSnapshot) {
        if (apiSnapshot.connectionState == ConnectionState.waiting) {
          return const Center(child: CircularProgressIndicator());
        } else if (apiSnapshot.hasError) {
          return Center(child: Text('API加载失败:${apiSnapshot.error}'));
        } else {
          final cityList = apiSnapshot.data!;
          // 嵌套FutureBuilder等待本地数据
          return FutureBuilder<List<int>>(
            future: Player.loadMissionStatus(),
            builder: (context, localSnapshot) {
              if (localSnapshot.connectionState == ConnectionState.waiting) {
                return const Center(child: CircularProgressIndicator());
              } else if (localSnapshot.hasError) {
                return Center(child: Text('本地数据加载失败:${localSnapshot.error}'));
              } else {
                final missionStatus = localSnapshot.data!;
                return ListView.builder(
                  itemCount: cityList.length,
                  itemBuilder: (context, index) {
                    final completedPercent = _calcCompletedPercent(cityList[index], missionStatus);
                    return ListTile(
                      title: Text(cityList[index].name),
                      subtitle: Text('完成率:$completedPercent%'),
                    );
                  },
                );
              }
            },
          );
        }
      },
    ),
  );
}

关键说明

  • 方案一的Future.wait会并行执行两个异步任务,比串行加载效率更高,适合初始化场景;
  • 避免直接在initState中使用await(initState不能是异步方法),通过FutureBuilder管理异步状态是Flutter的标准做法;
  • 如果后续本地数据可能动态更新,可结合ChangeNotifier或Provider实现状态监听,自动刷新UI。

内容的提问来源于stack exchange,提问作者Raptor

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 04:42:53