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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 14:42:08