Flutter 中 setState 获取 Firestore 数据时如何在加载完成前显示进度指示器
实现方案
你当前的写法存在两个核心问题:
- 直接将异步请求方法
getQoutes()传入FutureBuilder的future参数,会在每次组件重绘时重复触发Firestore数据拉取,额外消耗读配额 - 没有处理数据返回前的空值边界,布局嵌套存在滚动冲突问题
按照以下步骤修改即可实现加载态展示:
1. 调整状态变量与初始化逻辑
删除initState中直接调用的拉取方法,提前存储请求实例避免重复执行:
// 用户数据,初始为null Map<String, dynamic>? userInfo; // 存储数据拉取请求实例 late Future<void> quotesLoadFuture; Future<void> getQoutes() async { final docSnap = await FirebaseFirestore.instance .collection('user') .doc(auth.currentUser!.uid) .get(); userInfo = docSnap.data() as Map<String, dynamic>; } @override void initState() { super.initState(); // 仅在页面初始化时执行一次拉取 quotesLoadFuture = getQoutes(); }
2. 重写build方法的状态判断逻辑
移除多余的滚动嵌套组件,通过FutureBuilder的连接状态判断渲染内容:
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Some Quotes'), backgroundColor: Colors.deepOrange, ), body: FutureBuilder<void>( future: quotesLoadFuture, builder: (context, snapshot) { // 未加载完成状态:居中显示进度指示器 if (snapshot.connectionState != ConnectionState.done) { return const Center(child: CircularProgressIndicator()); } // 加载错误状态:显示错误提示 if (snapshot.hasError) { return Center(child: Text('加载失败: ${snapshot.error}')); } // 加载完成:渲染数据列表 final quotesList = userInfo?['quotes'] as List? ?? []; return Column( crossAxisAlignment: CrossAxisAlignment.center, children: [ Expanded( child: ListView.builder( // 不写死长度,取数据实际长度避免越界 itemCount: quotesList.length, itemBuilder: (context, index) { return Card_fun(quotesList[index]); }, ), ) ], ); }, ), floatingActionButton: FloatingActionButton( onPressed: () async { await popUpForm(); // 表单操作后如果需要刷新数据,只需重新赋值future并触发setState setState(() { quotesLoadFuture = getQoutes(); }); }, ), ); }
关键优化点
- 禁止在
FutureBuilder的future参数中直接调用异步方法,否则每次组件重绘(如键盘弹出、主题变化、setState触发)都会重新发起请求 - 移除了原代码中多余的
SingleChildScrollView和固定高度Container,ListView本身具备滚动能力,嵌套滚动组件会引发滑动冲突和布局溢出 - 增加了数组空值判断,不再写死
itemCount: 3,避免数据长度不足时触发数组越界报错 - 如果需要实现Firestore数据实时同步,可以将
FutureBuilder替换为StreamBuilder,直接监听文档快照流即可实现数据变更自动刷新,无需手动调用拉取方法
内容的提问来源于stack exchange,提问作者Gireesh
相关产品推荐
相关产品推荐

