下面的代码接受用户输入,并在延迟1秒后以大写打印。
最小可复制代码:
class FooPage extends ConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final asyncValue = ref.watch(resultProvider);
print('loading: ${asyncValue.isLoading}');
return Scaffold(
body: Column(
children: [
TextField(onChanged: (s) => ref.read(queryProvider.notifier).state = s),
asyncValue.when(
data: Text.new,
error: (e, s) => Text('Error = $e'),
loading: () => Text('Loading...'),
),
],
),
);
}
}
final stringProvider = FutureProvider.family<String, String>((ref, query) async {
await Future.delayed(Duration(seconds: 1));
return query.toUpperCase();
});
final queryProvider = StateProvider<String>((ref) => '');
final resultProvider = FutureProvider<String>((ref) async {
final query = ref.watch(queryProvider);
return ref.watch(stringProvider(query).future);
});
运行代码后,
- 输入任意文本(如
a
(并等待输出(大写( - 输入另一个文本(比如
b
(,然后等待输出(大写( - 按下退格键(即删除字符
b
(,现在控制台将打印loading: true
,因此加载小部件只需几秒钟。这会导致用户体验非常差
此问题发生在最新的2.0.2
版本上。那么,我如何获得以前的值,以便在获取数据后能够一致地显示data
呢?
更新:
这就是我使用git:的方式
dependencies:
flutter:
sdk: flutter
flutter_riverpod:
git:
url: https://github.com/rrousselGit/riverpod/tree/master/packages/flutter_riverpod
ref: master
2.1.0版本包含额外的实用程序,用于在加载过程中获取以前的值/错误。
注意发布时,2.1.0当前尚未发布。如果你想使用它,你可以在你的pubspec中使用git依赖项来做:
# Using dependency_overrides is necessary due to flutter_riverpod depending on riverpod
dependency_overrides:
flutter_riverpod:
git:
url: https://github.com/rrousselGit/riverpod/
ref: master
path: packages/flutter_riverpod
riverpod:
git:
url: https://github.com/rrousselGit/riverpod/
ref: master
path: packages/riverpod
现在可以在加载状态下获得以前的值/错误。
假设您将FutureProvider
定义如下:
final counterProvider = StateProvider<int>((ref) => 0);
final futureProvider = FutureProvider<String>((ref) async {
await Future.delayed(Duration(seconds: 2));
return "Hello world ${ref.watch(counterProivder)}';
});
其中改变CCD_ 8使得CCD_。
有了这个片段,在2.1.0及更高版本中,如果futureProvider
重新加载,您仍然可以访问value
/error
:
AsyncValue<String> state = ref.watch(futureProvider);
if (state.isLoading) {
// during loading state, value/errors are still available
print(state.value);
print(state.error);
}
当然,与漂亮的when
语法相比,编写if (state.isLoading)
有点不方便。
默认情况下,如果提供程序因Ref.refresh
/Ref.invalidate
而重建,则when
语法已正确调用data
/error
,而不是loading
。
另一方面,如果Ref.watch
故意触发重建(因为您的提供者的参数已经更改(,则不会执行此操作
但是when
公开了用于更改这一点的标志。
更具体地说,你可以写:
AsyncValue<String> state = ref.watch(futureProvider);
state.when(
skipLoadingOnReload: true,
loading: () => print('loading'),
data: (value) => print('value: $value'),
error: (err, stack) => print('error: $err');
);
使用此代码,无论提供程序是通过Ref.watch
重建还是通过Ref.refresh
重建,when
方法都将跳过loading
,而是调用data
/error
。
在将我的riverpod升级到2.0.2之后,我现在可以看到这种行为了。主要原因是延迟了1秒。除非消除延迟,否则无法消除加载,因为每次查询发生更改时,都会调用未来的提供程序,我尝试过了,它运行良好。现在,对于您的主要问题,即如何访问提供者的先前状态,您可以使用ref.listen()
来完成此操作。下面是关于如何访问它的演示。
class FooPage extends ConsumerWidget {
const FooPage({super.key});
@override
Widget build(BuildContext context, WidgetRef ref) {
ref.listen<AsyncValue<String>>(resultProvider, (previous, next) {
//* you can access the previous state here
if (previous?.value != '' && previous?.value != null) {
ref.read(upperCaseQuery.notifier).state = previous?.value ?? '';
}
});
final resultValue = ref.watch(resultProvider);
final upperCased = ref.watch(upperCaseQuery.state).state;
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(onChanged: (s) {
ref.read(queryProvider.notifier).state = s;
}),
//previous value of the the provider
Text(upperCased),
resultValue.when(
data: (value) => Text(value),
error: (e, s) => Text('Error = $e'),
loading: () => const Text('Loading...'),
),
],
),
),
);
}
}
final queryProvider = StateProvider<String>((ref) => 'default');
final resultProvider = FutureProvider.autoDispose<String>((ref) async {
final query = ref.watch(queryProvider);
await Future.delayed(const Duration(seconds: 1));
final upperCased = query.toUpperCase();
return upperCased;
}, name: 'result provider');
final upperCaseQuery = StateProvider<String>((ref) => '', name: 'upper cased');
如果将await Future.delayed(const Duration(seconds: 1));
从stringProvider
移动到resultProvider
,一切都会如您所期望的那样工作。
此外,让我为您提供一个代码现代化的变体:
/// Instead `stringProvider`.
Future<String> stringConvert(String query) async {
await Future.delayed(const Duration(seconds: 1));
return query.toUpperCase();
}
final queryProvider = StateProvider<String>((ref) {
return '';
});
final resultProvider = FutureProvider<String>((ref) async {
final query = ref.watch(queryProvider);
return stringConvert(query);
});