Flutter-toast loding 封装

使用案例

Toast.showHud("测试"); // Toast 白色主题
Toast.showHud("测试", type: ToastType.black); // Toast 黑色主题
Toast.showLoding(context, userInteraction: true) // loding 白色主题 userInteraction 是否允许交互
Toast.showLoding(context, userInteraction: true, type: ToastType.black); // loding 黑色主题

/// isRealTime: 是否立即显示 ture立即显示 false 是延迟加载的 注意:在initState中方法调用必须为false 不然会出问题
Toast.showLoding(context, userInteraction: true, type: ToastType.black, isRealTime: false); // loding 黑色主题
Toast 白色主题

Toast 黑色主题
loding 白色主题

loding 黑色主题

源码

// 普通toast 是依赖 fluttertoast
请在`pubspec.yaml`添加
fluttertoast: ^8.0.8        # HUD
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';

enum ToastType {
  white, // 白色
  black, // 黑色
}

class Toast {

  static const defaultType = ToastType.white; // 默认样式
  /// 显示Toast
  /// msg: 显示内容
  /// type: 样式
  static void showHud(String msg, {ToastType type = defaultType}) {
    Fluttertoast.cancel();
    Fluttertoast.showToast(
        msg: msg,
        gravity: Platform.isIOS ? ToastGravity.CENTER : ToastGravity.BOTTOM, // iOS显示在中间 安卓显示在底部
        backgroundColor: type == ToastType.black ? Colors.black : Colors.white,
        textColor: type == ToastType.black ? Colors.white : Colors.black,
        fontSize: 16.0);
  }

  /// 取消Toast
  static void dismiss() {
    Fluttertoast.cancel();
    if (RealTimeLoadingDialog.isShow()) {
      RealTimeLoadingDialog.dismiss();
    }
    if (LoadingDialog.isShow()) {
      LoadingDialog.dismiss();
    }
  }

  /// 显示loding
  /// BuildContext context
  /// msg: 显示内容
  /// userInteraction: 是否允许交互
  /// type: 样式
  /// isRealTime: 是否立即显示 ture立即显示 false 是延迟加载的 注意:在initState中方法调用必须为false 不然会出问题
  static void showLoading(BuildContext context,
      {String msg = "",
      bool userInteraction = false,
      ToastType type = _defaultType,
      bool isRealTime = true}) {
    dismiss();
    if (isRealTime) {
      RealTimeLoadingDialog.showLoading(context,
          content: msg, userInteraction: userInteraction, type: type);
    } else {
      LoadingDialog.showLoading(context,
          content: msg, userInteraction: userInteraction, type: type);
    }
  }

///加载弹框
class LoadingDialog {
  static Timer _timer; // 超时定时器
  static ToastType _type;
  static OverlayEntry itemEntry;
  // 是否显示
  static  bool _isShow = false;

  /// 展示
  /// BuildContext context
  /// content 显示内容
  /// userInteraction 是否允许交互 默认不允许
  static void showLoading(BuildContext context,
      {String content = "数据加载中...",
        bool userInteraction = false,
        ToastType type}) async {
    dismiss();
    _type = type;
    _isShow = true;
    ///显示悬浮menu
    itemEntry = OverlayEntry(
        builder: (BuildContext context) => _Loading(
          userInteraction: userInteraction,
          child: _contentView(content),
        ));
    _startTimer();
    WidgetsBinding.instance.addPostFrameCallback((_) {
      Overlay.of(context)?.insert(itemEntry);
    });
  }

  /// loding 是否显示
  static bool isShow() {
    return _isShow;
  }

  ///隐藏
  static void dismiss() {
    if (!_isShow) return;
    var tmp = itemEntry?.mounted;
    if (tmp == false) {
      Future.delayed(Duration(milliseconds: 3), () {
        dismiss();
      });
      return;
    }
    _cleanData();
    _isShow = false;
  }

  static _cleanData() {
    itemEntry?.remove();
    itemEntry = null;
    _stopTimer();
  }

  /// 内容View
  static _contentView(String content) {
    return _loadingContentView(_type, content);
  }

  /// 开始定时
  static _startTimer() {
    _timer = Timer(Duration(seconds: 60), () {
      // 60秒超时时间 自动消失
      LoadingDialog.dismiss();
    });
  }

  /// 销户定时
  static _stopTimer() {
    _timer?.cancel();
    _timer = null;
  }
}


/// 实时加载弹框
class RealTimeLoadingDialog {
  static Timer _timer; // 超时定时器
  static BuildContext _context; // 理论上只有一个 不然就可以用一个数组保存
  static ToastType _type;

  /// 展示
  /// BuildContext context
  /// content 显示内容
  /// userInteraction 是否允许交互 默认不允许
  static void showLoading(BuildContext context,
      {String content = "数据加载中...",
        bool userInteraction = false,
        ToastType type}) async {
    LoadingDialog.dismiss(); // 如果之前有显示取消显示
    if (_context == null) {
      _context = context;
      _type = type;
      var child = _contentView(content);
      _LoadingRoute _route = _LoadingRoute(
        child: _Loading(
          userInteraction: userInteraction,
          child: child,
        ),
      );
      Future.delayed(Duration.zero, () {
        // 必须添加 因为请求是异步等待的 async await
        Navigator.push(
          context,
          _route
        );
      });
      _startTimer();
    }
  }

  /// loading 是否显示
  static bool isShow() {
    return _context==null?false:true;
  }

  ///隐藏
  static void dismiss() {
    if (_context == null) return;
      _stopTimer();
      Navigator.of(_context).pop();
      _context = null;
  }

  /// 内容View
  static _contentView(String content) {
    return _loadingContentView(_type, content);
  }

  /// 开始定时
  static _startTimer() {
    _timer = Timer(Duration(seconds: 60), () {
      // 60秒超时时间 自动消失
      RealTimeLoadingDialog.dismiss();
    });
  }

  /// 销户定时
  static _stopTimer() {
    if (_timer == null) return;
    _timer?.cancel();
    _timer = null;
  }
}


///Widget
class _Loading extends StatelessWidget {
  final Widget child;

  final bool userInteraction;

  _Loading({Key key, @required this.child, this.userInteraction = false})
      : assert(child != null),
        super(key: key);

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
        onTap: () {
          if (userInteraction == false) return;
          LoadingDialog.dismiss();
        },
        child: Material(
            color: Colors.black12,
            child: Center(
              child: child,
            )));
  }
}

///Route
class _LoadingRoute extends PopupRoute {
  final Duration _duration = Duration(milliseconds: 300);
  Widget child;

  _LoadingRoute({@required this.child});

  @override
  Color get barrierColor => Colors.transparent;

  @override
  bool get barrierDismissible => true;

  @override
  String get barrierLabel => null;

  @override
  Widget buildPage(BuildContext context, Animation<double> animation,
      Animation<double> secondaryAnimation) {
    return child ?? Container();
  }

  @override
  Duration get transitionDuration => _duration;
}

Widget _loadingContentView(ToastType _type, String content, {Widget midView}) {
  if (midView == null) midView = _progress(_type);
  var contentView = Row(
    children: [
      Expanded(child: Container()),
      Container(
        decoration: BoxDecoration(
          color: _type == ToastType.black
              ? Color(0xFF252525).withOpacity(0.6)
              : Colors.white,
          borderRadius: BorderRadius.circular(12),
        ),
        padding: EdgeInsets.all(30),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [midView, _label(content, _type)],
        ),
      ),
      Expanded(child: Container())
    ],
  );
  return Column(
    children: [
      Expanded(child: Container()),
      contentView,
      Expanded(child: Container()),
    ],
  );
}

_progress(ToastType _type) {
  return Container(
    width: 28,
    height: 28,
    child: Theme(
      data: ThemeData(
        cupertinoOverrideTheme: CupertinoThemeData(
          // 菊花
          brightness:
          _type == ToastType.black ? Brightness.dark : Brightness.light,
        ),
      ),
      child: CupertinoActivityIndicator(
        radius: 14,
      ),
    ),
  );
}

_label(String content, ToastType _type) {
  if (content.length == 0)
    return Container();
  else {
    return Container(
        margin: EdgeInsets.only(top: 15),
        child: ConstrainedBox(
          constraints: BoxConstraints(
            maxWidth: 250,
          ),
          child: Text(
            content,
            overflow: TextOverflow.ellipsis,
            softWrap: true,
            maxLines: 5,
            style: TextStyle(
              fontSize: 16,
              color: _type == ToastType.black ? Colors.white : Colors.black,
            ),
          ),
        ));
  }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容