目前项目中的 riverpod 固定版本为:
riverpod: 2.2.0,最新版为:riverpod: 2.3.6,期间的更新日志可以点击查看。
坑点
升级到 2.3.4 的时候,第一次使用 autoDispose 类型的 provider 是没问题的,但是,后续继续使用,就会报错:
[, , 0]:split-0 error: Bad state: Trying to read an uninitialized value., trace:
#0 ProxyElementValueNotifier.value (package:riverpod/src/listenable.dart:26:7)
#1 ProviderElementProxy.read (package:riverpod/src/framework/proxy_provider_listenable.dart:117:28)
#2 ProviderContainer.read (package:riverpod/src/framework/container.dart:229:21)
#3 ConsumerStatefulElement.read (package:flutter_riverpod/src/consumer.dart:613:59)
#4 _FeelingDisplayWidgetState.initState (package:×××/src/ui/widget/×××_widget.dart:35:12)
#5 StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:5219:55)
#6 ComponentElement.mount (package:flutter/src/widgets/framework.dart:5062:5)
#7 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3971:16)
#8 Element.updateChild (package:flutter/src/widgets/framework.dart:3708:18)
#9 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5111:16)
#10 Element.rebuild (package:flutter/src/widgets/framework.dart:4805:7)
#11 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:5068:5)
#12 ComponentElement.mount (package:flutter/src/widgets/framework.dart:5062:5)
#13 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3971:16)
#14 MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6570:36)
#15 MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6582:32)
#16 Element.inflateWidget (package:flutter/src/widgets/framework.dart:3971:16)
#17 Element.updateChild (package:flutter/src/widgets/framework.dart:3708:18)
#18 ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5111:16)
#19 Element.rebuild (package:flutter/src/widgets/framework.dart:4805:7)
#20 ComponentElement._firstBuild (package:flutter/src/widgets/framework.dart:5068:5)
#21 ComponentElement.mount (package:flutter/src/widgets/framework.dart:5062:5)
#22 Element.infl
该 bug 并没有在 2.3.5 中解决,而是在 2.3.6 中解决:
所以,在使用 riverpod 的使用,需要绕过 2.3.4 和 2.3.5.
新特性
新增 StreamNotifier + StreamNotifierProvider
在以往使用 StreamProvider 来生成数据,容易使得该 provider 的数据生成方式固定了。
例子🌰:
final configProvider = StreamProvider<int>((ref) => Stream<int>.periodic(const Duration(seconds: ), (value) => value));
监听获取数据(要放到 build() 中,不能放在 initState())
ref.listen(numProvider, (previous, next) {
print('previous: ${(previous as AsyncValue<int>).value} next: ${(next as AsyncValue<int>).value}');
});
输出结果:
I previous: null next: 0
I previous: 0 next: 1
I previous: 1 next: 2
I previous: 2 next: 3
I previous: 3 next: 4
I previous: 4 next: 5
...
若使用 StreamNotifier + StreamNotifierProvider 就可以动态更改 stream 的返回值,下面来写个例子:
final numStreamProvider =
StreamNotifierProvider<NumStreamNotifier, int>(() => NumStreamNotifier());
class NumStreamNotifier extends StreamNotifier<int> {
@override
Stream<int> build() {
return Stream<int>.periodic(const Duration(seconds: 1), (value) => value).take(3);
}
refreshNumStream(){
Stream<int>.periodic(const Duration(seconds: 1), (value){
return value;
}).take(5).listen((event) {
state = AsyncValue.data(event + 100);
});
}
}
StreamNotifier 的写法和 StateNotifier 的写法还是有区别的:
StreamNotifier需要重载build()初始化值,而StateNotifier需要在构造方法中初始化。StreamNotifier的父类BuildlessStreamNotifier自带 ref,而StateNotifier需要用到 ref 的话,需要自己传进去。
我们五秒后调用下 refreshNumStream():
Future.delayed(const Duration(seconds: 5), (){
ref.read(numStreamProvider.notifier).refreshNumStream();
});
监听输出:
ref.listen(numStreamProvider, (previous, next) {
print('previous: ${(previous as AsyncValue<int>).value} next: ${(next as AsyncValue<int>).value}');
});
日志:
I previous: null next: 0
I previous: 0 next: 1
I previous: 1 next: 2
I previous: 2 next: 100
I previous: 100 next: 101
I previous: 101 next: 102
I previous: 102 next: 103
I previous: 103 next: 104
写法更新
1
数据源:
final numProvider = StreamProvider<int>((ref) => Stream<int>.periodic(const Duration(seconds: 1), (value) => value));
废弃写法:
ref.watch(numProvider.stream).listen((event) { });
推荐写法:
ref.listen(numProvider, (previous, next) {});
2
废弃写法:
final numProvider = StreamProvider<int>((ref) => Stream<int>.periodic(const Duration(seconds: 1), (value) => value));
final value = StreamProvider((ref) {
return ref.watch(numProvider.stream).map((e) => e.toString());
});
推荐写法:
final numProvider = FutureProvider((ref) => Future(() => 1));
ref.listen(numProvider, (previous, next) {});
总结来说,2.3.x 针对了 StreamProvider 进行了优化,提供新的构建方式和新的写法。