flutter Toast消息提示框

题记
—— 执剑天涯,从你的点滴积累开始,所及之处,必精益求精,即是折腾每一天。


本文章将讲述:
1、在 flutter 跨平台开发中,使用 Dart 实现 Toast 消息提示框效果
2、Overlay与OverlayEntry 使用分析


在这里插入图片描述

1 Toast 使用方法

          //默认是显示在中间的
          Toast.toast(context,msg: "中间显示的 ");
          
          Toast.toast(context,msg: "中间显示的 ",position: ToastPostion.center);
          
          Toast.toast(context,msg: "顶部显示的 Toast $_count",position: ToastPostion.top);
          
          Toast.toast(context,msg: "底部显示的 Toast $_count",position: ToastPostion.bottom);

2 Toast 源码

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

//Toast 显示位置控制 
enum ToastPostion {
  top,
  center,
  bottom,
}

class Toast {
  // toast靠它加到屏幕上
  static OverlayEntry _overlayEntry;
  // toast是否正在showing
  static bool _showing = false;
  // 开启一个新toast的当前时间,用于对比是否已经展示了足够时间
  static DateTime _startedTime;
  // 提示内容
  static String _msg;
  // toast显示时间
  static int _showTime;
  // 背景颜色
  static Color _bgColor;
  // 文本颜色
  static Color _textColor;
  // 文字大小
  static double _textSize;
  // 显示位置
  static ToastPostion _toastPosition;
  // 左右边距
  static double _pdHorizontal;
  // 上下边距
  static double _pdVertical;
  static void toast(
    BuildContext context, {
    //显示的文本
    String msg,
    //显示的时间 单位毫秒
    int showTime = 1000,
    //显示的背景
    Color bgColor = Colors.black,
    //显示的文本颜色
    Color textColor = Colors.white,
    //显示的文字大小
    double textSize = 14.0,
    //显示的位置
    ToastPostion position = ToastPostion.center,
    //文字水平方向的内边距
    double pdHorizontal = 20.0,
    //文字垂直方向的内边距
    double pdVertical = 10.0,
  }) async {
    assert(msg != null);
    _msg = msg;
    _startedTime = DateTime.now();
    _showTime = showTime;
    _bgColor = bgColor;
    _textColor = textColor;
    _textSize = textSize;
    _toastPosition = position;
    _pdHorizontal = pdHorizontal;
    _pdVertical = pdVertical;
    //获取OverlayState
    OverlayState overlayState = Overlay.of(context);
    _showing = true;
    if (_overlayEntry == null) {
      //OverlayEntry负责构建布局
      //通过OverlayEntry将构建的布局插入到整个布局的最上层
      _overlayEntry = OverlayEntry(
          builder: (BuildContext context) => Positioned(
                //top值,可以改变这个值来改变toast在屏幕中的位置
                top: buildToastPosition(context),
                child: Container(
                    alignment: Alignment.center,
                    width: MediaQuery.of(context).size.width,
                    child: Padding(
                      padding: EdgeInsets.symmetric(horizontal: 40.0),
                      child: AnimatedOpacity(
                        opacity: _showing ? 1.0 : 0.0, //目标透明度
                        duration: _showing
                            ? Duration(milliseconds: 100)
                            : Duration(milliseconds: 400),
                        child: _buildToastWidget(),
                      ),
                    )),
              ));
      //插入到整个布局的最上层
      overlayState.insert(_overlayEntry);
    } else {
      //重新绘制UI,类似setState
      _overlayEntry.markNeedsBuild();
    }
    // 等待时间
    await Future.delayed(Duration(milliseconds: _showTime));
    //2秒后 到底消失不消失
    if (DateTime.now().difference(_startedTime).inMilliseconds >= _showTime) {
      _showing = false;
      _overlayEntry.markNeedsBuild();
      await Future.delayed(Duration(milliseconds: 400));
      _overlayEntry.remove();
      _overlayEntry = null;
    }
  }

  //toast绘制
  static _buildToastWidget() {
    return Center(
      child: Card(
        color: _bgColor,
        child: Padding(
          padding: EdgeInsets.symmetric(
              horizontal: _pdHorizontal, vertical: _pdVertical),
          child: Text(
            _msg,
            style: TextStyle(
              fontSize: _textSize,
              color: _textColor,
            ),
          ),
        ),
      ),
    );
  }

//  设置toast位置
  static buildToastPosition(context) {
    var backResult;
    if (_toastPosition == ToastPostion.top) {
      backResult = MediaQuery.of(context).size.height * 1 / 4;
    } else if (_toastPosition == ToastPostion.center) {
      backResult = MediaQuery.of(context).size.height * 2 / 5;
    } else {
      backResult = MediaQuery.of(context).size.height * 3 / 4;
    }
    return backResult;
  }
}

3 Overlay与OverlayEntry

3.1 引言分析

在 Toast 源中,我们使用到了 OverlayEntry ,那么在说 OverlayEntry 之前,我们需要结合 Overlay 一块谈一谈。

Overlay 在英文中的解析为 【覆在……上面】,那么在 flutter 中,它是一个Stack的widget,那么通过 Overlay 可以将 overlay entry 插入到 overlay 中,使独立的child窗口悬浮于其他widget之上。

因为Overlay本身使用的是 Stack 布局,所以 overlay entr y可以使用 Positioned 或者 AnimatedPositioned 在overlay中定位自己的位置,所以在上述 Toast 源码中,我们使用 Positioned 构建的 OverlayEntry 的布局,并通过 Positioned 来控制子布局的对齐方式,也就是说我们提到的 Toast 的显示位置,如居中等。

当我们创建MaterialApp的时候,它会自动创建一个Navigator,然后创建一个Overlay; 然后利用这个Navigator来管理路由中的界面。可以理解为类似Android中的WindowManager,可以利用 addView 和 removeView 方法添加或删除 View 到界面中。

那么在实际开发中,我们要实现要将某个widget悬浮到页面表层,就可以利用Overlay来实现,例如我们的 Toast 提示框。

3.2 Overlay 与 OverlayEntry 的使用方法

一般就是插入子 Widget 与移动 Widget ,例如我们上述的 Toast ,显示就是向现有的 Widget 树中插入 Widget ,隐藏就是移除


//创建OverlayEntry
Overlay entry=new OverlayEntry(builder:(){
/*在这里创建对应的widget 并return回去,常用Positioned布局*/

});
//往Overlay中插入插入OverlayEntry
Overlay.of(context).insert(overlayEntry);
//调用entry自身的remove()方法,从所在的overlay中移除自己
entry.remove();

4 MediaQuery 的分析

在 flutter 开发中,使用MediaQuery获取屏幕高宽等信息。

MediaQuery.devicePixelRatio 每一个逻辑像素点对应的物理像素点个数
MediaQuery.size.width 用逻辑像素表示的屏幕宽度
MediaQuery.size.height 用逻辑像素表示的屏幕高度
MediaQuery.padding.top 屏幕上部被系统UI遮挡的部分的逻辑高度(即:状态栏高度)
MediaQuery.textScaleFactor 显示文字时,每一个逻辑像素对应的字体像素

4.1 像素简析

Flutter中控件的高宽和字体大小时,使用的是逻辑像素,并非是实际的物理像素。
Android 屏幕适配系列综述专栏》 文章中有详细分析

实际像素=逻辑像素*MediaQuery.devicePixelRatio

【1】 目前在西瓜视频上免费刊登 Flutter 系列教程,每日更新,欢迎关注接收提醒点击查看提示

在这里插入图片描述


【2】 本公众号会首发系列专题文章,付费的视频课程会在公众号中免费刊登,在你上下班的路上或者是睡觉前的一刻,本公众号都是你浏览知识干货的一个小选择,收藏不如行动,在那一刻,公众号会提示你该学习了。
在这里插入图片描述

早起的年轻人 CSDN认证博客专家 移动开发 项目管理 Java
只要用心去做,每一件事情还是有可能成功的,当然成功是没有界限的,只不过是达到自己心里的那个目标,公众号:我的大前端生涯,一个爱喝茶的程序员,通常会搞搞SpringBoot 、Herbinate、Mybatiys、Android、iOS、Flutter、Vue、小程序等.
©️2020 CSDN 皮肤主题: 代码科技 设计师:Amelia_0503 返回首页