目录

Flutter 开发实战:封装自定义 Loading 弹窗

一、为什么要封装自定义 Loading 弹窗

二、准备工作

三、封装自定义 Loading 弹窗

四、在项目中使用自定义 Loading 弹窗

五、进一步优化

六、总结


 

 

在 Flutter 应用开发中,Loading 弹窗是一个常见且重要的组件,它能在数据加载、网络请求等操作时,向用户展示当前的状态,提升用户体验。本文将详细介绍如何在 Flutter 项目中封装一个自定义的 Loading 弹窗。

一、为什么要封装自定义 Loading 弹窗

 

在 Flutter 开发中,虽然有一些默认的加载指示器,如CircularProgressIndicator,但在实际项目里,它们往往无法满足多样化的设计需求。比如,产品设计要求加载动画要有独特的风格,或者在弹窗上添加额外的文字描述,这时就需要封装自定义的 Loading 弹窗,让其风格与整个应用的设计风格保持一致,同时具备更好的交互性。

二、准备工作

 

  1. 创建 Flutter 项目:如果还没有 Flutter 项目,可以通过 Flutter 命令行工具创建一个新的项目。在终端执行flutter create custom_loading_demo,这样就创建了一个名为custom_loading_demo的 Flutter 项目。
  2. 熟悉 Flutter 布局和组件:需要对 Flutter 的布局系统(如RowColumnContainer等)以及常用组件(如AnimatedBuilderOpacity等)有一定的了解,因为在封装 Loading 弹窗时会用到这些知识来构建界面和实现动画效果。

三、封装自定义 Loading 弹窗

 

  1. 创建 Loading 弹窗组件:在lib目录下创建一个新的 Dart 文件,比如custom_loading.dart。在这个文件里,定义一个继承自StatelessWidgetCustomLoading类。

 

import 'package:flutter/material.dart';

class CustomLoading extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Container(
      // 弹窗整体的样式
      color: Colors.black54,
      child: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // 加载动画,可以使用CircularProgressIndicator并自定义样式
            CircularProgressIndicator(
              valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
              strokeWidth: 4.0,
            ),
            const SizedBox(height: 16.0),
            // 加载提示文字
            Text(
              '加载中...',
              style: TextStyle(color: Colors.white),
            )
          ],
        ),
      ),
    );
  }
}

 

在这段代码里,Container设置了半透明的黑色背景,CenterColumn用于布局,将加载动画和提示文字垂直居中显示。CircularProgressIndicator设置了白色的进度条颜色和 4 像素的宽度,Text组件用于显示 “加载中...” 的提示文字。

 

  1. 实现弹窗的显示和隐藏逻辑:为了方便在项目中使用这个 Loading 弹窗,需要创建一个工具类来管理它的显示和隐藏。在lib目录下创建loading_utils.dart文件。
import 'package:flutter/material.dart';
import 'custom_loading.dart';

class LoadingUtils {
  static OverlayEntry? _overlayEntry;

  static void showLoading(BuildContext context) {
    if (_overlayEntry != null) return;
    _overlayEntry = OverlayEntry(
      builder: (context) => CustomLoading(),
    );
    Overlay.of(context)?.insert(_overlayEntry!);
  }

  static void hideLoading() {
    _overlayEntry?.remove();
    _overlayEntry = null;
  }
}

 

LoadingUtils类中,_overlayEntry用于存储弹窗的OverlayEntry对象。showLoading方法负责创建并插入OverlayEntryOverlay中,从而显示 Loading 弹窗;hideLoading方法则用于移除OverlayEntry,隐藏 Loading 弹窗。

四、在项目中使用自定义 Loading 弹窗

 

  1. 模拟数据加载场景:在lib/main.dart文件的MyHomePage类中,添加一个按钮点击事件来模拟数据加载过程,并在加载前后显示和隐藏 Loading 弹窗。

 

import 'package:flutter/material.dart';
import 'loading_utils.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () async {
                // 显示Loading弹窗
                LoadingUtils.showLoading(context);
                // 模拟数据加载,这里使用Future.delayed延迟2秒
                await Future.delayed(const Duration(seconds: 2));
                // 隐藏Loading弹窗
                LoadingUtils.hideLoading();
              },
              child: const Text('点击加载数据'),
            )
          ],
        ),
      ),
    );
  }
}

 

在上述代码中,当点击按钮时,先调用LoadingUtils.showLoading(context)显示 Loading 弹窗,然后通过Future.delayed模拟 2 秒的数据加载过程,加载完成后调用LoadingUtils.hideLoading()隐藏 Loading 弹窗。

五、进一步优化

 

  1. 添加动画效果:可以使用AnimatedBuilder结合Opacity组件,为 Loading 弹窗添加淡入淡出的动画效果。在custom_loading.dart文件的CustomLoading类中修改代码如下:

 

import 'package:flutter/material.dart';

class CustomLoading extends StatefulWidget {
  @override
  _CustomLoadingState createState() => _CustomLoadingState();
}

class _CustomLoadingState extends State<CustomLoading> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _opacityAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 300),
    );
    _opacityAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _opacityAnimation,
      builder: (context, child) {
        return Opacity(
          opacity: _opacityAnimation.value,
          child: Container(
            color: Colors.black54,
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  CircularProgressIndicator(
                    valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
                    strokeWidth: 4.0,
                  ),
                  const SizedBox(height: 16.0),
                  Text(
                    '加载中...',
                    style: TextStyle(color: Colors.white),
                  )
                ],
              ),
            ),
          ),
        );
      },
    );
  }
}

 

这里将CustomLoading改为StatefulWidget,并使用AnimationControllerTween创建了一个淡入动画,在initState中启动动画,在dispose中释放资源。通过AnimatedBuilderOpacity组件实现了 Loading 弹窗的淡入效果。

 

  1. 自定义弹窗内容:可以在CustomLoading类中添加参数,让使用者可以自定义加载动画和提示文字。

 

import 'package:flutter/material.dart';

class CustomLoading extends StatefulWidget {
  final Widget? loadingIndicator;
  final String? loadingText;

  const CustomLoading({this.loadingIndicator, this.loadingText});

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

class _CustomLoadingState extends State<CustomLoading> with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _opacityAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 300),
    );
    _opacityAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller);
    _controller.forward();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _opacityAnimation,
      builder: (context, child) {
        return Opacity(
          opacity: _opacityAnimation.value,
          child: Container(
            color: Colors.black54,
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: [
                  widget.loadingIndicator?? CircularProgressIndicator(
                    valueColor: AlwaysStoppedAnimation<Color>(Colors.white),
                    strokeWidth: 4.0,
                  ),
                  const SizedBox(height: 16.0),
                  Text(
                    widget.loadingText?? '加载中...',
                    style: TextStyle(color: Colors.white),
                  )
                ],
              ),
            ),
          ),
        );
      },
    );
  }
}

 

这样,在使用CustomLoading时,可以传入自定义的加载动画和提示文字,例如:

 

LoadingUtils.showLoading(context, customLoading: CustomLoading(
  loadingIndicator: SizedBox(
    width: 50.0,
    height: 50.0,
    child: CircularProgressIndicator(
      valueColor: AlwaysStoppedAnimation<Color>(Colors.blue),
      strokeWidth: 5.0,
    ),
  ),
  loadingText: '数据加载中,请稍候...'
));

六、总结

 

通过以上步骤,我们成功地在 Flutter 项目中封装了一个自定义的 Loading 弹窗,并实现了显示、隐藏逻辑以及动画效果的优化。在实际项目开发中,根据不同的需求,还可以进一步扩展和优化这个 Loading 弹窗,比如添加更多的动画效果、支持不同的弹窗样式等。希望这篇博客能帮助你在 Flutter 开发中更好地处理加载状态的显示,提升应用的用户体验。

 

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐