【Flutter】水波纹扩散动画

1.说明

中心的按钮缩放,按钮底部有水波纹无限扩散的效果。

2.UI效果

水波纹扩散动画

3.源码

dart 复制代码
import 'dart:async';
import 'package:flutter/material.dart';

// 水波纹扩散动画
// AnimationController 用完,需要自行销毁
class WaterRippleDiffusionAnimation extends StatefulWidget {
  const WaterRippleDiffusionAnimation({super.key});

  @override
  State<WaterRippleDiffusionAnimation> createState() =>
      _WaterRippleDiffusionAnimationState();
}

class _WaterRippleDiffusionAnimationState
    extends State<WaterRippleDiffusionAnimation>
    with SingleTickerProviderStateMixin {
  // 中心按钮的动画
  late AnimationController _centerCtrl;
  late Animation<double> _centerScaleAnimation;

  final centerTweenItems = [
    TweenSequenceItem(
        tween: Tween<double>(begin: 1.0, end: 1.1)
            .chain(CurveTween(curve: Curves.easeIn)),
        weight: 40),
    TweenSequenceItem(
        tween: Tween<double>(begin: 1.1, end: 1.0)
            .chain(CurveTween(curve: Curves.easeOut)),
        weight: 40),
    TweenSequenceItem(
        tween: Tween<double>(begin: 1.0, end: 1.0)
            .chain(CurveTween(curve: Curves.easeOut)),
        weight: 20)
  ];

  @override
  void initState() {
    super.initState();

    _centerCtrl = AnimationController(
        vsync: this, duration: Duration(milliseconds: 2200));
    _centerScaleAnimation = _centerCtrl.drive(TweenSequence(centerTweenItems));

    _centerCtrl.repeat();
  }

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: const Text('水波纹扩散动画'),
      ),
      body: Center(
        child: Stack(
          alignment: Alignment.center,
          children: [
            const RippleCircles(
              circleCount: 2,
              circleMilliSecondsInterval: 1000,
              circleAnimationMilliSeconds: 3000,
              tweenBegin: 100,
              tweenEnd: 200,
              curve: Curves.linear,
            ),
            ScaleTransition(
                scale: _centerScaleAnimation,
                child: Container(
                  width: 100,
                  height: 100,
                  decoration: BoxDecoration(
                    shape: BoxShape.circle,
                    gradient: LinearGradient(colors: [
                      const Color(0xFFD92F2F).withOpacity(0.9),
                      const Color(0xFFFF7367).withOpacity(0.9),
                    ], begin: Alignment.bottomCenter, end: Alignment.topCenter),
                    boxShadow: [
                      BoxShadow(
                          color: const Color(0xFFE94031).withOpacity(0.6),
                          blurRadius: 200),
                    ],
                  ),
                )),
          ],
        ),
      ),
    );
  }
}

// 扩散圆形
class RippleCircles extends StatefulWidget {
  final int? circleCount;
  // 动画时间间隔
  final int? circleMilliSecondsInterval;
  // 一个动画的持续时间
  final int? circleAnimationMilliSeconds;
  final double? tweenBegin;
  final double? tweenEnd;
  final Curve? curve;

  const RippleCircles(
      {super.key,
      this.circleCount = 3,
      this.circleMilliSecondsInterval = 1000,
      this.circleAnimationMilliSeconds = 4000,
      this.tweenBegin = 100,
      this.tweenEnd = 300,
      this.curve = Curves.linear});

  @override
  State<RippleCircles> createState() => _RippleCirclesState();
}

class _RippleCirclesState extends State<RippleCircles>
    with TickerProviderStateMixin {
  //动画控件集合
  final List<Widget> _children = [];
  //动画控制器
  final List<AnimationController> _controllers = [];
  //添加动画计时器
  Timer? _addTimer;
  int _count = 0;

  @override
  void initState() {
    super.initState();
    _count = widget.circleCount!;
    _startAnimation();
  }

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

  void _startAnimation() {
    // 每隔1秒添加一个扩散圆形,总共创建_count个
    _addTimer = Timer.periodic(
        Duration(milliseconds: widget.circleMilliSecondsInterval!), (timer) {
      if (_count >= 1) {
        _addAnimation(isInit: true);
      } else {
        _addTimer?.cancel();
      }
    });
  }

  void _addAnimation({bool isInit = true}) {
    // 创建动画控制器
    var controller = AnimationController(
        duration: Duration(milliseconds: widget.circleAnimationMilliSeconds!),
        vsync: this);

    _controllers.add(controller);
    _count--;

    // 创建Tween
    var animation = Tween(begin: widget.tweenBegin, end: widget.tweenEnd)
        .animate(CurvedAnimation(parent: controller, curve: widget.curve!));

    var child = AnimatedBuilder(
        animation: controller,
        builder: (BuildContext context, Widget? child) {
          return Opacity(
            // opacity: 1.0 -
            //     ((animation.value - widget.tweenBegin!) /
            //         (widget.tweenEnd! - widget.tweenBegin!)),
            opacity: 0.65 -
                ((animation.value - widget.tweenBegin!) /
                        (widget.tweenEnd! - widget.tweenBegin!)) *
                    0.65,
            child: Container(
              width: animation.value,
              height: animation.value,
              decoration: const BoxDecoration(
                  color: Color(0xFFFF867B),
                  // border: Border.all(color: const Color(0xFFFF867B), width: 2),
                  shape: BoxShape.circle),
            ),
          );
        });

    _children.add(child);

    controller.repeat();

    if (mounted) {
      setState(() {});
    }
  }

  // 销毁所有动画
  void _disposeControllers() {
    for (var element in _controllers) {
      element.dispose();
    }
    _controllers.clear();
    _children.clear();
    if (_addTimer != null && _addTimer!.isActive) {
      _addTimer?.cancel();
    }
  }

  @override
  Widget build(BuildContext context) {
    return Stack(
      alignment: Alignment.center,
      children: _children,
    );
  }
}
相关推荐
江上清风山间明月1 天前
Flutter开发的应用页面非常多时如何高效管理路由
android·flutter·路由·页面管理·routes·ongenerateroute
Zsnoin能2 天前
flutter国际化、主题配置、视频播放器UI、扫码功能、水波纹问题
flutter
早起的年轻人2 天前
Flutter CupertinoNavigationBar iOS 风格导航栏的组件
flutter·ios
HappyAcmen2 天前
关于Flutter前端面试题及其答案解析
前端·flutter
coooliang2 天前
Flutter 中的单例模式
javascript·flutter·单例模式
coooliang2 天前
Flutter项目中设置安卓启动页
android·flutter
JIngles1232 天前
flutter将utf-8编码的字节序列转换为中英文字符串
java·javascript·flutter
B.-2 天前
在 Flutter 中实现文件读写
开发语言·学习·flutter·android studio·xcode
freflying11193 天前
使用jenkins构建Android+Flutter项目依赖自动升级带来兼容性问题及Jenkins构建速度慢问题解决
android·flutter·jenkins
机器瓦力3 天前
Flutter应用开发:对象存储管理图片
flutter