广告
返回顶部
首页 > 资讯 > 移动开发 >AndroidFlutter实现点赞效果的示例代码
  • 327
分享到

AndroidFlutter实现点赞效果的示例代码

2024-04-02 19:04:59 327人浏览 安东尼
摘要

目录前言绘制小手完整源码前言 点赞这个动作不得不说在社交、短视频等App中实在是太常见了,当用户手指按下去的那一刻,给用户一个好的反馈效果也是非常重要的,这样用户点起赞来才会有一种强

前言

点赞这个动作不得不说在社交、短视频等App中实在是太常见了,当用户手指按下去的那一刻,给用户一个好的反馈效果也是非常重要的,这样用户点起赞来才会有一种强烈的我点了赞的效果,那么今天我们就用Flutter实现一个掘金App上的点赞效果。

首先我们看下掘金App的点赞组成部分,有一个小手,点赞数字、点赞气泡效果,还有一个震动反馈,接下来我们一步一步实现。

知识点:绘制、动画、震动反馈

绘制小手

这里我们使用Flutter的Icon图标中的点赞小手,Icons图标库为我们提供了很多App常见的小图标,如果使用苹果苹果风格的小图标可以使用cupertino_icons: ^1.0.2插件,图标并不是图片,本质上和emoji图标一样,可以添加到文本中使用,所以图标才可以设置不同的颜色属性,对比使用png格式图标可以节省不少的内存。

接下来我们就将这两个图标绘制出来,首先我们从上图可以看到真正的图标数据其实是IconData类,里面有一个codePoint属性可以获取到Unicode统一码,通过String.fromCharCode(int charCode)可以返回一个代码单元,在Text文本中支持显示。

class IconData{
/// The Unicode code point at which this icon is stored in the icon font.
/// 获取此图标的Unicode代码点
final int codePoint;
}

class String{
/// 如果[charCode]可以用一个UTF-16编码单元表示,则新的字符串包含一个代码单元
external factory String.fromCharCode(int charCode);
}

接下来我们就可以把图标以绘制文本的形式绘制出来了

关键代码:

 // 赞图标
  final icon = Icons.thumb_up_alt_outlined;
// 通过TextPainter可以获取图标的尺寸
  TextPainter textPainter = TextPainter(
      text: TextSpan(
          text: String.fromCharCode(icon.codePoint),
          style: TextStyle(
              fontSize: 30,
              fontFamily: icon.fontFamily,// 字体形象家族,这个字段一定要设置,不然显示不出来
              color: Colors.black)),
      textAlign: TextAlign.center,
      textDirection: TextDirection.ltr);
  textPainter.layout(); // 进行布局
  Size size2 = textPainter.size; // 尺寸必须在布局后获取
  //将图标偏移到画布中央
  textPainter.paint(canvas, Offset(-size2.width / 2, -size2.height / 2));

通过上方代码我们就实现了将图标绘制到画板当中

接下来继续绘制点赞数量

代码:

TextPainter textPainter2 = TextPainter(
    text: TextSpan(
        text: "点赞",// 点赞数量
        style: TextStyle(
            fontSize: 9, fontWeight: FontWeight.w500, color: Colors.black)),
    textAlign: TextAlign.center,
    textDirection: TextDirection.ltr);
textPainter2.layout(); // 进行布局
// 向右上进行偏移在小手上面
textPainter2.paint(canvas, Offset(size.width / 9, -size.height / 2 + 5));

然后图标就变成了这样样子

我们看到,掘金App点赞的过程中,周围还有一些小气泡的效果,这里提供一个思路,将这些气泡的坐标点放到一个圆的外环上面,通过动画改变圆的半径达到小圆点由内向外发散,发散的同时改变小圆点的大小,从而达到气泡的效果, 关键代码:

var r = size.width / 2 - 15; // 半径
var d = 4; // 偏移量 气泡的移动距离

// 绘制小圆点 一共4个 掘金也是4个 角度可以自由发挥 这里根据掘金App的发散角度定义的
canvas.drawPoints(
    ui.PointMode.points,
    [
      Offset((r + d * animation2.value) * cos(pi - pi / 18 * 2),
          (r + d * animation2.value) * sin(pi - pi / 18 * 2)),
      Offset((r + d * animation2.value) * cos(pi + pi / 18 * 2),
          (r + d * animation2.value) * sin(pi + pi / 18 * 2)),
      Offset((r + d * animation2.value) * cos(pi * 1.5 - pi / 18),
          (r + d * animation2.value) * sin(pi * 1.5 - pi / 18)),
      Offset((r + d * animation2.value) * cos(pi * 1.5 + pi / 18 * 5),
          (r + d * animation2.value) * sin(pi * 1.5 + pi / 18 * 5)),
    ],
    
    _paint
      ..strokeWidth = 5
      ..color = Colors.blue
      ..strokeCap = StrokeCap.round);

得到现在的图形, 发散前

发散后

接下来继续我们来添加交互效果,添加动画,如果有看上一篇吃豆人,相信这里就很so easy了,首先创建两个动画类,控制小手和气泡,再创建两个变量,是否点赞和点赞数量,代码:

late Animation<double> animation; // 赞
late Animation<double> animation2; // 小圆点
ValueNotifier<bool> isZan = ValueNotifier(false); // 记录点赞状态 默认没点赞
ValueNotifier<int> zanNum = ValueNotifier(0); // 记录点赞数量 默认0点赞

这里我们需要使用动画曲线CurvedAnimation这个类,这个类可以实现不同的0-1的运动曲线,根据掘金的点赞效果,比较符合这个曲线规则,快速放大,然后回归正常大小,这个类帮我们实现了很多好玩的运动曲线,有兴趣的小伙伴可以尝试下其他运动曲线。

小手运动曲线

气泡运动曲线:

有了运动曲线之后,接下来我们只需将属性赋值给小手手和小圆点就好了

完整源码

封装一下,对外暴露大小,就是一个点赞组件了。

class ZanDemo extends StatefulWidget {
  const ZanDemo({Key? key}) : super(key: key);

  @override
  _ZanDemoState createState() => _ZanDemoState();
}

class _ZanDemoState extends State<ZanDemo> with TickerProviderStateMixin {
  late Animation<double> animation; // 赞
  late Animation<double> animation2; // 小圆点
  ValueNotifier<bool> isZan = ValueNotifier(false); // 记录点赞状态 默认没点赞
  ValueNotifier<int> zanNum = ValueNotifier(0); // 记录点赞数量 默认0点赞

  late AnimationController _controller; // 控制器
  late AnimationController _controller2; // 小圆点控制器
  late CurvedAnimation cure; // 动画运行的速度轨迹 速度的变化
  late CurvedAnimation cure2; // 动画运行的速度轨迹 速度的变化

  int time = 0;// 防止快速点两次赞导致取消赞

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
        vsync: this, duration: const Duration(milliseconds: 500)); //500ms
    _controller2 = AnimationController(
        vsync: this, duration: const Duration(milliseconds: 500)); //500ms

    cure = CurvedAnimation(parent: _controller, curve: Curves.easeInOutBack);
    cure2 = CurvedAnimation(parent: _controller2, curve: Curves.easeOutQuint);
    animation = Tween(begin: 0.0, end: 1.0).animate(cure);
    animation2 = Tween(begin: 0.0, end: 1.0).animate(_controller2);
  }

  @override
  Widget build(BuildContext context) {
    return InkWell(
      child: Center(
        child: CustomPaint(
          size: Size(50, 50),
          painter: _ZanPainter(animation, animation2, isZan, zanNum,
              Listenable.merge([animation, animation2, isZan, zanNum])),
        ),
      ),
      onTap: () {
        if (!isZan.value && !_isDoubleClick()) {
          _controller.forward(from: 0);
          // 延迟300ms弹窗气泡
          Timer(Duration(milliseconds: 300), () {
            isZan.value = true;
            _controller2.forward(from: 0);
          });
          Vibrate.feedback(FeedbackType.success);
          zanNum.value++;
        } else if (isZan.value) {
          Vibrate.feedback(FeedbackType.success);
          isZan.value = false;
          zanNum.value--;
        }
      },
    );
  }

  bool _isDoubleClick() {
    if (time == 0) {
      time = DateTime.now().microsecondsSinceEpoch;
      return false;
    } else {
      if (DateTime.now().microsecondsSinceEpoch - time < 800 * 1000) {
        return true;
      } else {
        time = DateTime.now().microsecondsSinceEpoch;
        return false;
      }
    }
  }
}

class _ZanPainter extends CustomPainter {
  Animation<double> animation;
  Animation<double> animation2;
  ValueNotifier<bool> isZan;
  ValueNotifier<int> zanNum;
  Listenable listenable;

  _ZanPainter(
      this.animation, this.animation2, this.isZan, this.zanNum, this.listenable)
      : super(repaint: listenable);

  Paint _paint = Paint()..color = Colors.blue;
  List<Offset> points = [];

  @override
  void paint(Canvas canvas, Size size) {
    canvas.clipRect(Offset.zero & size);
    canvas.translate(size.width / 2, size.height / 2);
    // 赞
    final icon =
        isZan.value ? Icons.thumb_up_alt_rounded : Icons.thumb_up_alt_outlined;
    // 通过TextPainter可以获取图标的尺寸
    TextPainter textPainter = TextPainter(
        text: TextSpan(
            text: String.fromCharCode(icon.codePoint),
            style: TextStyle(
                fontSize: animation.value < 0 ? 0 : animation.value * 30,
                fontFamily: icon.fontFamily,
                color: isZan.value ? Colors.blue : Colors.black)),
        textAlign: TextAlign.center,
        textDirection: TextDirection.ltr);
    textPainter.layout(); // 进行布局
    Size size2 = textPainter.size; // 尺寸必须在布局后获取
    //将图标偏移到画布中央
    textPainter.paint(canvas, Offset(-size2.width / 2, -size2.height / 2));

    var r = size.width / 2 - 15; // 半径
    var d = 4; // 偏移量

    canvas.drawPoints(
        ui.PointMode.points,
        [
          Offset((r + d * animation2.value) * cos(pi - pi / 18 * 2),
              (r + d * animation2.value) * sin(pi - pi / 18 * 2)),
          Offset((r + d * animation2.value) * cos(pi + pi / 18 * 2),
              (r + d * animation2.value) * sin(pi + pi / 18 * 2)),
          Offset((r + d * animation2.value) * cos(pi * 1.5 - pi / 18 * 1),
              (r + d * animation2.value) * sin(pi * 1.5 - pi / 18 * 1)),
          Offset((r + d * animation2.value) * cos(pi * 1.5 + pi / 18 * 5),
              (r + d * animation2.value) * sin(pi * 1.5 + pi / 18 * 5)),
        ],
        _paint
          ..strokeWidth = animation2.value < 1 ? 5 * animation2.value : 0
          ..color = Colors.blue
          ..strokeCap = StrokeCap.round);
    TextPainter textPainter2 = TextPainter(
        text: TextSpan(
            text: zanNum.value == 0 ? "点赞" : zanNum.value.toString(),
            style: TextStyle(
                fontSize: 9, fontWeight: FontWeight.w500, color: Colors.black)),
        textAlign: TextAlign.center,
        textDirection: TextDirection.ltr);
    textPainter2.layout(); // 进行布局
    // 向右上进行偏移在小手上面
    textPainter2.paint(canvas, Offset(size.width / 9, -size.height / 2 + 5));
  }

  @override
  bool shouldRepaint(covariant _ZanPainter oldDelegate) {
    return oldDelegate.listenable != listenable;
  }
}

到这里发现是不是少了点什么,不错,还少了震动的效果,这里我们引入flutter_vibrate: ^1.3.0这个插件,这个插件是用来管理设备震动效果的,Andoroid端记得加入震动权限

<uses-permission Android:name="android.permission.VIBRATE"/>使用方法也很简单,这个插件封装了一些常见的提示震动,比如操作成功、操作警告、操作失败等,其实就是震动时间的长短,这里我们就在点赞时候调用Vibrate.feedback(FeedbackType.success);有一个点击成功的震动就好了。

最后来看下最终效果图吧:

是不是和掘金App的效果一样,不信你点个赞看看~~

以上就是Android Flutter实现点赞效果的示例代码的详细内容,更多关于Flutter点赞的资料请关注编程网其它相关文章!

--结束END--

本文标题: AndroidFlutter实现点赞效果的示例代码

本文链接: https://www.lsjlt.com/news/146860.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • AndroidFlutter实现点赞效果的示例代码
    目录前言绘制小手完整源码前言 点赞这个动作不得不说在社交、短视频等App中实在是太常见了,当用户手指按下去的那一刻,给用户一个好的反馈效果也是非常重要的,这样用户点起赞来才会有一种强...
    99+
    2022-11-13
  • AndroidFlutter实现视频上滑翻页效果的示例代码
    目录前言PageView 组件介绍使用示例PageController 应用前言 我们在短视频应用中经常会看到不停上滑浏览下一条视频的沉浸式交互效果,这种交互能够让用户不停地翻页,直...
    99+
    2022-11-13
    Android Flutter上滑翻页效果 Flutter 上滑翻页 Android 翻页
  • Vue transition实现点赞动画效果的示例
    目录效果一览爱心效果 数字滚动动画 点赞动画 效果一览 爱心效果 材料:爱心图标两个,没有我这种 icon 组件的用 png 图片代替 <transition :nam...
    99+
    2022-11-12
  • 使用 Redis 缓存实现点赞和取消点赞的示例代码
    点赞功能是很多平台都会提供的一个功能,那么,我们要如何实现点赞和取消点赞呢? 这篇文章总结了我在项目中实现点赞的方法。 缓存 vs 数据库? 首先我们要考虑的是数据要放到哪里,很多...
    99+
    2022-11-11
  • AndroidFlutter实现3D动画效果示例详解
    目录前言AnimatedWidget 简介3D 旋转动画的实现总结前言 上一篇我们介绍了 Animation 和 AnimationController...
    99+
    2022-11-13
  • 利用Redis实现点赞功能的示例代码
    目录mysql 和 Redis优缺点1、Redis 缓存设计及实现部分代码如下Redis 存储结构如图2、数据库设计3、开启定时任务持久化存储到数据库部分代码如下提到点赞,大家一想到的是不是就是朋友圈的点赞呀?其实点赞对...
    99+
    2022-06-28
    Redis 点赞功能 Redis 点赞
  • Django点赞的实现示例
    目录1.前期准备2.html实现3.js实现【!!!注意这段代码写在for循环之内】4.css实现1.前期准备 用户models.py class User(models.Model...
    99+
    2022-11-13
  • AndroidFlutter实现上拉加载组件的示例代码
    前言 在此之前对列表下拉刷新做了调整方案,具体介绍可以阅读下拉刷新组件交互调整。既然列表有下拉刷新外当然还有上拉加载更多操作了,本次就来介绍如何为列表增加上拉加载更多的交互实现。 实...
    99+
    2022-11-13
  • Python实现屏幕代码雨效果的示例代码
    直接上代码 import pygame import random def main(): # 初始化pygame pygame.init() #...
    99+
    2022-11-13
  • 使用Redis实现点赞取消点赞的详细代码
    前言 异步实现 代码实现: private void like(long userId,int type,int textId,long entityUserId){ ...
    99+
    2022-11-13
  • Android实现仿今日头条点赞动画效果实例
    目录一、前言二、需求拆分三、实现方案1、点赞控件触摸事件处理2、点赞动画的实现2.1、点赞效果图片的获取和存储管理2.2、点赞表情图标动画实现2.3、点赞次数和点赞文案的绘制3、存放...
    99+
    2022-11-13
  • JavaScript实现流星雨效果的示例代码
    目录演示技术栈源码首先建立星星对象让星星闪亮起来创建流星雨对象让流星动起来演示 上一次做了一个雨滴的动画,顺着这种思维正好可以改成流星雨,嘿嘿我真是一个小机灵。 技术栈 还是先建立...
    99+
    2022-11-13
  • Unity实现跑马灯效果的示例代码
    目录一、效果二、需要动画插件DOTween三、脚本1.每个格子上的脚本文件2.管理脚本文件一、效果 二、需要动画插件DOTween 下载地址 三、脚本 1.每个格子上的脚本文件 u...
    99+
    2022-11-13
  • C#实现跑马灯效果的示例代码
    目录文章描述开发环境开发工具实现代码实现效果文章描述 跑马灯效果,功能效果大家应该都知道,就是当我们的文字过长,整个页面放不下的时候(一般用于公告等),可以让它自动实现来回滚动,以让...
    99+
    2022-11-13
    C#实现跑马灯效果 C# 跑马灯
  • jquery实现div阴影效果示例代码
    复制代码 代码如下: <html> <head> <style> .mydiv1 {height:250px;width:250px;border...
    99+
    2022-11-15
    jquery div阴影
  • 基于Python实现烟花效果的示例代码
    python烟花代码 如下 # -*- coding: utf-8 -*- import math, random,time import threading import tki...
    99+
    2022-11-13
  • Python实现图像去雾效果的示例代码
    目录修改部分训练测试数据集下载地址修改部分 我利用该代码进行了去雾任务,并对原始代码进行了增删,去掉了人脸提取并对提取人脸美化的部分,如下图 增改了一些数据处理代码,Create_...
    99+
    2022-11-13
  • android实现音乐跳动效果的示例代码
    效果图 实现 整体的流程图如下 上面主要步骤分为3个 1、计算宽度能放下多少列的音频块。 2、计算每一列中音频块的个数 3、绘制音频块 1、计算宽度能放下多少列的音频块。 ...
    99+
    2022-11-12
  • Qt实现字幕滚动效果的示例代码
    目录一、项目介绍二、项目基本配置三、UI界面设计四、主程序实现4.1 widget.h头文件4.2 widget.cpp源文件五、效果演示一、项目介绍 利用QTimer实现字幕滚动功...
    99+
    2022-11-13
  • JavaScript实现扯网动画效果的示例代码
    目录演示技术栈源码css控制js部分演示 技术栈 JavaScript prototype(原型对象): 所有的 JavaScript 对象都会从一个 prototype(原型对象...
    99+
    2022-11-13
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作