美文网首页
Flutter Toastx 开发思路

Flutter Toastx 开发思路

作者: _凌浩雨 | 来源:发表于2019-03-29 19:19 被阅读0次

    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();
        });
    
    4). 定义一个静态列表,将每次创建好的 OverlayEntry 对象添加到静态表中,使用递归方法,每次移除一个 OverlayEntry 后,插入静态列表的第一个元素,并显示延时关闭。

    Toastx_Gitee

    Toastx_Github

    相关文章

      网友评论

          本文标题:Flutter Toastx 开发思路

          本文链接:https://www.haomeiwen.com/subject/jsurbqtx.html