简介
Riverpod是一款flutter端用于管理状态器,类似于Provider库,都是同一个作者所写,在Provider的基础上提供了更强大的能力,作为一款现代的状态管理器,我认为应该具有的基本能力
- 状态的生命周期管理,提供状态的创建、缓存、销毁等能力
- 数据的共享能力
- 响应式,数据驱动UI变化
带着这三个能力去看Riverpod是怎么实现的
简单使用例子
scala
void main() {
runApp(ProviderScope( //需要有一个ProviderScope,这个scope是状态的容器
child: MyApp(),
));
}
//实现riverpod自带的ConsumerStatefulWidget
class MyHomePage extends ConsumerStatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
ConsumerState<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends ConsumerState<MyHomePage> {
@override
Widget build(BuildContext context) {
ref.watch(fileNotifierProvider);
return Container();
}
}
ProviderScope是什么?
在Riverpod里面,ProviderScope是一个产生ProviderContainer的组件,ProviderContainer是用于管理provider和状态的容器,provider本身是不管理状态的。这种管理的方式是一种中心化的管理思想,因此可以在ProviderContainer里面去获取到所有provider和与之对应的状态。
scala
class ProviderScope extends StatefulWidget {}
class ProviderScopeState extends State<ProviderScope> {
Widget build(BuildContext context) {
....
return UncontrolledProviderScope(
container: container,
child: widget.child,
);
}
}
class UncontrolledProviderScope extends InheritedWidget {
/// The [ProviderContainer] exposed to the widget tree.
final ProviderContainer container;
}
class ProviderContainer implements Node {
//ProviderContainer里面有一个map,保存provider和statereader状态读取器的映射
final Map<ProviderBase<Object?>, _StateReader> _stateReaders;
}
ProviderScope build出UncontrolledProviderScope这样一个InheritedWidget,在flutter的子组件是有办法通过context.getElementForInheritedWidgetOfExactType
去拿父组件的InheritedWidget,进一步拿到ProviderContainer
WidgetRef
使用Riverpod一般来说要使用提供的ConsumerStatefulWidget
和ConsumerWidget
,这两个组件用于提供WidgetRef,这个类似于Provider里面context的功能,是widget组件和状态的桥梁,用于获取provider的数据和一些操作
csharp
abstract class WidgetRef {
T watch<T>(ProviderListenable<T> provider);
bool exists(ProviderBase<Object?> provider);
T read<T>(ProviderListenable<T> provider);
@useResult
State refresh<State>(Refreshable<State> provider);
void invalidate(ProviderOrFamily provider);
}
WidgetRef是怎么来的?
scala
abstract class ConsumerWidget extends ConsumerStatefulWidget {
_ConsumerState createState() => _ConsumerState();
}
class _ConsumerState extends ConsumerState<ConsumerWidget> {
@override
Widget build(BuildContext context) {
return widget.build(context, ref); //调用widget的build方法
}
}
//-----------------------------------//
abstract class ConsumerStatefulWidget extends StatefulWidget {
const ConsumerStatefulWidget({super.key});
@override
// ignore: no_logic_in_create_state
ConsumerState createState();
@override
ConsumerStatefulElement createElement() {
return ConsumerStatefulElement(this);
}
}
abstract class ConsumerState<T extends ConsumerStatefulWidget>
extends State<T> {
//这里说明了ref其实就是context,这两个是同一个东西
late final WidgetRef ref = context as WidgetRef;
}
// ConsumerStatefulElement继承了StatefulElement并实现了WidgetRef接口
class ConsumerStatefulElement extends StatefulElement implements WidgetRef {}
read流程
csharp
ref.read(fileNotifierProvider)
//ConsumerStatefulElement.read
T read<T>(ProviderListenable<T> provider) {
_assertNotDisposed();
return ProviderScope.containerOf(this, listen: false).read(provider);
}
//ProviderContainer.containerOf
static ProviderContainer containerOf(
BuildContext context, {
bool listen = true,
}) {
UncontrolledProviderScope? scope;
if (listen) {
scope = context //
.dependOnInheritedWidgetOfExactType<UncontrolledProviderScope>();
} else {
scope = context
.getElementForInheritedWidgetOfExactType<UncontrolledProviderScope>()
?.widget as UncontrolledProviderScope?;
}
return scope.container;
}
kotlin
//ProviderContainer.read
Result read<Result>(
ProviderListenable<Result> provider,
) {
return provider.read(this);
}
//ProviderBase.read
//ProviderBase是Provider的基础类
@override
StateT read(Node node) {
//在ProviderContainer里面去注册当前的Provider和与之对应的StateReader,并创建一个ProviderElement实例
final element = node.readProviderElement(this);
//触发provider的build方法,并把build出来的state状态保存在element中
element.flush();
//销毁流程
element.mayNeedDispose();
//获取状态
return element.requireState;
}
//ProviderContainer.readProviderElement
@override
ProviderElementBase<State> readProviderElement<State>(
ProviderBase<State> provider,
) {
//去创建一个StateReader状态读取器,StateReader主要是去处理Provider在scope下可以overrider的
//情况,一般使用比较少
final reader = _putIfAbsent(provider);
//在StateReader里面创建与之对应的ProviderElementBase
return reader.getElement() as ProviderElementBase<State>;
}
ProviderElementBase类是处理状态state和依赖的核心类,用于保存状态,以及依赖关系,状态变化导致的依赖通知等功能
dart
//ProviderElementBase类
abstract class ProviderElementBase<StateT> implements Ref<StateT>, Node {
@override
ProviderContainer get container => _container;
late final ProviderContainer _container;
//我依赖谁
var _dependencies = HashMap<ProviderElementBase<Object?>, Object>();
HashMap<ProviderElementBase<Object?>, Object>? _previousDependencies;
List<ProviderSubscription>? _subscriptions;
//谁依赖我
List<ProviderSubscription>? _dependents;
/// Whether the element was disposed or not
@internal
bool get mounted => _mounted;
bool _mounted = false;
/* STATE */
Result<StateT>? _state;
@internal
void setState(StateT newState) {
final previousResult = getState();
final result = _state = ResultData(newState);
if (_didBuild) {
_notifyListeners(result, previousResult);
}
}
@internal
Result<StateT>? getState() => _state;
}
状态的创建
scss
//ProviderElementBase类
@internal
void flush() {
_maybeRebuildDependencies();
if (_mustRecomputeState) {
_mustRecomputeState = false;
_performBuild();
}
}
void _performBuild() {
final previousDependencies = _previousDependencies = _dependencies;
_dependencies = HashMap();
final previousStateResult = _state;
//由ProviderElementBase子类去实现,一般是调用provider.build方法
buildState();
//这里创建出来的状态和之前的状态进行比较,比的是不是同一个对象,如果是同一个对象的话,那么不更新
//这也是为什么riverpod的更新状态必须要调用state=xxx,这个方法。
//如果之前的state是一个对象的话,一般来说用调用copyWith更改里面一个字段,生成新对象再赋值
if (!identical(_state, previousStateResult)) {
_notifyListeners(_state!, previousStateResult);
}
}
scala
class FileNotifier extends _$FileNotifier {
@override
FileModel build() {
print("TAGTAG 重建了");
return FileModel(fileName: "22");
}
void updateState(FileModel f) {
state = f.copyWith(fileName:"yll");
}
}
read流程总结
暂时无法在飞书文档外展示此内容

watch流程
watch流程主要是处理组件的依赖关系,当数据更新之后,通知组件需要重建
dart
ref.watch(fileNotifierProvider);
//ConsumerStatefulElement.watch
Res watch<Res>(ProviderListenable<Res> target) {
//判断是否已经依赖了provider,避免重复依赖
return _dependencies.putIfAbsent(target, () {
final oldDependency = _oldDependencies?.remove(target);
if (oldDependency != null) {
return oldDependency;
}
return _container.listen<Res>(
target,
(_, __) => markNeedsBuild(), //数据更新之后的回调,通知widget rebuild
);
}).read() as Res;
}
//ProviderBase.addListener
ProviderSubscription<StateT> addListener(
Node node,
void Function(StateT? previous, StateT next) listener, {
required void Function(Object error, StackTrace stackTrace)? onError,
required void Function()? onDependencyMayHaveChanged,
required bool fireImmediately,
}) {
//和read流程一样,构建Container和provider之间的关系
final element = node.readProviderElement(this);
element.flush(); //生成state
//构建一个监听 _ProviderStateSubscription
return _ProviderStateSubscription<StateT>(
node,
listenedElement: element,
listener: (prev, next) => listener(prev as StateT?, next as StateT),
onError: onError,
);
}
//_ProviderStateSubscription构造函数
_ProviderStateSubscription(
super.source, {
required this.listenedElement,
required this.listener,
required this.onError,
}) {
final dependents = listenedElement._dependents ??= [];
dependents.add(this);
}
状态更新
scss
void setState(StateT newState) {
final previousResult = getState();
final result = _state = ResultData(newState);
if (_didBuild) {
_notifyListeners(result, previousResult);
}
}
暂时无法在飞书文档外展示此内容
到此为止,状态管理器的基本功能都已经实现了,值的创建,响应式数据驱动,状态的共享都有了
状态的销毁
AutoDisposeProvider
如果是AutoDisposeProvider,那么在此provider没有依赖的时候就会销毁状态
使用@riverpod默认生成的provider就是AutoDisposeNotifierProvider


第一种销毁时机,read时会去检测一次,如果没有依赖,那么read完就销毁状态
typescript
//ProviderBase.read
@override
StateT read(Node node) {
final element = node.readProviderElement(this);
element.flush();
// In case `read` was called on a provider that has no listener
element.mayNeedDispose();
return element.requireState;
}
/// 空实现
@protected
@visibleForOverriding
void mayNeedDispose() {}
//AutoDisposeProviderElementMixin
@override
void mayNeedDispose() {
final links = _keepAliveLinks;
// maintainState和links都是设置保持状态的api,maintainState已设置为deprecated
if (!maintainState && !hasListeners && (links == null || links.isEmpty)) {
_container.scheduler.scheduleProviderDispose(this);
}
}
void scheduleProviderDispose(
AutoDisposeProviderElementMixin<Object?> element,
) {
_stateToDispose.add(element);
_scheduleTask();
}
void _disposeProvider(ProviderBase<Object?> provider) {
final reader = _getOrNull(provider);
if (reader == null) return;
reader._element?.dispose();
if (reader.isDynamicallyCreated) {
void removeStateReaderFrom(ProviderContainer container) {
//实际的销毁函数
if (container._stateReaders[provider] == reader) {
container._stateReaders.remove(provider);
}
container._children.forEach(removeStateReaderFrom);
}
removeStateReaderFrom(this);
} else {
reader._element = null;
}
}
因此如果没有调用ref.watch,仅仅是使用ref.read,其实每一次读出来的状态不是同一个对象,每一次都会重新build一次状态。
第二种销毁时机,在element树销毁的时候。
scala
//ConsumerState里面没有销毁逻辑
abstract class ConsumerState<T extends ConsumerStatefulWidget>
extends State<T> {
/// An object that allows widgets to interact with providers.
late final WidgetRef ref = context as WidgetRef;
}
///ConsumerStatefulElement
@override
void unmount() {
super.unmount();
for (final dependency in _dependencies.values) {
dependency.close();
}
for (var i = 0; i < _listeners.length; i++) {
_listeners[i].close();
}
final manualListeners = _manualListeners?.toList();
if (manualListeners != null) {
for (final listener in manualListeners) {
listener.close();
}
_manualListeners = null;
}
}
///ProviderElementBase._onRemoveListener
void _onRemoveListener() {
_onRemoveListeners?.forEach(runGuarded);
if (!hasListeners) {
_didCancelOnce = true;
_onCancelListeners?.forEach(runGuarded);
}
mayNeedDispose();
}
非AutoDisposeProvider
- 如果不是AutoDisposeProvider,那么默认状态是永久的存留,因为其没有实现mayNeedDispose,使用keepAlive参数可以生成非AutoDisposeProvider


这种情况如果想刷新provider的状态该怎么办?WidgetRef有两个api用于重新计算状态
csharp
/// Forces a provider to re-evaluate its state immediately,
///and return the created value.
///立即计算状态,并返回
@useResult
State refresh<State>(Refreshable<State> provider);
/// Invalidates the state of the provider, causing it to refresh.
///
/// As opposed to [refresh], the refresh is not immediate and is instead
/// delayed to the next read or next frame.
///
/// Calling [invalidate] multiple times will refresh the provider only
/// once.
///
/// Calling [invalidate] will cause the provider to be disposed immediately.
///
/// If used on a provider which is not initialized, this method will have no effect.
void invalidate(ProviderOrFamily provider);
Consumer与Selector
这两个类主要是为了减小状态的刷新范围
比如在一棵widget树下面,如果有一个小组件需要刷新,我们当然是希望小组件单个刷新,而不会引起整个页面的刷新,一般来说有两个方法
- 把组件抽离成一个单独的widget
- 用Consumer把小组件包起来,形成一个单独的刷新区域

Selector的作用是在一个大的State里面包含好几个字段,只有里面一个字段发生变化的时候state都会重置,引起状态更新,但是如果只需要里面某个特定字段发生变化的时候才引起更新,这时候就需要Selector
less
@freezed
class FileModel with _$FileModel {
const factory FileModel({
///文件名
@Default("") String fileName,
@Default(0) int num,
///
}) = _FileModel;
}
//使用
body: Container(
child: ProviderScope(
child: Consumer(
builder: (context, ref, _) {
var fileName = ref.
watch(fileNotifierProvider.select((FileModel model) => model.fileName));
return Text(fileName);
},
),
),
),
selector会比较前后两次的值,如果值不相等才会去更新
dart
//_ProviderSelector
void _selectOnChange({
required Input newState,
required Result<Output> lastSelectedValue,
required void Function(Object error, StackTrace stackTrace) onError,
required void Function(Output? prev, Output next) listener,
required void Function(Result<Output> newState) onChange,
}) {
final newSelectedValue = _select(Result.data(newState));
if (!lastSelectedValue.hasState ||
!newSelectedValue.hasState ||
lastSelectedValue.requireState != newSelectedValue.requireState) {
onChange(newSelectedValue);
newSelectedValue.map(
data: (data) {
listener(
// TODO test from error
lastSelectedValue.stateOrNull,
data.state,
);
},
error: (error) => onError(error.error, error.stackTrace),
);
}
}
Family Provider
可以简单理解为带参数的provider,本质上是为不同参数生成不同的provider,然后再生成不同的目标对象
使用注解生成family provider
typescript
@riverpod
List<FileModel> filteredFileList(FilteredFileListRef ref 这个类是生成的, String keyword) {
final files = ref.watch(fileNotifierProvider);
if (keyword.isEmpty) return files;
return files.where((f) => f.fileName.contains(keyword)).toList();
}
使用
csharp
ref.read(filteredFileListProvider(_keyword));
filteredFileListProvider是一个family
scala
class FilteredFileListFamily extends Family<List<FileModel>>
这个类带一个call函数,call函数是能使得对象带有函数功能的方法,其实和对象调用普通方法差不多
javascript
FilteredFileListProvider call(
String keyword,
) {
return FilteredFileListProvider(
keyword,
);
}
call方法返回真正的provider
python
FilteredFileListProvider(
String keyword,
) : this._internal(
(ref) => filteredFileList(
ref as FilteredFileListRef,
keyword,
),
from: filteredFileListProvider,
name: r'filteredFileListProvider',
debugGetCreateSourceHash:
const bool.fromEnvironment('dart.vm.product')
? null
: _$filteredFileListHash,
dependencies: FilteredFileListFamily. _dependencies,
allTransitiveDependencies:
FilteredFileListFamily. _allTransitiveDependencies,
keyword: keyword,
);
所以说原理也是比较简单的,之前使用的provider是生成全局的一个provider,不会变;family是每次生成新的provider
使用注解生成代码
现在的riverpod推荐使用注解生成代码的形式,免去了一些provider代码的流程。使用Riverpod一般来说需要自己写一个Notiier类,用于管理state创建以及state更新逻辑;还需要写一个state类,这个state类通常用freezed注解去生成,主要是生成了json格式化的代码和copyWith代码,用于快速新生成一个State对象。
推荐两个插件用于生成Notifier和State模板代码的插件


生成Notifier,只需要打出river

生成的代码

生成freezed类

生成part语句,对应freezedPart功能
dart
part 'test3.freezed.dart';