FutureBuilder正确使用指南及LateInitializationError报错解决方法
错误原因
你触发LateInitializationError的核心原因是:late修饰的变量必须在被访问前完成赋值,而getUserPrefs是异步方法,initState不会等待它的then回调执行完成就会触发首次build,此时_dataFuture还未被赋值,直接被FutureBuilder访问就会抛出异常。
修复方案
最稳妥的修改方式是将「获取用户偏好」和「请求接口数据」两个有依赖关系的异步操作合并为一个Future,直接在initState中完成_dataFuture的初始化,无需依赖异步回调赋值:
首先修改State的变量声明和初始化逻辑:
late Future<List<TransaksiResp>> _dataFuture; late TransaksiService transaksiService; @override void initState() { super.initState(); transaksiService = TransaksiService(); // 直接初始化_dataFuture,无需等待异步回调 _dataFuture = _loadTransaksiData(); } // 整合两个异步操作的私有方法 Future<List<TransaksiResp>> _loadTransaksiData() async { // 第一步:获取用户偏好数据 SharedPreferences prefs = await SharedPreferences.getInstance(); final user = prefs.getStringList("user"); // 增加空判断避免非法数据崩溃 if (user == null || user.length < 3) { throw Exception("用户信息获取异常"); } // 第二步:请求交易数据 return await transaksiService.getTransaksi(user[2]); }
接下来可以优化FutureBuilder的等待状态,优化用户体验:
@override Widget build(BuildContext context) { return Scaffold( body: Column( children: <Widget>[ Flexible( child: SizedBox( child: FutureBuilder<List<TransaksiResp>>( future: _dataFuture, builder: (BuildContext context, AsyncSnapshot snapshot) { if(snapshot.hasError) { print(snapshot.error); return const Center( child: Text("加载失败,请稍后重试"), ); } else if (snapshot.hasData){ List<TransaksiResp> response = snapshot.data as List<TransaksiResp>; return _buildListView(response); } else { // 替换空容器为加载提示 return const Center( child: CircularProgressIndicator(), ); } }, ), ), ) ], ), ); }
注意事项
- 你之前了解的「不要直接在FutureBuilder的future参数中调用异步方法」的规范是正确的,提前将Future存入变量可以避免widget重绘时重复发起请求
- 存在依赖关系的异步操作建议合并到同一个async方法中串行执行,返回统一的Future可以避免多个异步回调带来的时序问题
- 避免在无空判断的情况下直接用
!强制解包可空值,主动抛出明确的异常更方便后续排查问题
内容的提问来源于stack exchange,提问作者Ananda Pramono
相关产品推荐
相关产品推荐

