Flutter Riverpod FutureProvider下Consumer加载进度条溢出如何实现全屏加载

该需求可以实现,核心思路是将加载状态的监听层级从子组件内部提升到页面根节点,用全屏遮罩展示加载动画,子组件仅处理数据渲染和错误提示,避免小容器内加载控件溢出问题。
修复后核心代码
1. 修正数据请求逻辑
import 'package:disk_space/disk_space.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class GetDiskInfo { Future getSize() async { Map diskDetails = {}; // 补充await,否则延迟逻辑不生效 await Future.delayed(const Duration(seconds: 8)); diskDetails['diskSize'] = await DiskSpace.getTotalDiskSpace; diskDetails['freeDisk'] = await DiskSpace.getFreeDiskSpace; diskDetails['usedSpaced'] = diskDetails['diskSize'] - diskDetails['freeDisk']; return diskDetails; } } final clientProvider = Provider((ref) => GetDiskInfo()); final responseProvider = FutureProvider<Map>((ref) async { final diskTest = ref.watch(clientProvider); return diskTest.getSize(); });
2. 改造SpaceCheck子组件
移除子组件内部的加载动画逻辑,仅保留数据渲染和错误处理:
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../providers/size_provider.dart'; class SpaceCheck extends StatelessWidget { final String diskLabel; final Color color; const SpaceCheck(this.diskLabel, this.color, {super.key}); @override Widget build(BuildContext context) { return Expanded( child: Container( child: Column( mainAxisAlignment: MainAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( margin: const EdgeInsets.all(5), child: Text(diskLabel), ), Container( margin: const EdgeInsets.all(5), child: Stack( children: [ Container( height: 5, decoration: BoxDecoration( color: Colors.grey, borderRadius: BorderRadius.circular(6)), ), Consumer(builder: (context, ref, child) { final responseAsyncValue = ref.watch(responseProvider); return responseAsyncValue.when( data: (value) { return FractionallySizedBox( alignment: Alignment.centerLeft, widthFactor: value['usedSpaced'] / value['diskSize'], child: Container( height: 5, decoration: BoxDecoration( color: color, borderRadius: BorderRadius.circular(6))), ); }, loading: () => const SizedBox.shrink(), error: (error, stackTrace) { // 弹窗逻辑放入帧回调,避免build过程中弹出报错 WidgetsBinding.instance.addPostFrameCallback((_) { showDialog(context: context, builder: (context) => AlertDialog( elevation: 10, title: const Text('Error'), content: Text(error.toString()), actions: [ TextButton( child: const Text('Ok'), onPressed: () => Navigator.of(context).pop(), ) ], )); }); return const SizedBox.shrink(); }); }) ], )), Consumer(builder: (context, ref, child) { final responseAsyncValue = ref.watch(responseProvider); return responseAsyncValue.when( data: (value) { return Container( alignment: Alignment.centerRight, margin: const EdgeInsets.all(5), child: Text( "${(value['usedSpaced'] / 1000).toStringAsFixed(2)} GB used of ${(value['diskSize'] / 1000).toStringAsFixed(2)} GB", style: const TextStyle(fontSize: 12), ), ); }, loading: () => const Text('加载中'), error: (error, stackTrace) => Text(error.toString())); }) ], ), ), ); } }
3. 父页面添加全屏加载层
在页面根节点的Stack中添加全屏加载层,仅在加载状态时显示:
import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class DiskPage extends ConsumerWidget { const DiskPage({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final responseState = ref.watch(responseProvider); return Scaffold( appBar: AppBar(title: const Text('磁盘空间检查')), body: Stack( children: [ // 原有页面内容 Column( children: [ SpaceCheck('内置存储', Colors.blue), // 其他页面组件 ], ), // 全屏加载遮罩 if (responseState.isLoading) const Positioned.fill( child: ColoredBox( color: Colors.black12, child: Center(child: CircularProgressIndicator()), ), ) ], ), ); } }
核心改动说明
- 加载状态判断提升到页面根层级,全屏加载动画不会受到小容器尺寸限制,不会溢出
- 修复原代码中
Future.delayed缺少await、AlertDialog直接在build方法返回导致的逻辑错误 - 子组件内部仅保留局部渲染逻辑,职责更清晰
内容的提问来源于stack exchange,提问作者user3698694
相关产品推荐
相关产品推荐

