1. 思路
1). 原生开发
- Android 使用原生 Toast 对象
- iOS 使用原生创建一个悬浮层添加到窗口上
2). Flutter 开发
- 使用 Overlay 对象添加一个 OverlayEntry 对象实现, 并将其按顺序显示出来
2. 原生开发
1). Android
- 原生 kotlin
先在 onCreate 方法中初始化 Channel, 这里显示吐司是由 Flutter 代码主动调用,因此我们使用 MethodChannel 对象,根据 Flutter 中传递过来的方法名称和参数去显示 Toast , 在 Android 中相对比较容易。
/**
* Flutter 向 Native 主动调用 MethodChannel
* Native 向 Flutter 发送事件 EventChannel
*/
class MainActivity: FlutterActivity() {
companion object {
/** Toast标识 */
private const val TOAST = "com.mazaiting/toast"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
initChannel()
}
/**
* 初始化 Channel
*/
private fun initChannel() {
initToastChannel()
}
/**
* Toast工具类
*/
private fun initToastChannel() {
// Toast 显示
MethodChannel(flutterView, TOAST).setMethodCallHandler { methodCall, result ->
println(methodCall.arguments)
when(methodCall.method) {
"showToast" -> {
Toast.makeText(this, methodCall.argument<String>("msg"), Toast.LENGTH_SHORT).show()
result.success("success")
}
else -> {
// 未实现
result.notImplemented()
}
}
}
}
2). iOS
- 先创建一个 UILabel, 设置它的字体大小,颜色,内容,居中,再创建一个 UIView , 设置它的背景,圆角,并将 UILabel 添加到其中,最后创建 UIWindow对象,将 UIView 添加到 UIWindow 中,最后将 UIWindow 对象添加在UIApplication.shared.windows, 源码如下。
//
// Toast.swift
// Runner
//
// Created by 麻再挺 on 2019/3/25.
// Copyright © 2019 The Chromium Authors. All rights reserved.
//
import UIKit
// 弹窗
class ToastView: NSObject {
// 单例
static let instance: ToastView = ToastView()
// 获取窗口对象
var windows = UIApplication.shared.windows
// 键盘视图
let rv = UIApplication.shared.keyWindow?.subviews.first as UIView!
// 获取屏幕宽高
let windowFrame = UIScreen.main.bounds
// 显示弹窗
// @param content 显示内容
// @param duration 显示时长, 默认为1.5s
func showToast(content: String, duration: CFTimeInterval = 1.5) {
// 清除所有弹窗
clear()
// 创建 Toast 视图
let contentView = UILabel(frame: CGRect(x: 0, y: 0, width: 300, height: 50))
// 设置字体大小
contentView.font = UIFont.systemFont(ofSize: 13)
// 设置字体颜色
contentView.textColor = UIColor.white
// 设置内容
contentView.text = content
// 设置对齐方式
contentView.textAlignment = NSTextAlignment.center
// 创建 Toast 父布局
let containerView = UIView()
// 设置圆角
containerView.layer.cornerRadius = 10
// 设置背景颜色
containerView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.7)
// 将 Toast 视图 添加到 Toast 父布局中
containerView.addSubview(contentView)
let frame = CGRect(x: 0, y: 0, width: 300, height: 50)
let window = UIWindow()
window.backgroundColor = UIColor.clear
window.frame = frame
containerView.frame = frame
window.windowLevel = UIWindowLevelAlert
window.center = CGPoint(x: windowFrame.maxX * 0.5, y: windowFrame.maxY * 0.8)
// window.center = CGPoint(x: rv?.center.x, y: rv?.center.y * 16 / 10)
window.isHidden = false
window.addSubview(containerView)
windows.append(window)
containerView.layer.add(AnimationUtil.getToastAnimation(duration: duration), forKey: "animation")
// perform(#selector(removeToast(_:)), with: window, afterDelay: duration)
DispatchQueue.main.asyncAfter(deadline: .now() + duration, execute:{
self.removeToast(sender: window)
})
}
// 移除当前所有弹窗
func removeToast(sender: AnyObject) {
if let window = sender as? UIWindow {
if let index = windows.firstIndex(of: window) {
// 移除对应 window
windows.remove(at: index)
}
} else {
print("can not find the window")
}
}
// 清除所有弹窗
func clear() {
// 取消之前的所有请求
NSObject.cancelPreviousPerformRequests(withTarget: self)
// 移除全部
windows.removeAll(keepingCapacity: false)
}
}
其中 AnimationUtil 工具类代码如下:
//
// AnimationUtil.swift
// Runner
//
// Created by 麻再挺 on 2019/3/26.
// Copyright © 2019 The Chromium Authors. All rights reserved.
//
import Foundation
// 动画工具类
class AnimationUtil {
// 弹窗动画
// @param duration 显示时长, 默认值为1.5s
// @return 动画
static func getToastAnimation(duration: CFTimeInterval = 1.5) -> CAAnimation {
// 大小变化动画
let scaleAnimation = CAKeyframeAnimation(keyPath: "transform.scale")
// 设置时间
scaleAnimation.keyTimes = [0, 0.1, 0.9, 1]
// 设置值
scaleAnimation.values = [0.5, 1, 1, 0.5]
// 设置显示时间
scaleAnimation.duration = duration
// 透明度变化动画
let opacityAnimation = CAKeyframeAnimation(keyPath: "opacity")
// 设置时间
opacityAnimation.keyTimes = [0, 0.8, 1]
// 设置值
opacityAnimation.values = [0.5, 1, 0]
// 设置显示时长
opacityAnimation.duration = duration
// 组动画
let animation = CAAnimationGroup()
// 设置动画组
animation.animations = [scaleAnimation, opacityAnimation]
// 动画过渡效果
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
// 设置时间
animation.duration = duration
// 设置重复次数
animation.repeatCount = 0
// 设置完成后移除
animation.isRemovedOnCompletion = false
return animation
}
}
3). Flutter 端代码
import 'package:flutter/services.dart';
class Toast {
// 获取原生Toast
static const TOAST = const MethodChannel("com.mazaiting/toast");
/// 显示 Toast
/// @param msg 显示信息
static Future<void> show(msg) async {
try {
final String result = await TOAST.invokeMethod('showToast', {'msg': msg});
print("Android_Toast: " + result);
} on PlatformException catch (e) {
print("Android_Toast: " + e.message);
}
}
}
3. Overlay 添加 OverlayEntry 方法
1). 创建一个 OverlayEntry 实体对象
//创建一个OverlayEntry对象
OverlayEntry overlayEntry = OverlayEntry(builder: (context) {
//外层使用Positioned进行定位,控制在Overlay中的位置
return new Positioned(
top: MediaQuery.of(context).size.height * 0.8, // 设置距离顶部80%
child: new Material(
type: MaterialType.transparency, // 设置透明
child: new Container(
width: MediaQuery.of(context).size.width, // 设置宽度
alignment: Alignment.center, // 设置居中
child: new Center(
child: Container(
constraints: BoxConstraints(
maxWidth: MediaQuery.of(context).size.width *
0.8), // 设置约束,最大宽度为屏幕宽的80%
child: new Card(
child: new Padding(
padding: EdgeInsets.all(10),
child: new Text(message),
),
color: Colors.grey,
shape: RoundedRectangleBorder(
borderRadius:
BorderRadius.all(Radius.circular(10))), // 设置圆角
),
),
),
),
));
});
2). 添加到 Overlay 中
//往Overlay中插入插入OverlayEntry
Overlay.of(context).insert(overlayEntry);
3). 延时后移除
//两秒后,移除Toast
new Future.delayed(Duration(seconds: 2)).then((value) {
// 移除
overlayEntry.remove();
});
网友评论