【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,
    );
  }
}
相关推荐
maaath13 分钟前
【无标题】Flutter for OpenHarmony 的文具手账应用开发实践
flutter·华为·harmonyos
里欧跑得慢15 分钟前
Flutter 主题管理:构建一致的用户界面
前端·css·flutter·web
liulian091615 小时前
Flutter for OpenHarmony 跨平台开发:单位转换功能实战指南
flutter
千码君201616 小时前
Trae:一些关于flutter和 go前后端开发构建的分享
android·flutter·gradle·android-studio·trae·vibe code
maaath17 小时前
【maaath】Flutter for OpenHarmony 手表配饰应用实战开发
flutter·华为·harmonyos
maaath18 小时前
【maaath】Flutter for OpenHarmony 跨平台计算器应用开发实践
flutter·华为·harmonyos
maaath1 天前
【maaath】Flutter for OpenHarmony 闹钟时钟应用开发实战
flutter·华为·harmonyos
maaath1 天前
【maaath】Flutter for OpenHarmony 短信管理应用实战
flutter·华为·harmonyos
maaath1 天前
【maaath】Flutter for OpenHarmony打造跨平台便签备忘录应用
flutter·华为·harmonyos
千码君20161 天前
flutter:与Android Studio模拟器的调试分享
android·flutter