Flutter 是 Google推出并开源的移动应用开发框架,主打跨平台、高保真、高性能。开发者能够经过 Dart语言开发 App,一套代码同时运行在 iOS 和 Android平台。git
Flutter官网:flutter-io.cngithub
抖音,英文名TikTok,一款火遍全球的短视频App。在玩抖音的日子里,最令我感到舒服的就是抖音的手势交互,加上近期都在进行Flutter方面的学习,所以就产生了使用Flutter来仿写TikTok手势交互的想法。编程
来看看实现的效果:markdown
Gif:giphy.com/gifs/Y0nMQw…框架
Github地址:github.com/ditclear/ti…函数
demo下载: oop
既然是手势交互,那么就必然要检测手势,在Flutter中提供了GestureDetector
来帮助开发者,并提供了多个回调来处理手势。布局
Property/Callback | Description |
---|---|
onTapDown | 用户每次和屏幕交互时都会被调用 |
onTapUp | 用户中止触摸屏幕时触发 |
onTap | 短暂触摸屏幕时触发 |
onTapCancel | 用户触摸了屏幕,可是没有完成Tap的动做时触发 |
onDoubleTap | 用户在短期内触摸了屏幕两次 |
onLongPress | 用户触摸屏幕时间超过500ms时触发 |
onVerticalDragDown | 当一个触摸点开始跟屏幕交互,同时在垂直方向上移动时触发 |
onVerticalDragStart | 当触摸点开始在垂直方向上移动时触发 |
onVerticalDragUpdate | 屏幕上的触摸点位置每次改变时,都会触发这个回调 |
onVerticalDragEnd | 当用户中止移动,这个拖拽操做就被认为是完成了,就会触发这个回调 |
onVerticalDragCancel | 用户忽然中止拖拽时触发 |
onHorizontalDragDown | 当一个触摸点开始跟屏幕交互,同时在水平方向上移动时触发 |
onHorizontalDragStart | 当触摸点开始在水平方向上移动时触发 |
onHorizontalDragUpdate | 屏幕上的触摸点位置每次改变时,都会触发这个回调 |
onHorizontalDragEnd | 水平拖拽结束时触发 |
onHorizontalDragCancel | onHorizontalDragDown没有成功完成时触发 |
onPanDown | 当触摸点开始跟屏幕交互时触发 |
onPanStart | 当触摸点开始移动时触发 |
onPanUpdate | 屏幕上的触摸点位置每次改变时,都会触发这个回调 |
onPanEnd | pan操做完成时触发 |
onScaleStart | 触摸点开始跟屏幕交互时触发,同时会创建一个焦点为1.0 |
onScaleUpdate | 跟屏幕交互时触发,同时会标示一个新的焦点 |
onScaleEnd | 触摸点再也不跟屏幕有任何交互,同时也表示这个scale手势完成 |
GestureDetector
并不会监听上面全部的手势,只有传入的callbacks非空时,才会监听。因此,若是你想要禁用某个手势时,能够给对应的callback传null。性能
本文主要关注的的是拖动相关的,好比onPanXX
、onHorizontalDragXX
、onVerticalDragXX
等等回调事件。学习
Transform能够在其子Widget绘制时对其应用一个矩阵变换(transformation),Matrix4是一个4D矩阵,经过它咱们能够实现各类矩阵操做。
Container( color: Colors.black, child: new Transform( alignment: Alignment.topRight, //相对于坐标系原点的对齐方式 transform: new Matrix4.skewY(0.3), //沿Y轴倾斜0.3弧度 child: new Container( padding: const EdgeInsets.all(8.0), color: Colors.deepOrange, child: const Text('Apartment for rent!'), ), ), ); 复制代码
效果以下:
在Flutter中提供了一些封装好的transform效果供开发者选择,好比:平移(translate)、旋转(rotate)、缩放(scale)。
在了解了这两点以后,咱们来逐步分解前文的效果。
首先,须要明确的是这些交互效果其实都是经过检测手指的滑动,获得一个x坐标或者y坐标的偏移量,而后配合Transform进行各类不一样的变换,明白了这一点,想作到这样的效果并不难。
这里的交互都是横向的滑动,所以这里主要处理onHorizontalDragXX
相关的事件。
而后来看看首页的布局:
Left:拍摄页 Middle:主页 Right:用户页
外层是一个GestureDetector
用于处理整个页面的手势,里面用的是一个Stack
,相似于Android中的FrameLayout
,它包含3个Transform
的子Widget。
这里选取拍摄页(left)来具体谈谈.
经过观察能够发现,随着偏移量的改变,这里其实包含两个变化:1.缩放 2. 前景色透明度。
缩放能够直接采用前文提到的Transform.scale
,前景色能够用foregroundDecoration
经过改变Color的透明度来达到效果,看看实现:
/// 左侧Widget /// /// 经过 [Transform.scale] 进行根据 [offsetX] 缩放 /// 最小 0.88 最大为 1 Transform buildLeftPage(double screenWidth) { return Transform.scale( scale: 0.88 + 0.12 * offsetX / screenWidth < 0.88 ? 0.88 : 0.88 + 0.12 * offsetX / screenWidth, child: Container( child: Image.asset( "assets/left.png", fit: BoxFit.fill, ), foregroundDecoration: BoxDecoration( color: Color.fromRGBO(0, 0, 0, 1 - (offsetX / screenWidth)), ), ), ); } 复制代码
当咱们的手指在横向移动的时候,记录下偏移总量offsetX
,而后经过setState进行更新。
onHorizontalDragUpdate: (details) { // 控制 offsetX 的值在 -screenWidth 到 screenWidth 之间 if (offsetX + details.delta.dx >= screenWidth) { setState(() { offsetX = screenWidth; }); } else if (offsetX + details.delta.dx <= -screenWidth) { setState(() { offsetX = -screenWidth; }); } else { setState(() { offsetX += details.delta.dx; }); } } 复制代码
经过setState更新偏移量offsetX以后,Flutter便会从新渲染视图,从而达到上图的效果。
Flutter提供了Hero动画来实现这样的过渡效果。Hero指的是能够在路由(页面)之间“飞行”的widget,简单来讲Hero动画就是在路由切换时,有一个共享的Widget能够在新旧路由间切换,因为共享的Widget在新旧路由页面上的位置、外观可能有所差别,因此在路由切换时会逐渐过渡,这样就会产生一个Hero动画。
/// tiktok_page.dart Widget build(BuildContext context) { return Hero( tag: "detail", //child ) ) /// detail_page.dart Widget build(BuildContext context) { return Hero( tag: "detail", // child ) } 复制代码
保证tag一致就能够了。
跟首页同样的思路,只是这里的手势是垂直方向。
布局一样是GestureDetector
加上Stack
再配合Transform.translate
。
Hero( tag: "detail", child: GestureDetector( onVerticalDragUpdate: (details){ // dy 不超过 -screenHeight * 0.6 dy += details.delta.dy; if ((dy < 0 && dy.abs() > screenHeight * 0.6)) { dy = -screenHeight * 0.6; } else { setState(() {}); } }, child: Stack( children: <Widget>[ Image.asset( "assets/detail.png", fit: BoxFit.fitWidth, width: screenWidth, height: screenHeight, ), Transform.translate( offset: Offset(0, dy + screenHeight), child: Container( height: screenHeight * 0.6, child: GestureDetector( onTap: () {}, child: Image.asset( "assets/comment.png",), ) ), ), ], ), ), ); 复制代码
在手指离开屏幕时,根据偏移利用动画进行调整。
onVerticalDragEnd: (_){ // 滑动截止时,根据 dy 判断是展开仍是回缩 if (dy < 0) { if (!isCommentShow && dy.abs() > screenHeight * 0.2) { if (dy.abs() > screenHeight * 0.2) { animateToTop(screenHeight); } else { animateToBottom(screenHeight); } } else { if (dy.abs() > screenHeight * 0.4) { animateToTop(screenHeight); } else { animateToBottom(screenHeight); } } } }, 复制代码
总的来讲,这些交互都是依靠着对手势的检测作到的,相比于Android,Flutter有着一切都是Widget的概念,
GestureDetector
以及Hero
都是Widget并且提供了不少回调函数,再配合数据驱动UI和Flutter优秀的渲染机制,减轻了开发者进行手势交互的难度。
Github地址:github.com/ditclear/ti…
若是本文对你有帮助,请点赞支持。
==================== 分割线 ======================
若是你想了解更多关于MVVM、Flutter、响应式编程方面的知识,欢迎关注我。
简书:www.jianshu.com/u/117f1cf0c…
Github: github.com/ditclear