标签:call render 最新版 主题 electron 中间 png sch ret
不要冒然评价我,你只知道我的名字,却不知道我的故事,你只是听闻我做了什么,却不知我经历过什么。
俗话说得好,产品有三宝,弹窗浮层加引导。
上图截图自我司 App 晓黑板中的口算模块,相信每个 App 开发在工作中都碰到这种场景,为了避免用户对新功能产生困惑,会对一些功能加一些引导操作。在原生开发中,例如 Android 开发中,我们可以使用 NewbieGuide 等开源库来实现。但是很遗憾的是,在 Dart packages 中找了一圈,一无所获。
但是我们还是很快就解决了问题,既然解决不了问题,我们就要学会让这个问题不存在,这时候开发一宝就显得尤其有用了。
本文完,大家下期再见??
~
~
~
开个玩笑。真的猛男,敢于直面惨淡的人生,也敢于正视淋漓的鲜血,这区区需求怎么能打倒我们。接下来我们开始琢磨一下这个引导操作要怎么实现,相信各位小伙伴接到这个需求第一个想到的就是,这玩意儿不就是在整个页面上面盖一个蒙层,然后把中间再抠一块出来。最后再加一些文字和一个下一步按钮就行了嘛。你要是把这个需求想得这么简单,那你可就真是大对特对了。所以我们就按前面说的三步来实现这个东西。当然,你如果不想接着往下看的话,可以直接点击这里使用我们开发完成的引导组件库来快速在你的 Flutter 项目中接入引导功能。
那么如何在 Flutter 页面上盖一个浮层呢?翻了一下 Flutter 的 API 文档,找到了两个法宝,分别是 Overlay 和 OverlayEntry。两者的使用方法如下。
class _MyWidgetState extends State<MyWidget> {
OverlayEntry overlayEntry;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
RaisedButton(
onPressed: () {
/// 1. 创建一个 overlayEntry 实例,builder 方法返回一个 Widget
/// 该 Widget 会被渲染到页面顶层
overlayEntry = OverlayEntry(
builder: (context) => Container(
color: Colors.white.withOpacity(.4),
child: Center(
child: RaisedButton(
onPressed: () {
/// 3. 执行 remove 方法销毁 overlayEntry 实例
overlayEntry.remove();
},
child: Text(‘点我关闭 OverlayEntry‘),
),
),
),
);
/// 2. 使用 OverlayState.insert 方法来显示 overlayEntry
Overlay.of(context).insert(overlayEntry);
},
child: Text(‘点我康康 Overlay 的用法‘),
),
],
),
);
}
}
如果你不嫌烦的话,还可以点击这里亲自试一试效果。
上面解决了浮层问题,下面我们就来看一下如何让蒙层中间某一块区域亮起来的问题,这个问题很简单,为什么会亮,因为有光,怎么有光。我想到了上帝,因为上帝说要有光,于是就有了光。
接下来就是如何成为上帝的第一步,我们要找到需要高亮的那个组件的大小和位置,经过我缜密的调查,发现在 Flutter 中可以通过 GlobalKey
来获取一个元素的大小和位置,核心代码如下:
/// 1. 声明一个 globalKey
final globalKey = GlobalKey();
RaisedButton(
/// 2. 将 globalKey 绑定到组件上
key: globalKey,
onPressed: () {
///
},
child: Text(
‘点我康康控制台输出‘,
),
);
/// 3. 通过下面的代码来获取组件的尺寸和位置
RenderBox renderBox = globalKey.currentContext.findRenderObject();
Size size = renderBox.size;
Offset offset = renderBox.localToGlobal(Offset.zero);
print(size);
print(offset);
其中 Size
中有 width
和 height
属性,分别表示高亮组件的宽高属性,Offset
中有 dx
和 dy
属性,分别表示组件左上角距离屏幕左侧和顶部的距离。相信做 Web 开发的同学对这个都很熟悉了。
如果你不嫌烦的话,还可以点击这里亲自试一试效果。
我们已经在代码层面定位到这个组件的位置了,接下来就是对该区域进行精准打击,让这块区域不被浮层的颜色所覆盖,请看成为上帝的第二步。
先看结果,可以看到中间的组件没有被遮罩层遮住,但是有眼睛的同学可能会发现,为啥上下还会各有一段也没被遮住,那是因为 RaisedButton
上下自带一个 margin,所以代码获取 RaisedButton
的实际占位比看起来要大,有兴趣的同学可以去研究一下怎么去掉这个 margin。又有同学会说了,为啥这个截图是移动端的截图,不是 Web 浏览器上的截图。这个问题问得好,请看下面核心代码。
OverlayEntry(
builder: (context) => Stack(
children: [
/// 我们使用了 ColorFiltered 来实现这个功能
ColorFiltered(
colorFilter: ColorFilter.mode(
/// 遮罩层颜色
Colors.red.withOpacity(.4),
BlendMode.srcOut,
),
child: Stack(
children: [
Container(
decoration: BoxDecoration(
/// 任何颜色均可
color: Colors.white,
backgroundBlendMode: BlendMode.dstOut,
),
),
Positioned(
/// 和需要高亮组件的大小和位置均一致
child: Container(
/// 任何颜色均可
color: Colors.white,
width: size.width,
height: size.height,
),
left: offset.dx,
top: offset.dy,
),
],
),
),
],
),
);
上面可以看到,我们使用了 Stack
和 Positioned
来实现将组件放到我们想要的位置,然后实现高亮的核心组件是 ColorFiltered
,ColorFiltered 可太好了,我可太喜欢这个组件了,它能做的事情也很有趣,后面我们还再出一篇文章去单独地介绍它。敬请期待叭~
然后说一说为啥我们的截图是移动端,不再是 Web 端,因为 ColorFiltered
这哥们太强大,以至于 Flutter 团队在 Flutter Web 上还没有完全实现它。你可以在 Flutter 仓库里面的随便找到不少关于 ColorFiltered 在 Web 上表现异常的 Issue。
当然如果你不嫌烦的话,而且也愿意在 Web 上试一下没有效果的效果,我也很贴心的为你准备了在线链接。
终于到了最后一步,加上下一步按钮和文字,这就不用说了,创建 overlayEntry
的时候你愿意在 builder
方法里面返回啥都行。
那么我还漏掉了什么没说呢?认真思考的同学可能想到,我要怎么更新 overlayEntry
呢,引导页一般有多个呀,我不能每次都 remove 掉当前的,然后再 insert 一个新的吧,那样页面肯定会有闪烁。其实如果认真看了OverlayEntry文档的话肯定不会错过这个 markNeedsBuild
方法。这里就不再举个例子了,我太懒了。总之就是如果 builder 的内容有变化,你对 overlayEntry 执行一次 overlayEntry.markNeedsBuild()
就可以了,Flutter 就会重新渲染一次 builder 返回的内容。以此来做到无闪烁切换引导页。
相信完成了上面三步,我们即使没有成为上帝,也能做到有光,让指定区域高亮起来了。一句话总结:
我们使用了 Overlay 和 ColorFiltered 即完成了引导页的制作
耐心看完了的小伙伴肯定都觉得作者诚不欺我,确实很简单,这两个组件我都用过。但是:
So,我们贴心地开发了一个小的库,并借鉴了 Web 端的知名引导库 Intro.js 的名字,给它取名为 flutter_intro
。
少废话,先看东西。
上图即为使用 flutter_intro
的默认主题可以快速实现的引导效果。那么我要怎么使用呢?首先在项目依赖文件 pubspec.yaml 中引入 flutter_intro
。点击这里查看最新版本。
import ‘package:flutter_intro/flutter_intro.dart‘;
/// 1. 引入 Intro,实例化一个对象,传入必传的 stepCount 参数
/// 和 widgetBuilder 参数,其中 widgetBuilder 可以使用库内置的
/// 方法,这样就只需传入需要显示的文本即可。
/// 当然如果你不嫌烦的话,也可以自己实现 widgetBuilder 方法
Intro intro = Intro(
/// 总共的引导页数量,必传
stepCount: 4,
/// 高亮区域与 widget 的内边距
padding: EdgeInsets.all(8),
/// 高亮区域的圆角半径
borderRadius: BorderRadius.all(Radius.circular(4)),
/// 使用库默认提供的 useDefaultTheme 可以快速构建引导页
/// 需要自定义引导页样式和内容,需要自己实现 widgetBuilder 方法
widgetBuilder: StepWidgetBuilder.useDefaultTheme(
/// 提示文本
texts: [
‘你好呀,我是 Flutter Intro。‘,
‘我可以帮你在 Flutter 项目中快读实现 Step By Step 引导。‘,
‘我的用法也十分简单,你可以通过 example 和 api 文档快速掌握和使用。‘,
‘为了快速实现引导,我也默认提供了一套样式,开箱即用,祝大家使用愉快,再见!‘,
],
/// 按钮文字
btnLabel: ‘我知道了‘,
/// 是否在按钮后显示当前步骤
showStepLabel: true,
),
);
下图为 flutter_intro
支持的一些参数配置所对应的位置介绍:
好熟悉的操作,和上面介绍的一模一样。
当然,这里为了方便大家使用,库的内部为大家创建好了 globalKey,使用的时候只需要通过 intro.keys[下标]
获取就行了。
Placeholder(
/// 2. 第一个引导页即绑定 keys 中的第一项,以此内推
key: intro.keys[0]
)
真是太方便了!
好了,我们已经做好全部的准备了。输入以下指令,点击运行。
intro.start(context);
没了,是不是很简单。
如果你嫌我的默认主题丑,想要自己实现 widgetBuilder
方法,我也可以接受。
final Widget Function(StepWidgetParams params) widgetBuilder;
该方法会在引导页出现时由 flutter_intro
内部调用,并会将当前页面上的一些数据通过参数的形式 StepWidgetParams
传进来,最终渲染在屏幕上的为此方法返回的组件。
class StepWidgetParams {
/// 返回前一个引导页方法,如果没有,则为 null
final VoidCallback onPrev;
/// 进入下一个引导页方法,如果没有,则为 null
final VoidCallback onNext;
/// 结束所有引导页方法
final VoidCallback onFinish;
/// 当前执行到第几个引导页,从 0 开始
final int currentStepIndex;
/// 引导页的总数
final int stepCount;
/// 屏幕的宽高
final Size screenSize;
/// 高亮组件的的宽高
final Size size;
/// 高亮组件左上角坐标
final Offset offset;
}
StepWidgetParams
提供了生成引导页所需要的所有参数,默认提供的主题也是基于此参数生成引导页。
天下没有不散的筵席,但是如果你请客,我可以多陪你吃一会儿。
这篇文章主要介绍了如何在 Flutter 中实现操作引导,并且我们基于此封装了一个我们眼里东半球最好用的 flutter_intro
。
对 Electron 感兴趣?请关注我们的开源项目 Electron Playground,带你极速上手 Electron。
我们每周五会精选一些有意思的文章和消息和大家分享,来掘金关注我们的 晓前端周刊。
我们是好未来 · 晓黑板前端技术团队。
我们会经常与大家分享最新最酷的行业技术知识。
欢迎来 知乎、掘金、Segmentfault、CSDN、简书、开源中国、博客园 关注我们。
标签:call render 最新版 主题 electron 中间 png sch ret
原文地址:https://www.cnblogs.com/xiaoheibanfe/p/14118986.html