flutter Toast消息提示框

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

重要消息

  • 精通点的可以查看这里 精述
  • Flutter 从入门实践到开发一个APP之UI基础篇 视频
  • Flutter 从入门实践到开发一个APP之开发实战基础篇
  • flutter从入门 到精通 系列文章

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


flutter Toast消息提示框_第1张图片

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

你可能感兴趣的:(flutter,flutter,从入门,到精通)