flutter 仿抖音的进场特效效果

从右边平移到最左边,停留两秒再向左移出屏幕
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()),
    );
  }
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很不错的一款直播短视频源码,支持二次开发原生短视频app源码 1.登录注册:支持手机验证码注册登录,QQ、微信、Facebook、Twitter一键登录及分享,登录后需要进行手机验证; 2.首页列表:首页展示热门直播、附近直播、关注主播列表,可在后台进行直播间推荐置顶操作。直播间封面显示主播名称、直播状态、房间类型、房间标题等,附近直播列表显示主播距离; 3.搜索功能:可根据主播名称和主播ID进行主播搜索,并添加关注; 4.每日签到:用户每日签到送礼,可收到系统赠送的金币奖励; 5.互动直播:开播前可进行封面图编辑上传,添加直播标题,选择直播房间类型,包含密码房间、计时收费房间、门票房间等,进行美颜设置,开播位置定位等; 6.美颜滤镜:全局美颜功能,美肤美形,可实现十级美颜调节,20余款不同风格的滤镜素材,支持50余款动态贴纸素材,可用于直播间和小视频拍摄编辑; 7.送礼打赏:在线送礼打赏,礼物可实现多重连发,支持豪华礼物定制,新增热门礼物、守护礼物、幸运礼物等礼物类别; 8.弹幕私信:直播间内可发送弹幕消息,也可以给主播和其他联系人发送私信进行聊天; 9.排行榜单:可在排行榜单查看主播收益排行及用户打赏排行信息; 10.用户中心:可进行用户基础信息设置,查看各类系统功能选项菜单; 特色功能 1.互动连麦:主播开播后,用户可向主播发起互动连麦请求,主播接受请求后进行连麦互动; 2.互动游戏:主播可在直播间内开启互动小游戏,丰富互动直播玩法,系统支持5款小游戏; 3.创建家族:上传认证资料即可创建家族,也可加入已有的家族,分享家族主播礼物收益; 4.在线商城:可充值购买VIP会员,购买靓号及坐骑,用户进入直播间时会有进场特效; 5.三级分销:单独的直播间分享邀请码和推广二维码图片,分享直播获取礼物分成收益; 7.私密直播:支持普通直播房间、密码房间、门票房间、计时收费房间等私密直播间类型; 8.连麦PK:主播可搜索当前在线主播,发起连麦礼物PK邀请,对方接受邀请后可进行连麦PK; 9.主播守护:直播间可充值守护主播,守护时长后台可进行自定义设置,开通守护会有守护礼物; 10.直播间红包:主播和用户都可以在直播间内发送红包,红包分为普通红包和随机红包; 11.代理推广:单独的代理商和推广员管理后台,可设置三者之间的佣金分成方式; 12.引导图功能:后台可上传图片或视频内容作为引导图,视频内容可跳过,点击显示广告内容; 13.数据统计:后台首页显示平台运营数据内容,需申请接入三方数据统计服务; 源码包括;安卓app+苹果app带后台 pc后端管理:thinkphp 安卓:java原生开发 ios:obje-ctive-c开发 源码完整,搭建过于复杂,所以站长没有测试 如果有需要的朋友可以自行下载测试 如果自己没有技术的话不建议自己搭建!
基于 Flutter 3.x 进行仿跨平台混合开发,是一种使用最新版本的Flutter框架来开发类似的应用程序的方法。 Flutter是一种跨平台的开发框架,可以让开发者使用同一套代码同时在iOS和Android平台上构建高性能的应用程序。它提供了丰富的UI组件和工具,使开发过程更加简单和高效。 要实现仿的跨平台混合开发,首先需要对的功能和界面进行分析和设计。然后,使用Flutter框架来实现这些功能和界面。 Flutter提供了丰富的UI组件,如按钮、文本、图片、视频播放等,可以用于构建的各种界面元素。开发者可以使用Dart语言来编写业务逻辑,同时可以使用Flutter提供的hot reload功能实时预览和调试应用程序。 为了实现跨平台开发,开发者可以使用Flutter的多平台支持。Flutter可以生成原生的iOS和Android应用程序,以及Web和桌面应用程序。这使得开发者可以在不同的平台上发布和部署仿的应用程序。 在开发过程中,开发者可以使用Flutter的插件来集成各种第三方功能和服务,如视频播放、数据存储、社交分享等。这些插件可以帮助开发者更加方便地实现仿的各种功能。 总之,基于Flutter 3.x进行仿跨平台混合开发,是一种高效、灵活的开发方法。通过使用Flutter框架和相关工具,开发者可以快速构建出功能完善、界面精美的仿应用程序,并在多个平台上进行发布和部署。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值