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,
);
}
}