1. 要实现先要了解的知识 :
(0)flutter 常用组件的使用
(1)flutter 动画
(2) flutter 控件位置及大小如何获取
动画:
这里只随口一说里边用到的, 详细的flutter动画介绍后面会再整理。
(1)AnimationController: 动画的控制: 控制动画的开始,介绍,时长等。
(2)Animation: 动画, 可设置监听动画的状态变化监听。 通过 animation.value 来获取动画的值。
控件位置及大小的获取:
一句话--> 通过 BuildContext , 能拿到BuildContext就可以拿到控件相关的位置、大小等信息。
至于一个小组件的context 如果取? 一般我了解的有2种,
a. 一个是提取一个小组件为自定义widget,build里边的context即是我们所需要的。
b. 另外一个相对简单的就是直接通过 GlobalKey 的 .currentContext 来拿到。
key.currentContext的方式:
RenderBox renderBox = spKey.currentContext.findRenderObject();
// 这个就是控件的大小 size.width, size.height
Size size = renderBox.size;
// offset 就是坐标相关的 offset.dx, offset.dy
Offset offset = renderBox.localToGlobal(Offset(renderBox.size.width * 0.5, renderBox.size.height * 0.5)); // 转化为全局左边, 这样更容易我们后边的处理。
通过提取Widget的方式:
相对于上边的3行代码, 就第一行不一样,RenderBox renderBox = context.findRenderObject(); // context 就是widget 中的BuildContext。
2. 思路
效果是 从点击的位置 在一小段时间内 将一个图片or红点or XXX 以一个曲线的方式移动到购物车中,来给人更真实的 “鸡蛋放到框里”的感觉。
我们知道这个移动的红点(这里就用红点了,你可以移动任何东西)移动的过程中是要跨越很多其他widget的,比如从列表第一个移动跨越整个列表到列表右下角悬浮的一个“购物车” , 或者更复杂点的,要移动到下边主菜单(Tabbar)中的购物车。所以, 我们完成移动的画布就需要是 “屏幕级”的了。 那我们就用 Stack ,用Stack在原有的页面上叠加一个用户看不到的“页面”, 我们的效果动画就在这个叠加的Widget上去完成。
动画的实现: (1)获取点击的按钮的位置a, (2)获取“购物车”的位置b 这样我们要做的工作就变成了, 将一个红点按动画的方式 从位置a移动到位置b, 这样问题就简单了 。 a --> b
3. 实现(主要代码)
(1)商品列表加入购物车主要代码的实现
Stack(
children: <Widget>[
Container(
child: ListView(
children: goodsList.map((item) {
return GoodsItem(
item: item,
addToShoppingCart: (o) {
count++;
setState(() {
goodsOffset = o;
});
},
);
}).toList()),
),
// 这个就是我们要做动画移动的“浮层”页面
AddAnimationContainer(
startOffset: goodsOffset, // 点击的商品位置
endOffset: spOffset, // 购物车位置
topHeight: topHeight, // 这个看实际情况,这里是顶部appBar的高度,我们做动画的时候计算用的 (全局坐标- 这个)
),
],
),
(2)移动动画实现
double top = 0; // 要移动的红点距离顶部的距离
double left = 0; // 要移动的红点距离左边的距离
AnimationController _controller;
Animation _animation;
@override
void initState() {
super.initState();
_controller =
AnimationController(duration: Duration(milliseconds: 800), vsync: this);
_animation = Tween(begin: 0, end: 1.0).animate(_controller)
..addListener(() {
setState(() {});
})
..addStatusListener((AnimationStatus status) { // 动画状态的监听
if (status == AnimationStatus.completed) { // 动画完成以后 通知上层回调
if (widget.onEnd != null) {
widget.onEnd(this.widget);
}
}
});
WidgetsBinding.instance.addPostFrameCallback((_) { // 这个回调是在组件绘制完成后第一个调用的,且只调用一次, 这里做开启动画的动作
_controller.forward();
});
}
@override
void dispose() {
// 一定不要忘记做 销毁 释放
_controller.dispose();
_animation = null;
_controller = null;
super.dispose();
}
@override
Widget build(BuildContext context) {
// 1\. 开始的坐标信息
var startX = widget.startOffset.dx;
var startY = widget.startOffset.dy;
// 2\. 结束点的坐标信息
var endX = widget.endOffset.dx;
var endY = widget.endOffset.dy;
// 3\. 动画值变化过程中计算 红点的 实时位置
var x = startX + (endX - startX) * _animation.value;
var y = startY + (endY - startY) * _animation.value;
// 4\. 根据实时位置来确定组件的实际位置
top = y;
left = x;
return Container(
child: Positioned(
top: top - widget.topHeight,
left: left,
child: Icon( // 这 就是 所说的要移动的 “红点” 组件,,,可以任意定义
Icons.arrow_drop_down_circle,
color: Colors.red,
size: 18,
),
),
);
}
4. 全部代码
下载地址一:
https://download.csdn.net/download/chwei_cson/12032825
下载地址二:
https://github.com/chweiGitHub/flutter_demo.git