You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 06:18:03