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

监听列表导致FutureProvider无限触发的问题解决

解决Riverpod搭配Isar时FutureProvider无限通知导致Widget重复Build的问题

你的推测没错:每次databaseManager.spools返回的List<String>都是新实例,即使内容完全一致,Riverpod基于对象引用的相等性判断会认为值已变化,从而反复通知监听者,导致Widget重复Build。下面是两种可行的解决方案:

方案一:改用StreamProvider监听Isar实时查询流

Isar支持通过watch()方法将查询转为Stream,只有当数据库中数据真的发生变化时,才会发射新的列表。这种方式能从根源避免不必要的更新。

修改Provider代码:

@riverpod
Stream<List<String>> spools(SpoolsRef ref) async* {
  final databaseManager = await ref.watch(databaseManagerProvider.future);
  // 替换为你实际的Isar查询watch逻辑,fireImmediately确保初始数据能立即发射
  yield* databaseManager.isar.spools.watch(fireImmediately: true);
}

修改Consumer代码:

/// Get all spools and make [DropdownMenuEntries] from them.
List<DropdownMenuEntry> spoolsMenuEntries() {
  return ref.watch(spoolsProvider).when(
        data: (spools) => spools
            .map((spool) => DropdownMenuEntry(value: spool, label: spool))
            .toList(),
        loading: () => [
          const DropdownMenuEntry(
            value: defaultSpoolName,
            label: defaultSpoolName,
          )
        ],
        error: (_, __) => [
          const DropdownMenuEntry(
            value: defaultSpoolName,
            label: defaultSpoolName,
          )
        ],
      );
}

方案二:在FutureProvider中添加列表内容相等性判断

如果坚持使用FutureProvider,可以通过对比列表内容,仅当内容真的变化时才返回新值,避免无意义的更新。

修改Provider代码:

@riverpod
Future<List<String>> spools(SpoolsRef ref) async {
  final databaseManager = await ref.watch(databaseManagerProvider.future);
  final newSpools = await databaseManager.spools;

  // 对比当前缓存的列表与新列表的内容
  final currentState = ref.state;
  if (currentState is AsyncData<List<String>>) {
    final oldSpools = currentState.value;
    if (_listsContentEqual(oldSpools, newSpools)) {
      // 内容一致,返回旧列表引用,避免触发更新
      return oldSpools;
    }
  }

  return newSpools;
}

// 辅助函数:判断两个字符串列表内容是否完全一致
bool _listsContentEqual(List<String>? a, List<String>? b) {
  if (a == null && b == null) return true;
  if (a == null || b == null) return false;
  if (a.length != b.length) return false;
  for (int i = 0; i < a.length; i++) {
    if (a[i] != b[i]) return false;
  }
  return true;
}

这种方式下,即使查询返回新的列表实例,只要内容和之前一致,Provider会复用旧的列表引用,Riverpod会判定值未变化,不会通知监听者。

内容的提问来源于stack exchange,提问作者Microbob

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 09:07:29