Flutter异步函数竞态问题:Shared Preferences加载滞后致数据不准
你的核心问题是异步加载的本地数据赶不上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
相关产品推荐
相关产品推荐

