Flutter Loading 的封装

前言:

我们应用开发中 很多地方都需要和后端进行通信。或者在做一些耗时操作的时候。我们不希望用户在没有返回数据的时候 进行点击。或者二次提交这个时候 需要一个 Loading 的弹框。来给用户展示数据提交或者响应中。

1.照例我们先看效果

就是上面类型的效果。一个可以提示用户 和 阻断用户继续点击的这么一个显示,为了达到理想的效果。

  1. 其实我们点击按钮的时候 是调用了多次的。这个场景就是 同时调用多次。但是只有一个在展示的的。
  2. 内部维护一个计时器,超过指定时间没有收到 hideLoading.指令能自己取消。防止某些情况下 漏掉取消
  3. 出现多次点击的时候我们以最后一次请求重置 计时时间

下面是我们的测试代码调用。类似模拟多次调用:

复制代码
onTap: () {
  AppOverlay().showLoading();
  AppOverlay().showLoading();
  AppOverlay().showLoading();
  AppOverlay().hideLoading();
  AppOverlay().hideLoading();
  AppOverlay().hideLoading();

  AppOverlay().showLoading();

  AppOverlay().hideLoading();

  AppOverlay().showLoading();
  AppOverlay().showLoading();
  AppOverlay().hideLoading();
  AppOverlay().hideLoading();

  AppOverlay().showLoading();

  Future.delayed(const Duration(seconds: 1)).then((value) {
    AppOverlay().hideLoading();
  });
},

2.实现思路介绍

内部使用:OverlayEntry。 这个东西很好用,可以直接盖住 其他组件。悬浮于其他组件之上。这样我们就能更好地控制 这个 OverlayEntry

3.具体的实现

开箱即用:

复制代码
import 'dart:async';

import 'package:flutter/material.dart';
import 'package:game/const/app_textStyle.dart';
import 'package:game/utils/app_screen.dart';
import 'package:game/wrap/extension/extension.dart';

import '../../utils/app_colors.dart';

class AppOverlay {
  //单例
  static final AppOverlay _instance = AppOverlay._internal();

  /// 页面引用计数器
  static int _count = 0;

  /// 超时时间
  final int _durationMaxTime = 10;

  /// 当前计时器时间
  int _durationCurrentTime = 0;

  final Duration duration = const Duration(seconds: 1);

  /// 计时器
  Timer? _timer;
  factory AppOverlay() {
    return _instance;
  }
  OverlayEntry? _overlayEntry;
  AppOverlay._internal(); // 不需要初始化

  showLoading({String? title}) {
    _showLoading(title: title);
  }

  hideLoading() {
    _hideLoading();
  }

  _showLoading({String? title}) async {
    Future.delayed(Duration.zero, () {
      _durationCurrentTime = 0;
      AppOverlay._count++;
      _timer ??= Timer.periodic(duration, (timer) {
        //print('_showLoading timer:$_durationCurrentTime');
        if (_overlayEntry != null) {
          _durationCurrentTime++;
          if (_durationCurrentTime > _durationMaxTime) {
            _hideLoading();
          }
        } else {
          timer.cancel();
          _timer = null;
        }
      });
      if (_overlayEntry == null) {
        _overlayEntry = OverlayEntry(builder: (BuildContext context) {
          //外层使用Positioned进行定位,控制在Overlay中的位置
          return Positioned(
            child: Material(
              color: Colors.transparent,
              child: Container(
                // width: MediaQuery.of(AppScreen.buildContext).size.width,
                alignment: Alignment.center,
                child: Center(
                  child: Container(
                    constraints: BoxConstraints(
                      minHeight: AppScreen.calc(160),
                      minWidth: AppScreen.calc(160),
                      maxWidth: 520.cale,
                    ),
                    decoration: BoxDecoration(
                      //color: AppColor.colordddddd,
                      color: AppColor.disabledColor,
                      borderRadius: BorderRadius.circular(AppScreen.calc(24)),
                    ),

                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      crossAxisAlignment: CrossAxisAlignment.center,
                      mainAxisAlignment: MainAxisAlignment.center,
                      children: [
                        Padding(
                          padding: title != null
                              ? EdgeInsets.only(top: 40.cale, bottom: 30.cale)
                              : EdgeInsets.symmetric(vertical: 40.cale),
                          child: const CircularProgressIndicator(
                            //color: AppColor.primaryColor,
                            color: Colors.white,
                          ),
                        ),
                        if (title != null)
                          Padding(
                            padding: EdgeInsets.only(
                              left: 24.cale,
                              right: 24.cale,
                              bottom: 40.cale,
                            ),
                            child: Text(
                              title,
                              style: AppTextStyle.textStyle_32_FFFFFF,
                              textAlign: TextAlign.center,
                            ),
                          ),
                      ],
                    ),
                  ),
                ),
              ),
            ),
          );
        });
        //往Overlay中插入插入OverlayEntry
        AppScreen.overlay!.insert(_overlayEntry!);
      }
    });
  }

  _hideLoading() {
    Future.delayed(Duration.zero, () {
      AppOverlay._count--;
      if (AppOverlay._count <= 0) {
        AppOverlay._count = 0;
        if (_overlayEntry != null) {
          _overlayEntry!.remove();
          _overlayEntry = null;
        } else {
          print("_overlayEntry is null");
        }
        if (AppOverlay._count <= 0) {
          _timer?.cancel();
          _timer = null;
        }
      } else {
        print("AppOverlay Using");
      }
    });
  }
}
相关推荐
LawrenceLan9 小时前
Flutter 零基础入门(九):构造函数、命名构造函数与 this 关键字
开发语言·flutter·dart
一豆羹10 小时前
macOS 环境下 ADB 无线调试连接失败、Protocol Fault 及端口占用的深度排查
flutter
行者9610 小时前
OpenHarmony上Flutter粒子效果组件的深度适配与实践
flutter·交互·harmonyos·鸿蒙
行者9613 小时前
Flutter与OpenHarmony深度集成:数据导出组件的实战优化与性能提升
flutter·harmonyos·鸿蒙
小雨下雨的雨13 小时前
Flutter 框架跨平台鸿蒙开发 —— Row & Column 布局之轴线控制艺术
flutter·华为·交互·harmonyos·鸿蒙系统
小雨下雨的雨13 小时前
Flutter 框架跨平台鸿蒙开发 —— Center 控件之完美居中之道
flutter·ui·华为·harmonyos·鸿蒙
小雨下雨的雨14 小时前
Flutter 框架跨平台鸿蒙开发 —— Icon 控件之图标交互美学
flutter·华为·交互·harmonyos·鸿蒙系统
小雨下雨的雨14 小时前
Flutter 框架跨平台鸿蒙开发 —— Placeholder 控件之布局雏形美学
flutter·ui·华为·harmonyos·鸿蒙系统
行者9615 小时前
OpenHarmony Flutter弹出菜单组件深度实践:从基础到高级的完整指南
flutter·harmonyos·鸿蒙
前端不太难15 小时前
Flutter / RN / iOS,在长期维护下的性能差异本质
flutter·ios