Flutter Riverpod如何设置加载状态最短显示时长?
解决Riverpod加载状态闪瞬而过问题:设置最小加载时长
针对你用Riverpod开发时加载屏一闪而过导致的体验割裂问题,可以通过封装自定义组件强制加载状态维持至少指定时长,以下是具体实现方案:
方案1:封装MinDurationLoader组件
这个组件会监听AsyncValue状态,同时启动计时器确保加载状态至少维持设定时长,彻底解决闪屏问题:
class MinDurationLoader<T> extends StatefulWidget { const MinDurationLoader({ super.key, required this.asyncValue, required this.minDuration, required this.dataBuilder, required this.errorBuilder, required this.loadingBuilder, }); final AsyncValue<T> asyncValue; final Duration minDuration; final Widget Function(T data) dataBuilder; final Widget Function(Object error, StackTrace? stackTrace) errorBuilder; final Widget Function() loadingBuilder; @override State<MinDurationLoader<T>> createState() => _MinDurationLoaderState<T>(); } class _MinDurationLoaderState<T> extends State<MinDurationLoader<T>> { bool _hasMetMinDuration = false; late Timer _minDurationTimer; @override void initState() { super.initState(); // 启动计时器,达到最小时长后更新状态 _minDurationTimer = Timer(widget.minDuration, () { if (mounted) { setState(() => _hasMetMinDuration = true); } }); } @override void didUpdateWidget(covariant MinDurationLoader<T> oldWidget) { super.didUpdateWidget(oldWidget); // 若加载状态结束,重置计时器确保数据返回后仍维持最小加载时长 if (oldWidget.asyncValue.isLoading && !widget.asyncValue.isLoading) { _minDurationTimer.cancel(); _minDurationTimer = Timer(widget.minDuration, () { if (mounted) { setState(() => _hasMetMinDuration = true); } }); } } @override void dispose() { _minDurationTimer.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return widget.asyncValue.when( data: (data) { // 未达到最小时长时继续显示加载界面 if (!_hasMetMinDuration) return widget.loadingBuilder(); return widget.dataBuilder(data); }, error: (err, stack) { // 错误状态直接展示(如需等待可自行修改逻辑) return widget.errorBuilder(err, stack); }, loading: () => widget.loadingBuilder(), ); } }
使用方式
替换你原来的ref.watch(...).when代码,直接用这个组件包裹:
MinDurationLoader( asyncValue: ref.watch(someProvider), minDuration: const Duration(seconds: 2), dataBuilder: (someData) { // 渲染你的数据布局 return YourDataLayout(data: someData); }, errorBuilder: (err, stack) { // 处理错误展示 return ErrorDisplay(message: err.toString()); }, loadingBuilder: () => LoadingScreen(), )
方案2:扩展AsyncValue简化调用
如果觉得每次写组件太繁琐,可以给AsyncValue加个扩展方法,让调用和原生when几乎一致:
extension AsyncValueMinDurationExtension<T> on AsyncValue<T> { Widget whenWithMinDuration({ required Duration minDuration, required Widget Function(T data) data, required Widget Function(Object error, StackTrace? stackTrace) error, required Widget Function() loading, }) { return MinDurationLoader<T>( asyncValue: this, minDuration: minDuration, dataBuilder: data, errorBuilder: error, loadingBuilder: loading, ); } }
调用示例:
ref.watch(someProvider).whenWithMinDuration( minDuration: const Duration(seconds: 2), data: (someData) => YourDataLayout(data: someData), error: (err, stack) => ErrorDisplay(message: err.toString()), loading: () => LoadingScreen(), )
注意事项
- 错误状态默认直接展示,若需要错误状态也维持最小时长,可修改
MinDurationLoader中error分支的逻辑 - 组件会在
dispose时取消计时器,避免内存泄漏;若加载状态反复切换,计时器会自动重置 - 可根据产品需求调整最小时长,比如设为1.5秒,平衡体验和等待感
内容的提问来源于stack exchange,提问作者Majoren
相关产品推荐
相关产品推荐

