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

Flutter Riverpod同步数据时加载状态不显示问题排查

问题根源

Riverpod的状态更新仅当存在活跃监听者(如页面通过ref.watch订阅Provider)时,才会触发UI重建。第一种场景中,点击配置页同步按钮时,产品管理页面尚未打开,listProductController没有被任何UI监听,因此syncProducts设置的loading状态无法被捕获;当进入产品页面时,ref.watch直接获取到同步完成后的最终状态,自然看不到加载过程。

解决方案

针对两种场景的差异,提供以下几种可行的修复方案:

方案1:新增同步状态标记字段

给控制器添加专门标记同步状态的字段,确保即使页面未打开时触发同步,后续进入页面也能感知到正在同步的状态。

步骤1:修改控制器代码

class ProductController extends StateNotifier<AsyncValue<List<Product>>> {
  final SupabaseClient _supabase;
  bool _isSyncing = false;

  // 对外暴露同步状态
  bool get isSyncing => _isSyncing;

  ProductController(this._supabase) : super(const AsyncLoading());

  Future<void> syncProducts() async {
    if (_isSyncing) return; // 防止重复触发同步
    _isSyncing = true;
    state = const AsyncLoading(); // 更新状态为加载中

    try {
      final response = await _supabase.from('products').select();
      final products = (response as List).map((json) => Product.fromJson(json)).toList();
      state = AsyncData(products);
    } catch (e) {
      state = AsyncError(e, StackTrace.current);
    } finally {
      _isSyncing = false; // 同步结束后重置标记
    }
  }
}

步骤2:修改产品页面监听逻辑

在产品页面同时监听控制器的state和isSyncing字段,优先根据isSyncing判断是否显示加载状态:

@override
Widget build(BuildContext context, WidgetRef ref) {
  final productState = ref.watch(listProductController);
  final isSyncing = ref.watch(listProductController.select((controller) => controller.isSyncing));

  // 如果正在同步,直接显示加载状态
  if (isSyncing) {
    return const Center(child: CircularProgressIndicator());
  }

  // 根据状态展示不同视图
  return productState.when(
    loading: () => const Center(child: CircularProgressIndicator()),
    data: (products) => ProductList(products: products),
    error: (error, stackTrace) => Center(child: Text('加载失败: $error')),
  );
}

方案2:让控制器保持存活(Keep Alive)

通过配置Provider的keepAlive属性,让控制器即使没有活跃监听者也不会被销毁,这样状态变化会被保留,后续进入页面时能直接获取到当前状态(包括加载中状态)。

修改Provider定义

final listProductController = StateNotifierProvider.autoDispose<ProductController, AsyncValue<List<Product>>>((ref) {
  final supabase = ref.watch(supabaseProvider);
  final controller = ProductController(supabase);
  ref.keepAlive(); // 让Provider在无监听时保持存活
  return controller;
});

注意:此方案会让控制器常驻内存,适合数据量较小、同步操作不频繁的场景,避免不必要的资源浪费。

员工模块适配

员工模块存在完全相同的问题,只需对listEmployedController和员工管理页面执行上述任一方案的修改即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 19:01:27