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");
      }
    });
  }
}
相关推荐
Bigger5 小时前
🚀 Flutter iOS App 上架 App Store 全流程(图文详解)
flutter·ios·app
_大学牲8 小时前
Flutter 之魂 Dio🔥:四两拨千斤的网络库
前端·数据库·flutter
HuWentao9 小时前
如何创建自我更新的通用项目脚本
前端·flutter
QuantumLeap丶13 小时前
《Flutter全栈开发实战指南:从零到高级》- 08 -导航与路由管理
flutter·ios·dart
折翅鵬17 小时前
Flutter兼容性问题:Could not get unknown property ‘flutter‘ for extension ‘android‘
android·flutter
Zender Han1 天前
Flutter 状态管理详解:深入理解与使用 Bloc
android·flutter·ios
技术男1 天前
flutter中怎么局部刷新
flutter
恋猫de小郭1 天前
Flutter 也有类 React Flow 的节点流程编辑器,快来了解下刚刚开源的 vyuh_node_flow
android·前端·flutter
火柴就是我2 天前
android:enableJetifier=true 再学习
android·flutter