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

如何在Riverpod中用select/selectAsync实现特定对象状态监听?

Riverpod 问题:仅监听特定对象属性变化的Provider不触发Widget重建

我已经查阅过相关文档,但始终无法实现需求。我的Flutter应用中有一个异步Riverpod Notifier,状态类型为Future<List<CustomObject>?>,代码如下:

@Riverpod(keepAlive: true)
class MyCustomObjects extends _$MyCustomObjects {

  @override
  Future<List<CustomObject>?> build() async {
    
    /// 一些异步代码,返回null或List<CustomObject> 
 
  }
}

该Notifier包含修改列表中特定CustomObject属性的方法,每个CustomObject都有唯一的int id属性。

我的需求是:创建第二个Provider,仅当MyCustomObjects中指定id的CustomObject(除id外的其他属性)发生变化时,才触发关联Widget的刷新/重建。

我尝试了两种实现方式,但均无效:

第一种实现

part 'single_custom_object_provider.g.dart';

/// Provider holding state of single CustomObject
@riverpod
Future<CustomObject?> singleCustomObject(Ref ref, int customObjectsId) async {
  return await ref.watch(
    myCustomObjectsProvider.select(
      (AsyncValue<List<CustomObject>?> value) => value
          .whenData((List<CustomObject>? value) =>
              value?.firstWhereOrNull((CustomObject customObject) {
                print('performing ID check');
                if (customObject.id == customObjectsId) {
                  print(customObject.propertyX);
                  return true;
                }
                return false;
              }))
          .value,
    ),
  );
}

现象

修改myCustomObjectsProvider中ID为12的CustomObject的propertyX值后,控制台会打印performing ID check和新的propertyX值,说明主Provider状态已更新,且singleCustomObjectProvider的ref.watch能识别到新值,但调用ref.watch(singleCustomObjectProvider(12))的ConsumerWidget/ConsumerStatefulWidget却从未重建。如果直接使用ref.watch(myCustomObjectsProvider),Widget重建正常,但这正是我想避免的——当前Widget仅需展示特定CustomObject的数据。

第二种实现

/// Provider holding state of single CustomObject
@riverpod
Future<CustomObject?> singleCustomObject(Ref ref, int customObjectsId) async {
  return await ref.watch(myCustomObjectsProvider.selectAsync(
        (objects) => objects?.firstWhere((customObject) {
                print('performing ID check');
                if (customObject.id == customObjectsId) {
                  print(customObject.propertyX);
                  return true;
                }
                return false;
    }),
  ));
}

现象

结果与第一种完全相同,Widget仍不重建。


解决方案

问题核心在于AsyncValue的状态传递逻辑以及对象相等性判断,以下是正确实现方式:

方案1:正确使用select返回AsyncValue

不要将AsyncValue转为Future返回,直接让Provider返回AsyncValue<CustomObject?>,确保状态更新能传递到Consumer:

part 'single_custom_object_provider.g.dart';

@riverpod
AsyncValue<CustomObject?> singleCustomObject(Ref ref, int customObjectsId) {
  return ref.watch(myCustomObjectsProvider.select((asyncList) {
    return asyncList.whenData((list) {
      return list?.firstWhereOrNull((obj) => obj.id == customObjectsId);
    });
  }));
}

方案2:selectAsync的正确用法

若坚持使用selectAsync,同样需直接返回AsyncValue类型:

part 'single_custom_object_provider.g.dart';

@riverpod
AsyncValue<CustomObject?> singleCustomObject(Ref ref, int customObjectsId) {
  return ref.watch(myCustomObjectsProvider.selectAsync((list) async {
    return list?.firstWhereOrNull((obj) => obj.id == customObjectsId);
  }));
}

关键注意事项

  1. 重写CustomObject的相等性判断:Riverpod依赖对象的==和hashCode识别属性变化,必须重写这两个方法:
class CustomObject {
  final int id;
  String propertyX;

  CustomObject({required this.id, required this.propertyX});

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is CustomObject &&
          runtimeType == other.runtimeType &&
          id == other.id &&
          propertyX == other.propertyX;

  @override
  int get hashCode => id.hashCode ^ propertyX.hashCode;
}
  1. 修改状态时创建新实例:在MyCustomObjects的修改方法中,不要直接修改现有对象属性,需创建新对象或新列表,确保状态引用变化:
void updatePropertyX(int id, String newPropertyX) {
  state = AsyncValue.data(state.value?.map((obj) {
    if (obj.id == id) {
      return CustomObject(id: obj.id, propertyX: newPropertyX);
    }
    return obj;
  }).toList());
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 17:10:20