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

Flutter中实现便捷全局加载效果的最佳实践是什么?

Flutter 实现类似 iOS AOP 加载视图的最佳实践

Flutter 里虽然没有原生 iOS 的 AOP 语法,但可以通过以下几种方案实现便捷的加载视图管理,避免重复编写显示/隐藏逻辑:

方案一:全局 Overlay 工具类(最接近 iOS 分类调用体验)

通过 Flutter 的 Overlay 组件实现全局弹窗式加载,无需依赖页面状态,直接在任意位置调用工具方法即可,和 iOS 里 [self showLoading...] 的调用方式几乎一致。

class LoadingUtil {
  static OverlayEntry? _overlayEntry;

  // 显示加载视图
  static void showLoading({String title = 'processing'}) {
    if (_overlayEntry != null) return; // 避免重复显示
    _overlayEntry = OverlayEntry(
      builder: (context) => Container(
        color: Colors.black.withOpacity(0.5),
        child: Center(
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              const CircularProgressIndicator(color: Colors.white),
              const SizedBox(height: 12),
              Text(title, style: const TextStyle(color: Colors.white, fontSize: 16)),
            ],
          ),
        ),
      ),
    );
    // 需要提前初始化全局 NavigatorKey,建议在 MaterialApp 里配置
    Overlay.of(GlobalKey<NavigatorState>().currentContext!)?.insert(_overlayEntry!);
  }

  // 隐藏加载视图
  static void hideLoading() {
    _overlayEntry?.remove();
    _overlayEntry = null;
  }
}

使用方式

在任意异步请求前后直接调用:

// 开始请求时显示
LoadingUtil.showLoading(title: 'processing');
// 模拟网络请求
await Future.delayed(const Duration(seconds: 2));
// 请求结束后隐藏
LoadingUtil.hideLoading();

方案二:封装页面基类(页面级加载管理)

如果需要每个页面独立管理加载状态,可以封装一个带加载逻辑的 BasePage 基类,让业务页面继承它,直接调用基类提供的加载方法。

abstract class BasePage extends StatefulWidget {
  const BasePage({super.key});

  @override
  BasePageState createState() => BasePageState();
}

class BasePageState extends State<BasePage> {
  bool _isLoading = false;
  String _loadingTitle = 'processing';

  // 显示页面级加载
  void showLoading({String title = 'processing'}) {
    setState(() {
      _isLoading = true;
      _loadingTitle = title;
    });
  }

  // 隐藏页面级加载
  void hideLoading() {
    setState(() {
      _isLoading = false;
    });
  }

  // 子类实现页面主体内容
  Widget buildContent();

  @override
  Widget build(BuildContext context) {
    return Stack(
      children: [
        buildContent(),
        // 根据加载状态显示遮罩和加载框
        if (_isLoading)
          Container(
            color: Colors.black.withOpacity(0.5),
            child: Center(
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  const CircularProgressIndicator(color: Colors.white),
                  const SizedBox(height: 12),
                  Text(_loadingTitle, style: const TextStyle(color: Colors.white, fontSize: 16)),
                ],
              ),
            ),
          ),
      ],
    );
  }
}

使用方式

业务页面继承 BasePage,实现 buildContent 方法,调用加载方法:

class HomePage extends BasePage {
  @override
  Widget buildContent() {
    return Scaffold(
      appBar: AppBar(title: const Text('Home')),
      body: Center(
        child: ElevatedButton(
          onPressed: () async {
            showLoading(title: 'loading...');
            await Future.delayed(const Duration(seconds: 2));
            hideLoading();
          },
          child: const Text('Start Request'),
        ),
      ),
    );
  }
}

方案三:结合状态管理实现全局/跨页面加载

如果需要跨页面共享加载状态(比如全局接口请求的统一加载提示),可以用 Riverpod、Provider 等状态管理工具实现。

以 Riverpod 为例:

// 定义加载状态和通知器
final loadingProvider = StateNotifierProvider<LoadingNotifier, LoadingState>((ref) => LoadingNotifier());

class LoadingState {
  final bool isLoading;
  final String title;

  LoadingState({required this.isLoading, this.title = 'processing'});
}

class LoadingNotifier extends StateNotifier<LoadingState> {
  LoadingNotifier() : super(LoadingState(isLoading: false));

  void showLoading({String title = 'processing'}) {
    state = LoadingState(isLoading: true, title: title);
  }

  void hideLoading() {
    state = LoadingState(isLoading: false);
  }
}

使用方式

在页面中监听状态并显示加载视图:

class HomePage extends ConsumerWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final loadingState = ref.watch(loadingProvider);
    return Scaffold(
      body: Stack(
        children: [
          Center(
            child: ElevatedButton(
              onPressed: () async {
                ref.read(loadingProvider.notifier).showLoading(title: 'loading...');
                await Future.delayed(const Duration(seconds: 2));
                ref.read(loadingProvider.notifier).hideLoading();
              },
              child: const Text('Start Request'),
            ),
          ),
          if (loadingState.isLoading)
            Container(
              color: Colors.black.withOpacity(0.5),
              child: Center(
                child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    const CircularProgressIndicator(color: Colors.white),
                    const SizedBox(height: 12),
                    Text(loadingState.title, style: const TextStyle(color: Colors.white, fontSize: 16)),
                  ],
                ),
              ),
            ),
        ],
      ),
    );
  }
}

方案选择建议

  • 追求最便捷的全局调用:选全局 Overlay 工具类
  • 页面独立管理加载状态:选页面基类
  • 跨页面共享加载状态或需要统一管理:选状态管理方案

内容的提问来源于stack exchange,提问作者ximmyxiao

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 22:42:54