使用Flutter仿写抖音的手势交互

640?wx_fmt=jpeg


/   今日科技快讯   /


5月14日,据CNBC报道,网约车公司Uber股价周一收盘下跌10.75%,跌至每股37.10美元,市值622亿美元,Uber于上周五登陆纽交所。


/   作者简介   /


本篇文章来自ditclearl的投稿,分享了他使用Flutter编写TikTok的手势交互功能的文章,相信会对大家有所帮助!同时也感谢作者贡献的精彩文章。


ditclearl的博客地址:

https://juejin.im/user/582d601d2e958a0069bbe687


/   写在前面   /


Flutter 是 Google推出并开源的移动应用开发框架,主打跨平台、高保真、高性能。开发者可以通过 Dart语言开发 App,一套代码同时运行在 iOS 和 Android平台。


Flutter官网:

flutter-io.cn


抖音,英文名TikTok,一款火遍全球的短视频App。在玩抖音的日子里,最令我感到舒服的就是抖音的手势交互,加上近期都在进行Flutter方面的学习,因此就产生了使用Flutter来仿写TikTok手势交互的想法。


来看看实现的效果:


640?wx_fmt=gif


/   GestureDetector以及Transform   /


既然是手势交互,那么就必然要检测手势,在Flutter中提供了GestureDetector来帮助开发者,并提供了多个回调来处理手势。


640?wx_fmt=png


640?wx_fmt=png


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!'),
    ),
  ),
)
;


效果如下:


640?wx_fmt=png


在Flutter中提供了一些封装好的transform效果供开发者选择,比如:平移(translate)、旋转(rotate)、缩放(scale)。


在了解了这两点之后,我们来逐步分解前文的效果。


/   交互分解   /


首先,需要明确的是这些交互效果其实都是通过检测手指的滑动,得到一个x坐标或者y坐标的偏移量,然后配合Transform进行各种不同的变换,明白了这一点,想做到这样的效果并不难。


首页的交互


640?wx_fmt=gif


这里的交互都是横向的滑动,因此这里主要处理onHorizontalDragXX相关的事件。


然后来看看首页的布局


640?wx_fmt=png


  • Left拍摄页 

  • Middle主页 

  • Right:用户页


外层是一个GestureDetector用于处理整个页面的手势,里面用的是一个Stack,类似于Android中的FrameLayout,它包含3个Transform的子Widget。


640?wx_fmt=png


这里选取拍摄页(left)来具体谈谈。


通过观察可以发现,随着偏移量的改变,这里其实包含两个变化:缩放和前景色透明度。


缩放可以直接采用前文提到的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(0001 - (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便会重新渲染视图,从而达到上图的效果。


Hero动画


640?wx_fmt=gif


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一致就可以了。


详情页的交互


640?wx_fmt=gif


跟首页一样的思路,只是这里的手势是垂直方向。


布局同样是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地址:

https://github.com/ditclear/tiktok_gestures


推荐阅读:

一篇文章带你看遍Google I/O 2019大会

巨佬Jake Wharton曾说过:一个App只需要一个Activity

Jetpack核心组件,ViewModel的使用及原理解析


欢迎关注我的公众号

学习技术或投稿


640.png?


640?wx_fmt=jpeg

长按上图,识别图中二维码即可关注

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Flutter与Android的交互可以通过平台通道(platform channel)来实现。平台通道允许Flutter应用程序与原生Android代码之间进行双向通信。 以下是使用平台通道进行Flutter与Android交互的一般步骤: 1. 在Flutter应用程序中,使用`flutter create`命令创建一个新的Flutter项目,或者在现有项目中添加一个新的Android模块。 2. 在Flutter应用程序中创建一个`MethodChannel`对象,该对象用于在Flutter和Android之间进行方法调用通信。例如,在Dart代码中添加以下代码: ```dart import 'package:flutter/services.dart'; // 创建 MethodChannel 对象 MethodChannel channel = MethodChannel('com.example.channelName'); // 在需要的地方调用原生 Android 方法 Future<void> callNativeMethod() async { try { final String result = await channel.invokeMethod('methodName'); print(result); } catch (e) { print('Error: $e'); } } ``` 3. 在Android项目中,创建一个类来处理来自Flutter应用程序的方法调用。例如,在Java代码中添加以下代码: ```java import io.flutter.app.FlutterActivity; import io.flutter.plugin.common.MethodChannel; import io.flutter.plugin.common.MethodCall; import io.flutter.plugin.common.MethodChannel.MethodCallHandler; import io.flutter.plugin.common.MethodChannel.Result; public class MainActivity extends FlutterActivity { private static final String CHANNEL = "com.example.channelName"; @Override public void configureFlutterEngine(FlutterEngine flutterEngine) { super.configureFlutterEngine(flutterEngine); // 注册方法调用处理器 new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), CHANNEL) .setMethodCallHandler(new MethodCallHandler() { @Override public void onMethodCall(MethodCall call, Result result) { if (call.method.equals("methodName")) { // 处理来自Flutter的方法调用 String response = someNativeMethod(); result.success(response); } else { result.notImplemented(); } } }); } // 原生 Android 方法的实现 private String someNativeMethod() { // 实现自己的逻辑 return "Response from Android"; } } ``` 在上述代码中,我们首先定义了一个与Flutter应用程序通信的通道名称(CHANNEL),然后注册了一个方法调用处理器。当Flutter应用程序调用`methodName`方法时,处理器将调用`someNativeMethod`方法并返回响应。 4. 最后,您可以在Flutter应用程序中调用`callNativeMethod`方法,这将触发与Android的交互,并返回来自Android的响应。 请注意,上述代码只是示例,并且可以根据您的需求进行修改和扩展。此外,您还可以使用平台通道传递参数和接收回调,以实现更复杂的交互逻辑。 这是使用平台通道在Flutter和Android之间进行基本交互的基本步骤。希望对您有所帮助!如果有任何进一步的问题,请随时提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值