如何在Riverpod中用select/selectAsync实现特定对象状态监听?
我已经查阅过相关文档,但始终无法实现需求。我的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); })); }
关键注意事项
- 重写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; }
- 修改状态时创建新实例:在
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

