Flutter仿掘金点赞效果

老孟导读:今天分享一下如何实现掘金点赞效果,这不仅仅是一篇技术文章,还是一篇解决问题思路的文章,遇到一个需求时,如何拆分需求,然后一步一步实现,这个过程比单纯的技术(此文)更有含金量。
先来看一下掘金点赞的效果:

说点题外话,感谢一下二哥(沉默王二 ),给了我很多建议和帮助,公众号搜索沉默王二即可关注。
遇到组合动画效果时,首先拆分一下这个动画,以掘金点赞效果为例,共分为3个动画效果:
- 小手图标改变颜色并且缩放一下。
- 圆环由粗变细,透明度逐渐变为0。
- 最外圈小点点透明度逐渐变为0。
拆分好了之后,就一步一步实现其效果。
小手缩放效果
小手缩放效果需要2个图标,选中和未选中两种状态,我从阿里的图标库中选了2个类似的图标(未找到一摸一样的)。两种状态的图标定义如下:
///
/// 未点赞icon
///
const Icon _unLikeIcon = Icon(
IconData(0xe60a, fontFamily: 'appIconFonts'),
);
///
/// 点赞icon
///
const Icon _likeIcon = Icon(
IconData(0xe60c, fontFamily: 'appIconFonts'),
color: Color(0xFF1afa29),
);
关于如何使用阿里的图标库中的图标可以查看此文章。
由于小手图标的动画效果是放大->还原,使用组合动画实现其效果,代码如下:
@override
initState() {
_animationController =
AnimationController(duration: Duration(milliseconds: 300), vsync: this);
_iconAnimation = Tween(begin: 1.0, end: 1.3).animate(_animationController);
_iconAnimation = TweenSequence([
TweenSequenceItem(
tween: Tween(begin: 1.0, end: 1.3)
.chain(CurveTween(curve: Curves.easeIn)),
weight: 50),
TweenSequenceItem(tween: Tween(begin: 1.3, end: 1.0), weight: 50),
]).animate(_animationController);
}
@override
Widget build(BuildContext context) {
return _buildLikeIcon();
}
_buildLikeIcon() {
return ScaleTransition(
scale: _iconAnimation,
child: widget.like
? IconButton(
padding: EdgeInsets.all(0),
icon: _likeIcon,
onPressed: () {
_clickIcon();
},
)
: IconButton(
padding: EdgeInsets.all(0),
icon: _unLikeIcon,
onPressed: () {
_clickIcon();
},
),
);
}
添加按钮点击效果:
_clickIcon() {
if (_iconAnimation.status == AnimationStatus.forward ||
_iconAnimation.status == AnimationStatus.reverse) {
return;
}
setState(() {
widget.like = !widget.like;
});
if (_iconAnimation.status == AnimationStatus.dismissed) {
_animationController.forward();
} else if (_iconAnimation.status == AnimationStatus.completed) {
_animationController.reverse();
}
}

圆环动画
圆环的动画效果是线条宽度逐渐变为0,透明度逐渐变为0,相对简单,使用AnimatedBuilder实现:
_buildCircle() {
return !widget.like
? Container()
: AnimatedBuilder(
animation: _circleAnimation,
builder: (BuildContext context, Widget child) {
return Container(
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
color: Color(0xFF5FA0EC)
.withOpacity(_circleAnimation.value),
width: _circleAnimation.value * 8)),
);
},
);
}
定义_circleAnimation:
_circleAnimation =
Tween(begin: 1.0, end: 0.0).animate(_animationController);

最外圈小点点
最外圈的小点点动画效果是最简单的,透明度逐渐变为0,但布局相对复杂,围绕小手形成一个圆形,使用Flow实现此布局,Flow是一个非常酷炫的布局组件,更多用法查看此文。
构建单个小圆点
_buildCirclePoint(double radius, Color color) {
return !widget.like
? Container()
: AnimatedBuilder(
animation: _circleAnimation,
builder: (BuildContext context, Widget child) {
return Container(
width: radius,
height: radius,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: color.withOpacity(_circleAnimation.value)),
);
},
);
}
构建围绕小手的多个点:
_buildCirclePoints() {
return Flow(
delegate: CirclePointFlowDelegate(),
children: <Widget>[
_buildCirclePoint(2, Color(0xFF97B1CE)),
_buildCirclePoint(5, Color(0xFF4AC6B7)),
_buildCirclePoint(2, Color(0xFF97B1CE)),
_buildCirclePoint(5, Color(0xFF4AC6B7)),
_buildCirclePoint(2, Color(0xFF97B1CE)),
_buildCirclePoint(5, Color(0xFF4AC6B7)),
_buildCirclePoint(2, Color(0xFF97B1CE)),
_buildCirclePoint(5, Color(0xFF4AC6B7)),
_buildCirclePoint(2, Color(0xFF97B1CE)),
_buildCirclePoint(5, Color(0xFF4AC6B7)),
_buildCirclePoint(2, Color(0xFF97B1CE)),
_buildCirclePoint(5, Color(0xFF4AC6B7)),
_buildCirclePoint(2, Color(0xFF97B1CE)),
_buildCirclePoint(5, Color(0xFF4AC6B7)),
_buildCirclePoint(2, Color(0xFF97B1CE)),
_buildCirclePoint(5, Color(0xFF4AC6B7)),
],
);
}
CirclePointFlowDelegate 定义如下:
class CirclePointFlowDelegate extends FlowDelegate {
CirclePointFlowDelegate();
@override
void paintChildren(FlowPaintingContext context) {
var radius = min(context.size.width, context.size.height) / 2.0;
//中心点
double rx = radius;
double ry = radius;
for (int i = 0; i < context.childCount; i++) {
if (i % 2 == 0) {
double x =
rx + (radius - 5) * cos(i * 2 * pi / (context.childCount - 1));
double y =
ry + (radius - 5) * sin(i * 2 * pi / (context.childCount - 1));
context.paintChild(i, transform: Matrix4.translationValues(x, y, 0));
} else {
double x = rx +
(radius - 5) *
cos((i - 1) * 2 * pi / (context.childCount - 1) +
2 * pi / ((context.childCount - 1) * 3));
double y = ry +
(radius - 5) *
sin((i - 1) * 2 * pi / (context.childCount - 1) +
2 * pi / ((context.childCount - 1) * 3));
context.paintChild(i, transform: Matrix4.translationValues(x, y, 0));
}
}
}
@override
bool shouldRepaint(FlowDelegate oldDelegate) => true;
}

交流
老孟Flutter博客地址(近200个控件用法):http://laomengit.com
欢迎加入Flutter交流群(微信:laomengit)、关注公众号【老孟Flutter】:
![]() |
![]() |
Flutter仿掘金点赞效果的更多相关文章
- Android -- 自定义ViewGroup+贝塞尔+属性动画实现仿QQ点赞效果
1,昨天我们写了篇简单的贝塞尔曲线的应用,今天和大家一起写一个QQ名片上常用的给别人点赞的效果,实现效果图如下: 红心的图片比较丑,见谅见谅(哈哈哈哈哈哈).... 2,实现的思路和原理 从上面的效果 ...
- Android -- 《 最美有物》好看的点赞效果
1,前天在鸿洋的公众号上看到一款不错的点赞效果,是仿最美有物的点赞,再加上自己最近学习状态很差,自己想着通过这个效果练手一下,果然,花了整整两天的时间,按照以前的效率的话一天就够了,哎,已经调整了一个 ...
- Flutter 仿滴滴出行App
绿色出行 Flutter 仿滴滴出行App 地图:采用高德地图,仅简单完成了部分功能,基础地图,地址检索,逆地理编码. 界面:仿滴滴主界面,地图中心请求动效果,服务tabs展开效果,地址检索界面,城市 ...
- Android仿IOS回弹效果 ScrollView回弹 总结
Android仿IOS回弹效果 ScrollView回弹 总结 应项目中的需求 须要仿IOS 下拉回弹的效果 , 我在网上搜了非常多 大多数都是拿scrollview 改吧改吧 试了一些 发现总 ...
- PHP实现仿Google分页效果的分页函数
本文实例讲述了PHP实现仿Google分页效果的分页函数.分享给大家供大家参考.具体如下: /** * 分页函数 * @param int $total 总页数 * @param int $pages ...
- Android类似Periscope点赞效果
原文 https://github.com/AlanCheen/PeriscopeLayout 主题 安卓开发 PeriscopeLayout A layout with animation li ...
- 原生wcPop.js消息提示框(移动端)、内含仿微信弹窗效果
wcPop.js移动端消息对话框插件是之前的wxPop.js的升级版,优化了js和css,并且新增了仿微信弹窗效果, 是一款含有多种情景模式的原生模态消息对话框代码,可用于替代浏览器默认的alert弹 ...
- EF+LINQ事物处理 C# 使用NLog记录日志入门操作 ASP.NET MVC多语言 仿微软网站效果(转) 详解C#特性和反射(一) c# API接受图片文件以Base64格式上传图片 .NET读取json数据并绑定到对象
EF+LINQ事物处理 在使用EF的情况下,怎么进行事务的处理,来减少数据操作时的失误,比如重复插入数据等等这些问题,这都是经常会遇到的一些问题 但是如果是我有多个站点,然后存在同类型的角色去操作 ...
- jquery 仿windows10菜单效果下载
http://www.kuitao8.com/20150923/4079.shtml jquery 仿windows10菜单效果下载
随机推荐
- php正则验证手机、邮箱
//验证电话private function reg_phone($phone){ if (preg_match("/^13[0-9]{1}[0-9]{8}$|15[0189] ...
- Python 输出 log 到文件的方法
import loggingfrom logging.handlers import RotatingFileHandler module_name = "test_module" ...
- [Qt] QString 常用函数
1. append(), prepend() 2. count(), size(), length() 这三个函数是相同的 3. trimmed() 去掉首尾空格 4. isNull() 对未赋值的字 ...
- Python与Excel交互——Xlwings实战
这一期直接来实战. 比如说,我们在一个快递网站上爬取了几个快递的轨迹信息,我们需要将数据保存下来,一个常规做法是把数据保存在数据库里(Mysql,MongoDB,Redis),另一个是用Excel的形 ...
- Omnicore RPC API中文文档
2019独角兽企业重金招聘Python工程师标准>>> OmniCore是比特币核心的一个分支,它在比特币协议之上实现了一个新的Omni协议层,用于代币发行.众售等应用,USDT就是 ...
- 使用mvp+rxjava+retrofit加载数据
将mvp和rxjava和retrofit简单整合的列子,让activity的代码不再那么臃肿,只负责显示数据. 关于mvp的介绍可以看这里, 关于Rxjava的介绍可以看这里; 关于retrofit的 ...
- 【阅读笔记】Ranking Relevance in Yahoo Search (三)—— query rewriting
5. QUERY REWRITING 作用: query rewriting is the task of altering a given query so that it will get bet ...
- 使用PHP-Beast加密你的PHP源代码
PHP-Beast是一个PHP源码加密的模块,其使用DES算法加密,用户可以自定义加密的key来加密源代码. 1. PHP-Beast的安装 $ wget https://github.com/lie ...
- 洛谷 P1816 忠诚 ST函数
题目描述 老管家是一个聪明能干的人.他为财主工作了整整10年,财主为了让自已账目更加清楚.要求管家每天记k次账,由于管家聪明能干,因而管家总是让财主十分满意.但是由于一些人的挑拨,财主还是对管家产生了 ...
- 线程池(Java中有哪些方法获取多线程)
线程池(Java中有哪些方法获取多线程) 前言 获取多线程的方法,我们都知道有三种,还有一种是实现Callable接口 实现Runnable接口 实现Callable接口 实例化Thread类 使用线 ...

