Flutter Riverpod中StateNotifier搭配.select()过滤UI重构的实现问题
问题原因
两个核心问题导致你写的select逻辑没有生效:
- StateNotifierProvider对外暴露的直接是其管理的状态对象(也就是UserState实例),原有代码的select回调中多写了
.state访问,属于语法错误 - 你定义的UserState没有重写
==运算符与hashCode,Dart默认按内存地址对比对象,即便是两个属性完全相同的新实例,也会被判定为值发生了变化,触发不必要的重构
修正方案
第一步:改造UserState类,实现相等性对比
两种实现方式二选一即可:
方式1:手动重写==和hashCode
class UserState { final int id; final String name; const UserState(this.id, this.name); @override bool operator ==(Object other) => identical(this, other) || other is UserState && runtimeType == other.runtimeType && name == other.name; @override int get hashCode => name.hashCode; }
方式2:使用equatable包简化实现(更推荐,不易出错)
import 'package:equatable/equatable.dart'; class UserState extends Equatable { final int id; final String name; const UserState(this.id, this.name); // 仅将name加入相等性判断列表,id变化不会触发相等性判定变化 @override List<Object?> get props => [name]; }
注意:如果需要全属性对比,只要把id也加入props数组即可,select本身会做二次过滤,两种写法都可以正常工作。
第二步:修正select的使用写法
// 在Consumer组件内 final userName = watch(userNotifierProvider.select((value) => value.name));
此时只有当UserState的name属性发生变化时,才会触发当前Consumer组件的重构,id变化不会产生任何影响。
其他可选方案
如果你不想依赖select实现,也可以拆分状态Provider:
- 单独创建
userNameProvider用来持有name状态,userIdProvider持有id状态,两个Provider独立更新,互不影响 - 业务逻辑层更新时按需更新对应Provider即可,也能实现同样的效果
内容的提问来源于stack exchange,提问作者Fran Maric
相关产品推荐
相关产品推荐

