使用Riverpod StateNotifier+family修饰符时状态值为空求助
问题:添加family修饰符后无法获取状态数据
不使用family修饰符时代码运行完全正常,但一旦添加该修饰符,读取状态数据就返回空值。确认从API保存状态时数据不为空(能打印出value.category),但读取时却为空。
State Notifier代码
class SingleRequestNotifier extends StateNotifier<Request> { SingleRequestNotifier({this.ref, this.lang, this.requestID}) : super(Request.initial()) { fetchSingleRequestData(lang: lang, requestID: requestID); } final Ref? ref; final String? lang; final String? requestID; final RequestServices dataService = GetIt.instance<RequestServices>(); Request get getSingleRequest { return state; } Future fetchSingleRequestData({String? lang, String? requestID}) async { try { await dataService .getSingleRequestList(lang: lang!, requestID: requestID!) .then((value) { state = value; print(value.category); // here value is printing ref!.read(isLoadingProvider.notifier).state = false; }); } catch (e) { rethrow; } } }
Provider代码
final singleRequestStateProvider = StateNotifierProvider.family<SingleRequestNotifier, Request, Map<String,dynamic>>( (ref, arg) { return SingleRequestNotifier( ref: ref, lang: arg['lang'], requestID: arg['requestID']); });
获取数据的代码
Map<String, dynamic> args = { 'lang': 'en-AE', 'requestID': widget.requestID }; Request _request = ref.watch(singleRequestStateProvider(args)); print(_request.category); // here value is empty
原因分析
核心问题出在family的参数相等性判断逻辑:
- 你使用普通
Map<String, dynamic>作为family的参数,但Dart中普通Map的相等性是基于对象引用的——即使两个Map的键值对完全一致,只要是不同的Map实例,就会被family判定为不同参数。 - 如果
args是在Widget的build方法内创建的,每次Widget重建都会生成新的Map对象,这会触发family创建全新的SingleRequestNotifier实例。而新实例刚初始化时状态是Request.initial(),此时API请求还未完成,所以读取到的就是空值。
解决方法
1. 复用参数对象
把args提取为Widget的成员变量,避免每次build都创建新Map:
// 在Widget类中定义 late final Map<String, dynamic> args; @override void initState() { super.initState(); args = { 'lang': 'en-AE', 'requestID': widget.requestID }; } // 在build中使用 Request _request = ref.watch(singleRequestStateProvider(args));
2. 使用不可变且正确实现相等性的类型
自定义参数类
创建包含必要字段的类,重写==和hashCode方法:
class RequestArgs { final String lang; final String requestID; RequestArgs({required this.lang, required this.requestID}); @override bool operator ==(Object other) => identical(this, other) || other is RequestArgs && runtimeType == other.runtimeType && lang == other.lang && requestID == other.requestID; @override int get hashCode => lang.hashCode ^ requestID.hashCode; }
更新Provider和调用代码:
// Provider final singleRequestStateProvider = StateNotifierProvider.family<SingleRequestNotifier, Request, RequestArgs>( (ref, arg) { return SingleRequestNotifier( ref: ref, lang: arg.lang, requestID: arg.requestID); }); // 获取数据 final args = RequestArgs(lang: 'en-AE', requestID: widget.requestID); Request _request = ref.watch(singleRequestStateProvider(args));
使用元组
借助tuple包中的Tuple2(默认实现了正确的相等性判断):
// Provider final singleRequestStateProvider = StateNotifierProvider.family<SingleRequestNotifier, Request, Tuple2<String, String>>( (ref, arg) { return SingleRequestNotifier( ref: ref, lang: arg.item1, requestID: arg.item2); }); // 获取数据 final args = Tuple2('en-AE', widget.requestID); Request _request = ref.watch(singleRequestStateProvider(args));
内容的提问来源于stack exchange,提问作者Nashaf
相关产品推荐
相关产品推荐

