FutureBuilder重复调用getContacts致FastContacts报错求助
解决FastContacts并发调用报错的问题
核心原因
FutureBuilder的future参数如果直接传入方法调用(比如getContacts()),每次Widget rebuild时都会重新执行这个方法,导致FastContacts.getAllContacts()被并发调用,触发「Bad state: getAllContacts is already in progress.」错误,最终返回空数组。
解决方案
1. 将Future的创建移到initState中
在State类的initState方法里提前初始化获取联系人的Future,确保整个Widget生命周期内只执行一次请求,从根源避免并发调用:
class ContactListScreen extends StatefulWidget { const ContactListScreen({super.key}); @override State<ContactListScreen> createState() => _ContactListScreenState(); } class _ContactListScreenState extends State<ContactListScreen> { late Future<List<Contact>> _contactsFuture; @override void initState() { super.initState(); // 仅在Widget初始化时执行一次联系人获取逻辑 _contactsFuture = _getContacts(); } Future<List<Contact>> _getContacts() async { // 先申请并检查通讯录权限 final PermissionStatus status = await Permission.contacts.request(); if (status != PermissionStatus.granted) { return []; } try { return await FastContacts.getAllContacts(); } on StateError catch (_) { // 兜底处理并发调用错误(理论上不会触发,做冗余保障) return []; } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('联系人列表')), body: FutureBuilder<List<Contact>>( // 使用提前初始化的Future,而非直接调用_getContacts() future: _contactsFuture, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center(child: CircularProgressIndicator()); } if (snapshot.hasError) { return const Center(child: Text('获取联系人失败')); } final contacts = snapshot.data ?? []; if (contacts.isEmpty) { return const Center(child: Text('暂无联系人')); } return ListView.builder( itemCount: contacts.length, itemBuilder: (context, index) { final contact = contacts[index]; return ListTile( title: Text(contact.displayName), subtitle: contact.phones.isNotEmpty ? Text(contact.phones.first.number) : null, ); }, ); }, ), ); } }
2. 可选优化:添加请求锁
如果担心特殊场景下仍会触发并发调用,可以给获取方法加一个状态锁,确保同一时间只有一个请求在执行:
bool _isFetching = false; Future<List<Contact>> _getContacts() async { if (_isFetching) return []; _isFetching = true; try { final PermissionStatus status = await Permission.contacts.request(); if (status != PermissionStatus.granted) { return []; } return await FastContacts.getAllContacts(); } on StateError catch (_) { return []; } finally { _isFetching = false; } }
关键说明
- 把Future初始化放在
initState里,彻底避免FutureBuilder重复触发请求。 - 必须处理权限逻辑,权限拒绝时返回空数组(可根据业务需求调整提示文案),只有系统确实无联系人时才会显示「暂无联系人」。
- 捕获
StateError做兜底,避免意外情况导致页面崩溃。
内容的提问来源于stack exchange,提问作者Rishabh
相关产品推荐
相关产品推荐

