Flutter封装组动画混合动画AnimatedGroup示例详解

目录
  • 一、来源
  • 二、AnimatedGroup使用示例:
  • 三、AnimatedGroup源码
  • 最后

一、来源

项目中遇到混合动画的情况,每次实现都需要生命一堆属性,让代码变得杂乱,难以维护。

参考 iOS 组动画 CAAimationGroup, 随花半天时间封装一个混合动画组件 AnimatedGroup。

此组件基于极简、高扩展、高适用的封装原则,基本满足当前项目开发。

二、AnimatedGroup使用示例:

//
//  AnimatedGroupDemo.dart
//  flutter_templet_project
//
//  Created by shang on 12/6/21 5:52 PM.
//  Copyright  12/6/21 shang. All rights reserved.
//
import 'package:flutter/material.dart';
import 'package:flutter_templet_project/basicWidget/animated_group.dart';
class AnimatedGroupDemo extends StatefulWidget {
  AnimatedGroupDemo({ Key? key, this.title}) : super(key: key);
  final String? title;
  @override
  _AnimatedGroupDemoState createState() => _AnimatedGroupDemoState();
}
class _AnimatedGroupDemoState extends State<AnimatedGroupDemo> {
  GlobalKey<AnimatedGroupState> _globalKey = GlobalKey();
  final _animations = <AnimatedGroupItemModel>[
    AnimatedGroupItemModel(
      tween: Tween<double>(begin: .0, end: 300.0,),
      begin: 0.0,
      end: 0.6
    ),
    AnimatedGroupItemModel(
      tween: ColorTween(begin: Colors.green, end: Colors.red,),
      begin: 0.0,
      end: 0.6
    ),
    AnimatedGroupItemModel(
      tween: Tween<EdgeInsets>(
        begin: const EdgeInsets.only(left: .0),
        end: const EdgeInsets.only(left: 100.0),
      ),
      begin: 0.6,
      end: 1.0
    ),
  ];
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title ?? "$widget"),
      ),
      body: Center(
        child: Column(
          children: [
            ElevatedButton(
              child: Text("start animation"),
              onPressed: (){
                _globalKey.currentState?.palyeAnimations(isRemovedOnCompletion: false);
              },
            ),
            Container(
              width: 300,
              height: 300,
              child: AnimatedGroup(
                key: _globalKey,
                duration: Duration(milliseconds: 2000),
                animations: _animations,
                child: Text("AnimatedGroupWidget 混合动画", style: TextStyle(color: Colors.white, backgroundColor: Colors.green),),
                builder: (BuildContext context, Widget? child, List<Animation<dynamic>> animations) {
                  final aHeight = animations[0];
                  final aColor = animations[1];
                  final aPadding = animations[2];
                  return Stack(
                    children: [
                      Container(
                        alignment: Alignment.bottomCenter,
                        padding: aPadding.value,
                        child: Container(
                          color: aColor.value,
                          width: 50.0,
                          height: aHeight.value,
                        ),
                      ),
                      Center(child: child!)
                    ],
                  );
                },
              ),
              decoration: BoxDecoration(
                color: Colors.black.withOpacity(0.1),
                border: Border.all(
                  color: Colors.black.withOpacity(0.5),
                )
              ),
            )
          ],
        ),
      ),
    );
  }
}

三、AnimatedGroup源码

//
//  AnimatedGroupDemo.dart
//  flutter_templet_project
//
//  Created by shang on 1/19/23 8:21 AM.
//  Copyright  1/19/23 shang. All rights reserved.
//
import 'package:flutter/material.dart';
/// 混合动画回调类型
typedef AnimatedGroupBuilder = Widget Function(BuildContext context, Widget? child, List<Animation<dynamic>> animations);
class AnimatedGroup extends StatefulWidget {
  /// 混合动画
  AnimatedGroup({
    Key? key,
    required this.animations,
    required this.builder,
    this.controller,
    this.duration = const Duration(milliseconds: 2000),
    this.child,
  }) : super(key: key);
  /// 混合动画数组
  List<AnimatedGroupItemModel> animations;
  /// 混合动画回调
  AnimatedGroupBuilder builder;
  /// 控制器
  AnimationController? controller;
  /// AnimationController 控制的 duration 属性
  Duration? duration;
  /// 不需要多次构建的部分
  Widget? child;
  @override
  AnimatedGroupState createState() => AnimatedGroupState();
}
/// 混合动画 State
class AnimatedGroupState extends State<AnimatedGroup> with TickerProviderStateMixin {
  AnimationController? _controller;
  /// 仅限于无法满足功能时使用(透传对象, 方便二次开发)
  AnimationController get controller => _controller!;
  List<Animation<dynamic>> _animations = [];
  @override
  void initState() {
    _controller = widget.controller ?? AnimationController(duration: widget.duration, vsync: this);
    _animations = widget.animations.map((e) => e.tween.animate(_buildAnim(e.begin, e.end))).toList();
    super.initState();
  }
  @override
  void dispose() {
    _controller?.dispose();
    super.dispose();
  }
  @override
  Widget build(BuildContext context) {
    return AnimatedBuilder(
      animation: _controller!,
      child: widget.child,
      builder: (BuildContext context, Widget? child){
        return widget.builder(context, child, _animations);
      }
    );
  }
  /// 开始执行动画
  ///
  /// isRemovedOnCompletion 是否单程动画
  ///
  /// isReverse 是否逆转动画
  palyeAnimations({bool isRemovedOnCompletion = true, bool isReverse = false}) async {
    try {
      if (!isReverse) {
        await _controller?.forward().orCancel;
        if (!isRemovedOnCompletion) {
          await _controller?.reverse().orCancel;
        }
      } else {
        await _controller?.reverse().orCancel;
        if (!isRemovedOnCompletion) {
          await _controller?.forward().orCancel;
        }
      }
    } on TickerCanceled {
      // the animation got canceled, probably because we were disposed
    };
  }
  /// 创建动画对象
  CurvedAnimation _buildAnim(double begin, double end) {
    return CurvedAnimation(
      parent: _controller!,
      curve: Interval(
        begin,
        end,
        curve: Curves.ease,
      ),
    );
  }
}
/// 混合动画单个动画模型
class AnimatedGroupItemModel{
  /// 混合动画单个动画模型
  AnimatedGroupItemModel({
    required this.tween,
    required this.begin,
    required this.end,
  });
  /// 动画 Tween
  Tween tween;
  /// 动画开始时间 (0 - 1.0)
  double begin;
  /// 动画结束时间 (0 - 1.0)
  double end;
}

最后

代码复制到项目中可直接运行;

github

以上就是Flutter封装组动画混合动画AnimatedGroup示例详解的详细内容,更多关于Flutter封装AnimatedGroup的资料请关注我们其它相关文章!

(0)

相关推荐

  • Flutter在项目中使用动画不使用包实现详解

    目录 前言 正文 1 按下按钮柔软的感觉 2 想要一个像 Instagram 一样的喜欢按钮吗? 3 动画页面过渡 4 动画文字 5 更改/闪动文本样式 前言 动画对于 web 和移动应用程序都非常重要.但是在移动应用程序中不应该使用夸张的动画.简单但是很多动画使你的应用程序更好用.以至于当你点击一个按钮时,一种平滑的感觉或者页面过渡都会影响到你. 正文 1 按下按钮柔软的感觉 class _CustomButtonState extends State<CustomButton> with

  • flutter封装点击菜单工具栏组件checkBox多选版

    目录 效果展示 实现代码 代码调用 效果展示 单选版可看上篇博文 用flutter封装一个点击菜单工具栏组件 本文是CHeckbox多选版 效果如图所示,点击选项回调选中的index和是否选中的值,可以自定义横向纵向,传递宽高后自动计算子项宽高,自定义边框.背景.选中的样式 实现代码 第一部分是封装子项组件, ToolMenuCheckboxItemWidget组件如下: import 'dart:core'; import 'package:flutter/material.dart'; //

  • Flutter绘制3.4边形及多边形渐变动画实现示例

    目录 正文 绘制3.4边形 整数边形的绘制 分数边形的绘制 具体代码 效果改进1 效果改进2 正文 项目被优化了,人也跟着被优化了,正好趁这一个月整理整理关于flutter的一些东西. 绘制3.4边形 先看一下效果图: 起因是上上上上上个月浏览flutter的canvas相关内容时,点进去一个网站,看到一个让我眼前一亮的动效: 作者用的代码是swift的,我没细看,不过他文章里的一句话让我醍醐灌顶: That is, we want the shape be asked to draw mult

  • flutter封装单选点击菜单工具栏组件

    目录 效果展示 实现代码 代码调用 效果展示 CHeckbox多选版 flutter封装点击菜单工具栏组件 本文是单选版 效果如图所示,点击选项回调选中的index,可以自定义横向纵向,传递宽高后自动计算子项宽高,自定义边框.背景.选中的样式 实现代码 第一部分是封装子项组件, ToolMenuItemWidget组件如下: import 'dart:core'; import 'package:flutter/material.dart'; /// @author 编程小龙 /// @创建时间

  • 封装flutter状态管理工具示例详解

    目录 引言 RxBinder 代码实现 Demo 完美运行 引言 关于 Flutter 状态管理,公司项目使用的是Bloc方案.Bloc 其实本质上是 provider 的封装扩展库,整体通过 InheritedWidget .Notifier 外加 Stream中转实现状态变更通知. 关于 Bloc 实现原理,有兴趣的同学可以观看这篇文章 Bloc原理解析 RxBinder 撇开Bloc内部实现策略,小轰尝试基于数据驱动模型,自定义一套状态管理工具.构思如下: 主要成员如下: RxBinder

  • Flutter学习LogUtil封装与实现实例详解

    目录 一. 为什么要封装打印类 二. 需要哪些类 三. 打印输出的抽象类 四. 格式化日志内容 格式化堆栈 堆栈裁切工具类 格式化堆栈信息 格式化JSON 五. 需要用到的常量 六. 为了控制多个打印器的设置做了一个配置类 七. Log的管理类 九. 调用LogUtil 十. 定义一个Flutter 控制台打印输出的方法 十一. 现在使用前初始化log打印器一次 使用 一. 为什么要封装打印类 虽然 flutter/原生给我们提供了日志打印的功能,但是超出一定长度以后会被截断 Json打印挤在一

  • Flutter添加页面过渡动画实现步骤

    目录 正文 使用插件探索不同的转换 步骤 1: 在 pubspec.yaml 中添加页面动画转换 步骤 2: 在 PageOne 上导入库 步骤3.添加以下导航代码行 其他类型转换的完整代码: 总结 正文 大家好,在这篇文章中,我们将学习如何添加动画,同时从一个页面到其他在 Flutter.我们将覆盖不同类型的动画和实现基本动画 Flutter 使用包页动画过渡. 动画在提升用户体验方面起着至关重要的作用,但动画到底是什么呢? 设计语言,例如 Material,定义了在路线(或屏幕)之间转换时的

  • 基于fluttertoast实现封装弹框提示工具类

    目录 提示 实现效果 实现 测试 提示 已将代码上传至gitee,后续会继续更新学习封装的一些组件: flutter练习 实现效果 实现 1.先在pubspec.yaml文件汇总引入fluttertoast的包: fluttertoast: ^8.0.8 # 弹窗 2.封装弹框工具类DialogUtils: import 'package:flutter/material.dart'; import 'package:fluttertoast/fluttertoast.dart'; /// @a

  • Flutter封装组动画混合动画AnimatedGroup示例详解

    目录 一.来源 二.AnimatedGroup使用示例: 三.AnimatedGroup源码 最后 一.来源 项目中遇到混合动画的情况,每次实现都需要生命一堆属性,让代码变得杂乱,难以维护. 参考 iOS 组动画 CAAimationGroup, 随花半天时间封装一个混合动画组件 AnimatedGroup. 此组件基于极简.高扩展.高适用的封装原则,基本满足当前项目开发. 二.AnimatedGroup使用示例: // // AnimatedGroupDemo.dart // flutter_

  • jQuery编程动画的基本方法示例详解

    目录 一.动画 .show() .hide() .fadeIn() .fadeOut() .animate() .slideDown() .slideUp() .delay() .clearQueue() .fadeTo() 一.动画 jQuery提供了一些列的动画基本方法,同时也提供了自定动画方案.animate(). .show() 当提供一个 duration(持续时间)参数,.show()成为一个动画方法..show()方法将为匹配元素的宽度,高度,以及不透明度,同时进行动画操作. 持续

  • 使用纯JavaScript封装一个消息提示条功能示例详解

    目录 介绍 思路&布局 操作逻辑 完整代码 介绍 一个类似Element UI.Ant-Design UI等 UI 框架的消息提示功能,方便在任何网页环境中直接调用函数使用:区别在不依赖 js 及 css 引用,而是使用纯 js 进行封装实现,代码更精简,同时保持和 UI 框架一样的视觉效果(可自行修改成自己喜欢的样式) 代码仓库 在线预览效果(点击[登录].[点击复制]按钮时触发提示效果) 思路&布局 先来写单个提示条,并实现想要的过渡效果,最后再用逻辑操作输出节点即可:这里不需要父节点

  • Flutter的键值存储数据库使用示例详解

    目录 Flutter 键值存储数据库 unqlite unqlite_flutter 快速上手 简单键值对存储 JSON 为什么你应该使用unqlite_flutter? Flutter 键值存储数据库 键值存储是开发中十分常见的需求,在Flutter开发中,一般使用 shared_preferences 插件来实现.shared_preferences 本质上就是将键值对保存到一个XML文件中进行持久化. 而shared_preferences 实际上存在一定缺陷,譬如其性能较差,不适合处理大

  • Flutter 弹性布局基石flex算法flexible示例详解

    目录 flex 布局算法 非弹性组件在 main 轴受到的约束是 unbounded fit 参数 Expanded Spacer flex 布局算法 Flutter 弹性布局的基石 是 flex 和 flexible.理解了这两个 widget,后面的row,column 就都轻而易举了.本文用示例的方式详细介绍 flex 的布局算法. 先布局 flex 为 0 或 null 的 child.在 main 轴上 child 受到的约束是 unbounded.如果 crossAxisAlignm

  • vue中Axios的封装和API接口的管理示例详解

    目录 一.axios的封装 安装 引入 环境的切换 设置请求超时 post请求头的设置 请求拦截 响应的拦截 封装get方法和post方法 axios的封装基本就完成了,下面再简单说下api的统一管理. 2018.8.14更新 我们所要的说的axios的封装和api接口的统一管理,其实主要目的就是在帮助我们简化代码和利于后期的更新维护. 一.axios的封装 在vue项目中,和后台交互获取数据这块,我们通常使用的是axios库,它是基于promise的http库,可运行在浏览器端和node.js

  • Flutter 首页必用组件NestedScrollView的示例详解

    昨天Flutter 1.17版本重磅发布,新的版本主要是优化性能.修复bug,有人觉得此版本毫无亮点,但也从另一方面体现了Flutter目前针对移动端已经较为完善,想了解具体内容,文末有链接,如果你想升级到最新版本,建议慎重,有些人升级后项目无法运行. 今天介绍的组件是NestedScrollView,大部分的App首页都会用到这个组件. 可以在其内部嵌套其他滚动视图的滚动视图,其滚动位置是固有链接的. 在普通的ScrollView中, 如果有一个Sliver组件容纳了一个TabBarView,

  • react使用mobx封装管理用户登录的store示例详解

    1.MobX 介绍 MobX 是一个简单.可伸缩的响应式状态管理库.通过 MobX 你可以用最直观的方式修改状态,其他的一切 MobX 都会为你处理好(如自动更新UI),并且具有非常高的性能.当状态改变时,所有应用到状态的地方都会自动更新. 1.1 React和Mobx关系 React 通过提供机制把应用状态转换为可渲染组件树并对其进行渲染.而MobX提供机制来存储和更新应用状态供 React 使用. 1.2 核心概念 State:驱动应用的数据 Computed values:计算值.如果你想

  • Android Flutter实现3D动画效果示例详解

    目录 前言 AnimatedWidget 简介 3D 旋转动画的实现 总结 前言 上一篇我们介绍了 Animation 和 AnimationController 的使用,这是最基本的动画构建类.但是,如果我们想构建一个可复用的动画组件,通过外部参数来控制其动画效果的时候,上一篇的方法就不太合适了.在 Flutter 中提供了 AnimatedWidget 组件用于构建可复用的动画组件.本篇我们用 AnimatedWidget 来实现组件的3D 旋转效果,如下图所示. AnimatedWidge

  • 利用Pygame制作简单动画的示例详解

    目录 前言 计时器 绘制精灵 加载精灵 完整代码 前言 实现一个帧动画,使用的一个图,根据不同的时间显示不同的图. 使用的就是如下所示的一张图,宽度780 * 300 ,使用加载图片 260 * 150来实现. pygame.init() screen = pygame.display.set_mode((400, 300), 0, 32) pygame.display.set_caption("动画") while True: for event in pygame.event.ge

随机推荐