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

重要消息 【经验分享视频教程 感兴趣的伙伴可以瞅瞅】


最终实现的效果演示【左面ios】【右面android】


本文将描述 在 flutter 项目中实现新手功能引导框功能
1、flutter_guidance_plugin 插件使用
2、组件 CustomPaint 与 CustomPainter 的使用分析
3、组件 WillPopScope 的使用分析
4、canvas 中手势识别 GestureDetector 使用分析
5、Container 实现蒙版效果
6、Canvas 绘制文本分析

1 pub 引用

在 flutter 项目中的配置文件 pubspec.yaml 中添加依赖 插件源码在这里

1.1 通过git添加依赖
  #新手蒙版引导插件flutter_guidance_plugin:#git 方式依赖git:#仓库地址url: https://github.com/zhaolongs/flutter_guidance_plugin.git# 分支ref: master
1.2 通过pub.flutter-io.cn来添加依赖
flutter_guidance_plugin: ^0.0.3
2 创建引导使用

导包

import 'package:flutter_guidance_plugin/flutter_guidance_plugin.dart';
2.1 创建指引数据 方式一

这里是直接指定的 指引位置 x y

  ///创建新手指引数据randomTestData(){List<CurvePoint> curvePointList = [];for (int i = 0; i < 5; i++) {///创建指引CurvePoint curvePoint = CurvePoint(0, 0);if(i==0){///x,y 指定指引位置 从0-1 ,手机屏幕左上角开始为(0,0)位置,右下角为(1,1)curvePoint.x = double.parse("0.5");curvePoint.y = double.parse("0.17");///为引导框内显示的文字curvePoint.tipsMessage = "点击这里可 显示可滑动的引导蒙版";}else if(i==1){curvePoint.x = double.parse("0.5");curvePoint.y = double.parse("0.1");curvePoint.tipsMessage = "点击这里可 再次显示 引导蒙版";}else{curvePoint.x = double.parse("0.${_randomBit(3)}");curvePoint.y = double.parse("0.${_randomBit(3)}");curvePoint.tipsMessage = "这是随机的引导消息内容$i";}curvePointList.add(curvePoint);}return curvePointList;}

在这里我们使用到了 CurvePoint ,这是一个自定义的类,用来保存页面数据行为

class CurvePoint {///x,y 指定指引位置 从0-1 ,手机屏幕左上角开始为(0,0)位置,右下角为(1,1)double x;double y;///为引导框内显示的文字String tipsMessage;String nextString;CurvePoint(this.x, this.y,{this.tipsMessage = "--", this.nextString = "下一步"});
}

对于函数 _randomBit 是生成随机数

  _randomBit(int len) {String scopeF = "123456789"; //首位String scopeC = "0123456789"; //中间String result = "";for (int i = 0; i < len; i++) {if (i == 1) {result = scopeF[Random().nextInt(scopeF.length)];} else {result = result + scopeC[Random().nextInt(scopeC.length)];}}return result;}

然后触发蒙版指引-> 在刚刚进入页面时触发或者点击按钮时触发

  void show1() {///获取模拟数据List<CurvePoint>  curvePointList = randomTestData();///参数一 上下文对象///参数二 [curvePointList]用户指引数据集合///参数三 [pointX][pointY] 当 curvePointList 为null 时起作用 可用作只有一个引导指引功能页面///参数五 [isSlide] 为true 时,提示框可以移动///参数六 [logs] 为true 时输出Log日志showBeginnerGuidance(context, curvePointList: curvePointList,pointX: 0,pointY: 0,isSlide:false ,logs: true);}void show2() {///获取模拟数据List<CurvePoint>  curvePointList = randomTestData();showBeginnerGuidance(context, curvePointList: curvePointList,logs: true,isSlide: true);}

最终实现的效果就如上图中所示 源码在这里

2.2 创建指引数据 方式二

这里是自动获取坐标位置

首先是创建 GlobalKeyPoint ,GlobalKeyPoint 的参数一 是GlobalKey 实例,用来标识 Widget 组件 ,tipsMessage 是气泡提示文字


///用来保存全局新手引导 key
//
///首页面重新定位
GlobalKeyPoint homeHeaderTabBodyRestartLocationGlobalKey = GlobalKeyPoint(GlobalKey(),isShowReact:true,tipsMessage: "点击这里可以重新定位哈");
///首页面客服
GlobalKeyPoint homeHeaderTabBodyCustomerGlobalKey =  GlobalKeyPoint(GlobalKey(),isShowReact:true,tipsMessage: "点击这里可以咨询客服哟");

第二步是在要指引的 Widget 中设置 key:homeHeaderTabBodyRestartLocationGlobalKey.key

第三步是触发蒙版指引效果

     List<GlobalKeyPoint> globalKeyPointList = [];///重新定位globalKeyPointList.add(homeHeaderTabBodyRestartLocationGlobalKey);///客服globalKeyPointList.add(homeHeaderTabBodyCustomerGlobalKey);showBeginnerGuidance(context,globalKeyPointList: globalKeyPointList, logs: true, isSlide: true);

展示效果如下:

当然也可以考虑在页面刚刚绘制完成时触发

  @overridevoid initState() {super.initState();WidgetsBinding.instance.addPostFrameCallback((duration) {///页面初始化完成后再构建数据});
}

3、蒙版效果实现

在这里实现的蒙版效果从两方面来讲:

3.1 一是

我们这里的蒙版引导层实际上是通过 Navigator push 了一个 Widget ,那么我们需要实现 push 出来的新在页面层要保持透明,那么在这里是这样实现的:

  ///背景透明的跳转Navigator.of(context).push(PageRouteBuilder(opaque: false,pageBuilder: (context, animation, secondaryAnimation) {///GuideSplashPage 是引导页面具体实现return GuideSplashPage(curvePointList:curvePointList,pointX: pointX,pointY: pointY,textColor: tipsTextColor,isSlide:isSlide,clickCallback:clickCallback);}));
}

PageRouteBuilder 在 flutter 中用来自定义路由切换功能,在这里通过 pageBuilder 函数来构建将到 跳转的页面

对于参数 opaque ,我们可以理解为用来配置开启的页面背景是否透明,这里配置的为 false ,如果配置为 true,那么页面背景将不会透明。

在这个 PageRouteBuilder 中,我们还可以添加一个渐变动画,使用页面切换效果体验更佳

  ///背景透明的跳转Navigator.of(context).push(PageRouteBuilder(opaque: false,pageBuilder: (context, animation, secondaryAnimation) {///GuideSplashPage 是引导页面具体实现return GuideSplashPage(curvePointList: curvePointList,pointX: pointX,pointY: pointY,textColor: tipsTextColor,isSlide: isSlide,clickCallback: clickCallback);},transitionsBuilder: (BuildContext context, Animation<double> animation1,Animation<double> animation2, Widget child) {///  渐变过渡return FadeTransition(///渐变过渡 0.0-1.0opacity: Tween(begin: 0.0, end: 1.0).animate(CurvedAnimation(///动画样式parent: animation1,///动画曲线curve: Curves.fastOutSlowIn,),),child: child,);},),);

这里我们添加了一个 transitionsBuilder ,它是用来控制页面效果效果的,例如这里添加了一个 FadeTransition 渐变过渡效果


从上述图片效果中我们可以看到 引导蒙版层出现与消失的时候是有 透明度渐变的效果的。

3.2 二是

当新景透明后,我们需要设置一个如上述图片中的黑色蒙版效果,在这里使用的是 Container 填充整个页面,然后设置一个透明度的背景

 Container(color: Color(0x90000000),... ...
)

4 Android 手机中返回键按钮事件的拦截

在我们的蒙版实现页面 GuideSplashPage 中,我们使用到了 组件 WillPopScope,在Flutter中通过WillPopScope来实现返回按钮拦截

  @overrideWidget build(BuildContext context) {double padding = (MediaQuery.of(context).size.width / 9);double height = MediaQuery.of(context).size.height;double width = MediaQuery.of(context).size.width;///在Flutter中通过WillPopScope来实现返回按钮拦截return WillPopScope(child: Container(height: height,width: width,color: Color(0x90000000),child: ...///在Android手机中,当点击后退按钮的时候,会回调此事件///在这里返回 false 表示拦截事件onWillPop: () async {return Future.value(false);});}
5 气泡提示实现

其实关于气泡提示实现我们也可以使用一张切图来实现,不过无小编喜欢搞事件,所以在这里使用了 三阶贝塞尔曲线 cubicTo 来绘制这个气泡,三阶贝塞尔曲线核心就是做好 控制点与目标点的计算,如下图小编绘制了坐标分析图

这的坐标计算代码比较多,所以小编就不放代码了,可以到源码中查看哈,三阶贝塞尔曲线 cubicTo 使用分析在这里。

6 flutter 中 Canvas 绘制文本分析

Canvas 中并没有直接像 Android ios 中提供绘制文字的方法,在这里 我们是通过段落构建器来综合实现的绘制文本功能

 ///[textWidth] 文本的宽度///[textOffset] 文本绘制的开始位置 左上角///[text] 绘制的文字内容void drawTextFunction(double textWidth, Offset textOffset, Canvas canvas, String text,{Color textColor = Colors.blue}) {///创建画笔  var textPaint = Paint();///设置画笔颜色textPaint.color = textColor;/// 新建一个段落建造器,然后将文字基本信息填入;ui.ParagraphBuilder pb = ui.ParagraphBuilder(ui.ParagraphStyle(textAlign: TextAlign.center,fontWeight: FontWeight.w400,fontStyle: FontStyle.normal,fontSize: 15.0,));///设置文字的样式pb.pushStyle(ui.TextStyle(color: textColor));if (text == null || text.length == 0) {text = "--";} else if (text.length > 20) {text = text.substring(0, 20);text += "...";}pb.addText(text);// 设置文本的宽度约束ui.ParagraphConstraints pc = ui.ParagraphConstraints(width: textWidth);// 这里需要先layout,将宽度约束填入,否则无法绘制ui.Paragraph paragraph = pb.build()..layout(pc);///偏移量在这里指的是文字左上角的 位置canvas.drawParagraph(paragraph, textOffset);}
}

需要注意的是这里的ParagraphBuilder TextStyle 我们通过 ui. 别名来调用的,这是因为

import 'dart:ui' as ui; //这里用as取个别名,有库名冲突import 'package:flutter/material.dart';

在这两个包内,存在相同名字的 ParagraphBuilder 与 TextStyle ,而我们需要使用 ui 包中的,所以在这里通过 别名调用。

7 关于下一步点击事件

在这里,我们不能使用 Button 或者 InkWell 等,也不好使用,因为这里的下一步,我们是通过 Canvas 绘制出来的,Canvas 是不能绘制事件的,而且在这里,我们绘制的 下一步的按钮的位置也是不固定的

所以在这里,小编通过

         GestureDetector(onTapUp: (TapUpDetails detail) {GuideLogs.e('onTapUp');onTap(context, detail);},/*横向拖动的开始状态*/onHorizontalDragStart: (startDetails) {GuideLogs.e('拖动的开始');if (widget.isSlide) {setState(() {slideOffset = startDetails.globalPosition;});}},onHorizontalDragUpdate: (startDetails) {GuideLogs.e('位置变化 dx:${startDetails.globalPosition.dx}  dy:${startDetails.globalPosition.dy}');if (widget.isSlide) {setState(() {slideOffset = startDetails.globalPosition;});}},/*横向拖动的结束状态*/onHorizontalDragEnd: (endDetails) {GuideLogs.e('拖动的结束');if (widget.isSlide) {setState(() {slideOffset = Offset.zero;});}},child: Stack(children: <Widget>[在这里放我们的 CustomPaint 画布绘制的内容],),)

对于 onTap 点击事件来讲,我们这里进行了计算

///用户计算下一步的点击事件void onTap(BuildContext context, TapUpDetails detail) {Offset globalPosition = detail.globalPosition;GuideLogs.e("onTapUp 点击了 ${globalPosition.dx}  ${globalPosition.dy}");if (nextRect != null) {///获取当前 下一步的区域double left = nextRect.left - 60;double right = nextRect.right + 60;double bottom = nextRect.bottom + 60;double top = nextRect.top - 60;///获取当前屏幕上手指点击的位置double dx = globalPosition.dx;double dy = globalPosition.dy;if (dx > left && dx < right && dy > top && dy < bottom) {if (currentPointIndex < widget.curvePointList.length - 1) {///如果当前不是最后一页面,那么取出下一页的内容信息setState(() {currentPointIndex++;});///蒙版中点击下一步的回调事件if(widget.clickCallback!=null){widget.clickCallback(false);}} else {///如果是最后一页退出蒙版引导 if(widget.clickCallback!=null){widget.clickCallback(true);}Navigator.of(context).pop();}}}}///记录下一步按钮位置的回调void liserClickCallback(Rect rect) {this.nextRect = rect;}

上述 使用到的 nextRect,是的个Rect,是用来记录引导层中下一步按钮的绘制位置信息的。


完毕,如有疑问请回复,也可以关注一下,小编最近正在搞事情 Java 、mybatis、springboot 、vue、react、Sql、android、ios 会持续记录

【经验分享视频教程 感兴趣的伙伴可以瞅瞅】

Flutter新手引导蒙版(浮层)相关推荐

  1. Flutter 项目开发指导 从基础入门到精通使用目录

    Flutter 从入门 到精通系列文章 本文章为 Flutter 开发中的经验积累分享.教程分享.开发笔记分享目录,持续维护中. 题记 -- 执剑天涯,从你的点滴积累开始,所及之处,必精益求精. Fl ...

  2. android 蒙版图片带拖动_推荐一个好用小巧的Android引导蒙版(浮层)库

    更新:目前该库已更新v2.0版本,修改了调用api,详细使用可以看:可能是最好用的Android引导层库 前言 每当一个项目开发一个新功能,总会想办法及时让用户得知有这样一个新功能,这时通常会采用引导 ...

  3. Python AI 弹幕播放器来袭!完美的弹幕机制!

    AI智能弹幕(也称蒙版弹幕):弹幕浮在视频的上方却永远不会挡住人物.起源于哔哩哔哩的web端黑科技,而后分别实现在IOS和Android的app端,如今被用于短视频.直播等媒体行业,用户体验提升显著. ...

  4. 实现app第一次安装浮层引导View!

    在我们第一次安装app的时候,有一些app会出现一个覆盖在我们原来View上面的浮层view,用来做一个app的指示,让用户更快的知道app的整体架构和功能点.下面大家看一下效果图: 这就是我们要出现 ...

  5. Flutter开发之《新锐专家之路:混合开发篇》笔记(55)

    Flutter新锐专家之路:混合开发篇 作者:闲鱼技术-正物 本篇是此系列的第二篇,重点介绍如何让Flutter能够开发,实现业务需求.这部分包括:混合栈的管理,混合下的能力补齐和包管理. 混合栈的管 ...

  6. 【Flutter】Flutter 调试 ( 调试回退功能 | Debug 调试中查看变量的方式 | 控制台信息 )

    文章目录 一.调试回退功能 二.Debug 调试中查看变量的方式 三.Debug 控制台信息 四.相关资源 一.调试回退功能 在调试过程中 , 经常错过关键位置的调试 , 如没有进入关键方法进行调试 ...

  7. 弹层蒙版(mask),ios滚动穿透,我们项目的解决方案

    问题描述 项目开发遇到一个ios独有的问题,在wkwebview中稳定复现 问题: 弹出一个蒙版,当在蒙版上面滑动的时候蒙版后面的内容滚动了 这当然是ios的bug,但是经过我们测试iphone7也会 ...

  8. Flutter BlendMode混合模式详解

    原文: https://docs.flutter.io/flutter/dart-ui/BlendMode-class.html 1.介绍: 在画布上绘画时使用的算法. 在画布上绘制形状或图像时,可以 ...

  9. flutter学习之二Material Design设计规范

    前言: 最近在自学flutter跨平台开发,从学习的过程来看真心感觉不是那么一件特别容易的事.不但要了解语法规则, 还要知晓常用控件,和一些扩展性的外延知识,所以套一句古人的话"路漫漫其修远 ...

最新文章

  1. 训练图像预处理函数功能(paddle)
  2. python基础练习(七)
  3. 快速搭建ELK,以及简单故障处理
  4. 使用VideoView做个实用的视频播放器
  5. Spring教程:使用Spring框架和Spring Boot创建Hello World REST API
  6. python package和目录_PyCharm中Directory与Python package的区别
  7. hibernate中antlr对于hql生成抽象语法树源码解析
  8. 你需要知道的基础算法知识——STL和基础数据结构(七)
  9. 为什么招聘高级前端开发这么难?
  10. spring awre的理解
  11. Google高级搜索
  12. python接入excel_Excel 借助 Python 连接 WorkBench,实现 Excel 输入参数返回结果
  13. C语言将0xea转换为字符ea,eA第10章 c语言程序设计初步.ppt
  14. 如何安装Python中numpy,在DOS验证下一步步解决安装问题(DOS下从python的验证到pip验证到Numpy安装成功)
  15. 测试版ios15怎么信任软件,ios15的信任文件在哪?ios15信任授权在哪里设置?
  16. 什么是GB18030,与GBK的关系?
  17. Ambari2.7.4 + HDP3.1.4 离线安装(1)
  18. 【案例】 生成词云玩玩?
  19. 常用第三方包汇总(持续更新)
  20. Kafka的数据是如何存储的

热门文章

  1. 天弘基金首席架构师李鑫:微服务接口限流的算法及架构实现
  2. 爬虫爬取赶集网租房信息
  3. 甲型流感HIN1来袭有感
  4. Framework Design Guidelines读书笔记
  5. 1.基于turtle库的小动物绘制及分辨
  6. c语言弹琴小程序,非常好玩的弹钢琴小程序.doc
  7. TIPTOP ERP系统用户可视化界面修改账号密码
  8. Win10如何打开/关闭软件自启动功能
  9. 账户初始资金修改问题
  10. 25岁上下的你,现在混得怎么样?豆瓣6000 热帖,看完心里真的好难受!