从右边平移到最左边,停留两秒再向左移出屏幕
1.这个要通过eventBus通知,收到通知就往列表添加数据
2.这个要做一个队列,显示完一个(删除)再显示下一个
3.添加数据的时候就调开始执行动画;一个动画执行完后就开始调下一个的开始执行动画
4.如果动画正在播放,就不允许调开始执行动画,防止上面两个地方让队列混乱
5.因为是多个动画,使用到TickerProviderStateMixin
6.创建两个控制器和动画
示例代码
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import '../../../common/avatar.dart';
import '../../../common/globalEventBus.dart';
import '../../../models/group/groupUserBean.dart';
import '../../../utils/printUtil.dart';
class DirectSeedingEnterRoomAnimation extends StatefulWidget {
const DirectSeedingEnterRoomAnimation({Key? key}) : super(key: key);
@override
State<StatefulWidget> createState() => DirectSeedingEnterRoomAnimationState();
}
class DirectSeedingEnterRoomAnimationState
extends State<DirectSeedingEnterRoomAnimation>
with TickerProviderStateMixin {
String tip = '进场动画效果----';
//动画控制器,可控制前进后退,重置
late AnimationController _controllerOne;
//第二个动画控制器
late AnimationController _controllerTwo;
//用于引导第一个动画:从右到左
late Animation<Offset> _animationOne;
//用于引导第二个动画:向左滑动
late Animation<Offset> _animationTwo;
late StreamSubscription<LiveEnterRoomAnimation>? _liveEnterRoomAnimation;
//是否播放第一个动画
bool firstAnimation = true;
//动画显示的队列
List<GroupUserBean> userEnterList = [];
//默认是空闲false,有任务就是true
bool animationState = false;
@override
void initState() {
//第一个动画:从右往左执行,动画的时间
_controllerOne = AnimationController(
duration: const Duration(milliseconds: 500), vsync: this);
_controllerTwo = AnimationController(
duration: const Duration(milliseconds: 1000), vsync: this);
_animationOne = Tween(begin: const Offset(2.5, 0), end: const Offset(0, 0))
.animate(_controllerOne);
//第二个动画:向左滑出去
_animationTwo = Tween(begin: const Offset(0, 0), end: const Offset(-2.5, 0))
.animate(_controllerTwo);
//第一个动画监听
_animationOne.addListener(() {
if (_animationOne.status == AnimationStatus.completed) {
//第一个动画结束
PrintUtil.prints('$tip 第一个动画结束');
//播放第二个动画
firstAnimation = false;
if (_controllerTwo.isCompleted || _controllerTwo.isDismissed) {
Future.delayed(const Duration(seconds: 2), () {
_controllerTwo.forward();
PrintUtil.prints('$tip 第二个动画开始');
});
}
mySetState(() {});
} else if (_animationOne.status == AnimationStatus.forward) {
PrintUtil.prints('$tip 第一个动画进行中');
//第一个动画进行中
animationState = true;
//动画开始,任务状态为忙碌
mySetState(() {});
}
});
//第二个动画监听
_animationTwo.addListener(() {
if (_animationTwo.status == AnimationStatus.completed) {
//第二个动画结束,重置位置
PrintUtil.prints('$tip 第二个动画结束');
//回到第一个动画
firstAnimation = true;
_controllerOne.reset();
_controllerTwo.reset();
//移除第一个数据
if (userEnterList.isNotEmpty) {
userEnterList.removeAt(0);
}
//任务闲下来了
animationState = false;
//展示下一个数据
showAnimation();
mySetState(() {});
} else if (_animationTwo.status == AnimationStatus.forward) {
PrintUtil.prints('$tip 第二个动画进行中');
//动画开始,任务状态为忙碌
mySetState(() {});
}
});
_liveEnterRoomAnimation = EventBusUtil.listen((event) {
//收到一个就添加一个数据,然后开始展示
userEnterList.add(event.userInfo);
PrintUtil.prints('$tip 收到event数据${userEnterList.length}');
showAnimation();
});
super.initState();
}
//取第一个数据展示,第二个动画播放完就删除数据
showAnimation() {
if (userEnterList.isNotEmpty) {
PrintUtil.prints('$tip 展示数据时当前状态是$animationState false是空闲');
//空闲状态才给播放,防止添加数据是播放和播放完又调这个方法这两个混着了
if (animationState == false) {
if (_controllerOne.isCompleted || _controllerOne.isDismissed) {
mySetState(() {});
Future.delayed(Duration.zero, () {
_controllerOne.forward();
PrintUtil.prints('$tip 第一个动画启动 ${userEnterList.length}');
});
}
}
}
}
getAvatar() {
String avatar = '';
if (userEnterList.isNotEmpty) {
if (userEnterList[0].avatarThumb != null) {
avatar = userEnterList[0].avatarThumb ?? '';
}
}
return avatar;
}
getNickName() {
String name = '';
if (userEnterList.isNotEmpty) {
if (userEnterList[0].nickName != null) {
name = userEnterList[0].nickName ?? '';
}
}
return name;
}
//判断是否显示动画
canShowAnimation() {
bool show = false;
if (getAvatar() != '' || getNickName() != '') {
show = true;
}
return show;
}
mySetState(callBack) {
if (mounted) {
setState(() {
callBack();
});
}
}
@override
void dispose() {
_controllerOne.dispose();
_controllerTwo.dispose();
if (_liveEnterRoomAnimation != null) {
_liveEnterRoomAnimation!.cancel();
}
super.dispose();
}
@override
Widget build(BuildContext context) {
return Positioned(
bottom: 300.r,
left: 0,
child: SlideTransition(
position: firstAnimation ? _animationOne : _animationTwo,
child: canShowAnimation()
? Container(
padding: const EdgeInsets.only(
left: 8, right: 8, top: 3, bottom: 3),
decoration: BoxDecoration(
color: Colors.grey,
borderRadius: BorderRadius.circular(50)),
child: Row(
children: [
Avatar(
width: 25,
height: 25,
avatarUrl: getAvatar(),
radius: 50),
LimitedBox(
maxWidth: 130,
child: Text(
'${getNickName()}',
style: const TextStyle(
color: Colors.black, fontSize: 15),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
),
Container(
margin: const EdgeInsets.only(left: 5),
child: const Text(
'进入直播间1',
style: TextStyle(color: Colors.black, fontSize: 15),
),
)
],
),
)
: Container()),
);
}
}