Flutter实现动画列表AnimateListView

由于业务需要,在打开列表时,列表项需要一个从右边飞入的动画效果,故封装一个专门可以执行动画的列表组件,可以自定义自己的动画,内置有水平滑动,缩放等简单动画。花里胡哨的动画效果由你自己来定制吧。

功能:

1.可自定义动画。

2.内置水平滑动和缩放动画。

演示:

代码:

import 'dart:math';

import 'package:flutter/cupertino.dart';
import 'package:get/get.dart';

///可设置动画的列表。
///内部使用的是[SingleChildScrollView]实现,
///需要外层设置定义可约束高度。
class KqAnimateListView<T> extends StatefulWidget {
  ///数据
  final List<T> data;

  ///列表
  final Widget Function(T t) item;

  ///动画自定义
  final IAnimate? animate;

  ///是否需要每次刷新时都执行动画
  final bool isNeedFlashEveryTime;

  const KqAnimateListView({
    super.key,
    required this.item,
    required this.data,
    this.animate,
    this.isNeedFlashEveryTime = false,
  });

  @override
  State<StatefulWidget> createState() => _KqAnimateListViewState<T>();
}

class _KqAnimateListViewState<T> extends State<KqAnimateListView<T>>
    with TickerProviderStateMixin {
  late IAnimate animate;
  late AnimationController controller;
  late Animation animation;

  @override
  void initState() {
    super.initState();
    animate = widget.animate ?? ScaleAnimate();
    animate.init(widget.data.length);
    controller = animate.getAnimationController(this);
    animation = animate.getAnimation(controller, this);
    //启动动画(正向执行)
    controller.forward();
  }

  @override
  void didUpdateWidget(covariant KqAnimateListView<T> oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (widget.isNeedFlashEveryTime) {
      animate = widget.animate ?? ScaleAnimate();
      animate.init(widget.data.length);
      controller = animate.getAnimationController(this);
      animation = animate.getAnimation(controller, this);
      //启动动画(正向执行)
      controller.forward();
    }
  }

  @override
  Widget build(BuildContext context) {
    return SizedBox.expand(
      child: SingleChildScrollView(
        child: Column(
          children: _getChildren(context),
        ),
      ),
    );
  }

  List<Widget> _getChildren(BuildContext context) {
    List<Widget> list = [];
    for (int i = 0; i < widget.data.length; i++) {
      Widget child = widget.item.call(widget.data[i]);
      list.add(
        animate.animate(context, i, child, animation, controller),
      );
    }
    return list;
  }

  @override
  void dispose() {
    controller.dispose();
    super.dispose();
  }
}

///动画抽象类。
///实现该类,定制自己的列表动画。
abstract class IAnimate {
  ///初始化
  ///[length] 列表长度
  void init(int length);

  ///获取AnimationController
  AnimationController getAnimationController(TickerProvider provider);

  ///获取Animation
  Animation getAnimation(
      AnimationController controller, State<StatefulWidget> state);

  ///定制自己的动画,每一个item都会调用到animate,
  ///当需要差异动画时需要根据[index]计算不同item的动画时机
  ///[widget] 执行动画之后的widget
  ///[index] 列表的item的index
  Widget animate(
    BuildContext context,
    int index,
    Widget widget,
    Animation animation,
    AnimationController controller,
  );
}

///水平移动动画
class HorizontalAnimate extends IAnimate {
  ///动画执行时长比例
  static double rate = 0.4;

  ///单个动画执行长度
  static double baseGap = 1000.0;

  ///两个个动画执行间隔长度
  static double twoGap = 200.0;

  ///动画执行的总长度,根据列表长度动态计算值
  late double gap;

  ///动画是从左边还是右边开始执行
  HorizontalAnimateDirection direction;

  HorizontalAnimate({this.direction = HorizontalAnimateDirection.right});

  @override
  void init(int length) {
    gap = length * twoGap + baseGap;
  }

  @override
  Widget animate(BuildContext context, int index, Widget widget,
      Animation animation, AnimationController controller) {
    double width = context.width;

    ///范围0->1
    double mix = (animation.value - twoGap * index) / baseGap;
    if (mix > 1) {
      mix = 1;
    }
    double left = width * (1 - mix);
    return Container(
      transform: Matrix4.translationValues(
          direction == HorizontalAnimateDirection.left ? -left : left, 0, 0),
      child: widget,
    );
  }

  @override
  AnimationController getAnimationController(TickerProvider provider) {
    return AnimationController(
        duration: Duration(milliseconds: (gap * rate).toInt()),
        vsync: provider);
  }

  @override
  Animation getAnimation(
      AnimationController controller, State<StatefulWidget> state) {
    return Tween(begin: 0.0, end: gap).animate(controller)
      ..addListener(() {
        if (state.mounted) {
          state.setState(() {});
        }
      });
  }
}

enum HorizontalAnimateDirection {
  ///左边
  left,

  ///右边
  right;
}

///缩放动画
class ScaleAnimate extends IAnimate {
  ///动画执行时长比例
  static double rate = 0.4;

  ///单个动画执行长度
  static double baseGap = 1000.0;

  ///两个个动画执行间隔长度
  static double twoGap = 200.0;

  ///动画执行的总长度,根据列表长度动态计算值
  late double gap;

  @override
  Widget animate(BuildContext context, int index, Widget widget,
      Animation animation, AnimationController controller) {
    double width = context.width;

    ///范围0.5->1
    double mix = ((animation.value - twoGap * index) / baseGap + 1) / 2;
    if (mix > 1) {
      mix = 1;
    }
    double widthMix = width * mix;
    return SizedBox(
      width: max(0, widthMix),
      child: widget,
    );
  }

  @override
  AnimationController getAnimationController(TickerProvider provider) {
    return AnimationController(
        duration: Duration(milliseconds: (gap * rate).toInt()),
        vsync: provider);
  }

  @override
  Animation getAnimation(
      AnimationController controller, State<StatefulWidget> state) {
    return Tween(begin: 0.0, end: gap).animate(controller)
      ..addListener(() {
        if (state.mounted) {
          state.setState(() {});
        }
      });
  }

  @override
  void init(int length) {
    gap = length * twoGap + baseGap;
  }
}

 使用:

                KqAnimateListView(
                    data: const [
                      "Test1",
                      "Test2",
                      "Test3",
                      "Test3",
                      "Test3",
                      "Test3",
                      "Test3",
                      "Test3",
                      "Test3",
                      "Test3",
                      "Test3",
                      "Test3",
                      "Test3"
                    ],
                    item: (t) {
                      return Container(
                        width: double.infinity,
                        height: 50,
                        color: Colors.redAccent,
                        margin: EdgeInsets.only(top: 10.r),
                        child: Text(t),
                      );
                    },
                    animate: type == 0
                        ? HorizontalAnimate(
                            direction: HorizontalAnimateDirection.left)
                        : type == 1
                            ? HorizontalAnimate(
                                direction: HorizontalAnimateDirection.right)
                            : ScaleAnimate(),
                    isNeedFlashEveryTime: true,
                  )

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/97786.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

springboot实战(二)之将项目上传至远程仓库

目录 环境&#xff1a; 背景&#xff1a; 操作&#xff1a; 1.注册码云账号 2.创建仓库 步骤&#xff1a; 1.注册完码云账号后&#xff0c;点击加号&#xff0c;新建仓库 2.输入项目名称和介绍&#xff0c;点击创建 3.复制仓库地址&#xff0c;你可以选择https协议或者…

VSAN硬盘出现resetremoved

原创作者&#xff1a;运维工程师 谢晋 VSAN硬盘出现reset&removed 客户环境有8台服务器dell R740和R740XD服务器组成了一套VSAN集群&#xff0c;但R740那四台的物理机老是出现硬盘故障需进行硬盘更换&#xff0c;后发现刚换完的硬盘没过几天又坏了&#xff0c;先开始怀疑…

C++学习笔记总结练习:运算符重载两种方式

运算符重载的两种方式 1 基本概念 基础 运算符时具有特殊名字的函数&#xff1a;由关键字operator和气候定义的运算符共同组成。 可以被重载的运算符 方式 将运算符重载为类的成员函数。重载运算符函数&#xff0c;并声明为类的友元。 规则 重载后的运算符必须至少有一个…

vscode html使用less和快速获取标签less结构

扩展插件里面搜索 css tree 插件 下载 使用方法 选择你要生成的标签结构然后按CTRLshiftp 第一次需要在输入框输入 get 然后选择 Generate CSS tree less结构就出现在这个里面直接复制到自己的less文件里面就可以使用了 在html里面使用less 下载 Easy LESS 插件 自己创建…

深圳产品展示视频拍摄一站式服务

产品展示视频拍摄一站式服务是指一家专业的拍摄制作公司或团队提供从策划、拍摄到后期制作的全方位服务&#xff0c;以满足客户的产品展示需求。这种服务通常包括以下方面&#xff0c;由产品展示视频制作公司老友记小编从以下几个方面为您整理&#xff1a; 1.策划和预制阶段&a…

【期末复习笔记】计算机操作系统

计算机操作系统 进程的描述与控制程序执行进程进程的定义与特征相关概念定义特征进程与程序的区别 进程的基本状态和转换PCBPCB中的信息作用PCB的组织方式 线程进程与线程的比较 处理机调度与死锁处理机调度处理机调度的层次 调度算法处理机调度算法的目标处理机调度算法的共同…

PhpStorm安装篇

PhpStorm安装篇: 下载地址 : 进入官网下PhpStorm: PHP IDE and Code Editor from JetBrains 下载完之后&#xff0c;安装包 安装目录建议不要放C盘&#xff0c;放其他盘&#xff0c;其他直接一直点 next &#xff0c;到结束 安装完&#xff0c;打开编辑器 注册账号并登录后…

继续绷紧油市神经,市场预计沙特10月继续自愿减产

KlipC报道&#xff1a;据了解&#xff0c;市场参与者大都认为沙特阿拉伯将会把自愿额外减产的措施延长至10月底&#xff0c;以寻求在经济低迷的背景下提振油价。 KlipC的合伙人Andi D表示&#xff1a;“今年5月起&#xff0c;沙特就自愿减产日均50万桶原油&#xff0c;今年6月初…

苹果使用3D打印技术制造Apple Watch Series 9手表外壳

据彭博社的马克・古尔曼报道&#xff0c;苹果公司正在使用 3D 打印技术来制造即将推出的部分Apple Watch Series 9 的外壳。这种制造工艺可以节省传统数控加工所需的大量金属材料&#xff0c;同时缩短生产时间。这与之前苹果分析师郭明錤的说法相吻合。 苹果公司自2021年推出Ai…

Linux 虚拟机同步时间crontab以及crond详解

目录 一 Linux 虚拟机同步时间设置 1. 检查是否安装cron服务&#xff08;即时间同步器&#xff09; 2. 下载时间同步器 3. 编辑crontab 内容 4. 同步更新电脑网络时间 5.设置 reload 6. 查看 crond 状态 二 crond 详解 1. 启动/关闭cron服务 2. crontab命令格式 3. …

【LeetCode-中等题】138. 复制带随机指针的链表

文章目录 题目解题核心思路&#xff1a;找random指针指向思路一&#xff1a;哈希思路二&#xff1a;迭代构造新链表 方法一&#xff1a;哈希递归方法二&#xff1a;纯哈希方法三&#xff1a;迭代 节点拆分 题目 解题核心思路&#xff1a;找random指针指向 这里的拷贝属于深拷…

为什么曾经一马当先的C语言,如今却开始出现骂声

今日话题&#xff0c;为什么曾经一马当先的C语言&#xff0c;如今却开始出现各种骂声&#xff1f;C语言的发展历程可以追溯到20世纪70年代初期&#xff0c;它的设计理念、简洁性、可移植性以及对底层硬件的直接控制能力使其在计算机科学领域逐渐受到重视从而成为了天王搬到存在…

安捷伦DSA91204A高性能示波器/Agilent DSA91204A

描述 安捷伦的DSA90000A系列示波器为同等的DSO90000A产品增加了串行数据分析、EZJIT和50 M存储标准。 Infiniium DSO90000系列拥有业界领先的技术&#xff0c;能够提供超越规格的卓越示波器性能。加速测量任务的定制软件可以完全集成到示波器应用中&#xff0c;实现无缝操作。…

搭建Ubuntu本地web小游戏网站并通过内网穿透实现公网用户远程访问的步骤指南

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《高效编程技巧》《cpolar》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 前言1. 本地环境服务搭建2. 局域网测试访问3. 内网穿透3.1 ubuntu本地安装cpolar内网穿透3.2 创建隧道3.3 测试公网访…

大数据(六):Pandas的基础应用详解(三)

专栏介绍 结合自身经验和内部资料总结的Python教程&#xff0c;每天3-5章&#xff0c;最短1个月就能全方位的完成Python的学习并进行实战开发&#xff0c;学完了定能成为大佬&#xff01;加油吧&#xff01;卷起来&#xff01; 全部文章请访问专栏&#xff1a;《Python全栈教…

下岗吧,Excel

ChatGPT的诞生使Excel公式变得过时。通过使用 ChatGPT 的代码解释器你可以做到&#xff1a; 分析数据创建图表 这就像用自然语言与电子表格交谈一样。我将向大家展示如何使用 ChatGPT 执行此操作并将结果导出为Excel格式&#xff1a; 作为示例&#xff0c;我将分析并创建美国…

用最少数量的箭引爆气球【贪心算法】

用最少数量的箭引爆气球 有一些球形气球贴在一堵用 XY 平面表示的墙面上。墙面上的气球记录在整数数组 points &#xff0c;其中points[i] [xstart, xend] 表示水平直径在 xstart 和 xend之间的气球。你不知道气球的确切 y 坐标。 一支弓箭可以沿着 x 轴从不同点 完全垂直 地…

JVM ZGC垃圾收集器

ZGC垃圾收集器 ZGC&#xff08;“Z”并非什么专业名词的缩写&#xff0c;这款收集器的名字就叫作Z Garbage Collector&#xff09;是一款在JDK 11中新加入的具有实验性质[1]的低延迟垃圾收集器&#xff0c;是由Oracle公司研发的。 ZGC收集器是一款基于Region内存布局的&#…

vue 入门案例模版

vue 入门案例1 01.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title> &l…

如何用Python爬虫持续监控商品价格

目录 持续监控商品价格步骤 1. 选择合适的爬虫库&#xff1a; 2. 选择目标网站&#xff1a; 3. 编写爬虫代码&#xff1a; 4. 设定监控频率&#xff1a; 5. 存储和展示数据&#xff1a; 6. 设置报警机制&#xff1a; 7. 异常处理和稳定性考虑&#xff1a; 可能会遇到的…