Flutter如何实现带扩散渐隐涟漪效果的圆形按钮?
Flutter 按钮涟漪扩散动画实现方案

你当前的静态实现已经完成了基础布局,只需要补全动画逻辑即可实现白色圆形放大+透明度渐隐的涟漪效果,具体实现如下:
核心实现逻辑
- 用
AnimationController控制动画总时长、循环播放状态 - 分别定义缩放、透明度两个补间动画,控制涟漪的尺寸变化和透明度过渡
- 改造自定义画笔
CirclePainter,支持传入动画参数动态绘制不同状态的圆形 - 用
AnimatedBuilder监听动画值变化,驱动画布实时重绘
完整代码实现
1. 封装涟漪动画组件
class RippleEffect extends StatefulWidget { const RippleEffect({super.key, required this.buttonDiameter}); // 传入中心GO按钮的直径,保证涟漪和按钮起始尺寸对齐 final double buttonDiameter; @override State<RippleEffect> createState() => _RippleEffectState(); } class _RippleEffectState extends State<RippleEffect> with SingleTickerProviderStateMixin { late AnimationController _rippleController; late Animation<double> _scaleAnim; late Animation<double> _opacityAnim; @override void initState() { super.initState(); // 动画总时长1.5秒,可按需调整 _rippleController = AnimationController( vsync: this, duration: const Duration(milliseconds: 1500), )..repeat(); // 设置为循环播放 // 涟漪从按钮等大放大到2.5倍,可按需调整放大倍率 _scaleAnim = Tween<double>(begin: 1, end: 2.5).animate( CurvedAnimation(parent: _rippleController, curve: Curves.easeOut), ); // 涟漪从完全不透明过渡到完全透明 _opacityAnim = Tween<double>(begin: 1, end: 0).animate( CurvedAnimation(parent: _rippleController, curve: Curves.easeOut), ); } @override void dispose() { _rippleController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _rippleController, builder: (context, child) { return CustomPaint( size: Size(widget.buttonDiameter, widget.buttonDiameter), painter: CirclePainter( scale: _scaleAnim.value, opacity: _opacityAnim.value, ), ); }, ); } }
2. 改造自定义圆形画笔
class CirclePainter extends CustomPainter { CirclePainter({required this.scale, required this.opacity}); final double scale; final double opacity; final _basePaint = Paint() ..strokeWidth = 2 ..style = PaintingStyle.stroke; @override void paint(Canvas canvas, Size size) { // 动态设置透明度 _basePaint.color = Colors.white.withOpacity(opacity); // 以中心为原点绘制缩放后的正圆,保证和按钮始终对齐 final center = Offset(size.width / 2, size.height / 2); final currentRadius = (size.width / 2) * scale; canvas.drawCircle(center, currentRadius, _basePaint); } @override bool shouldRepaint(covariant CirclePainter oldDelegate) { // 动画值变化时触发重绘 return oldDelegate.scale != scale || oldDelegate.opacity != opacity; } }
3. 替换原有Stack布局代码
注意把涟漪层和按钮层的位置参数对齐,避免错位:
Stack( alignment: Alignment.center, children: [ // 涟漪层:GO按钮加完内边距直径约为70,可根据实际显示效果微调 Positioned( bottom: 15, child: RippleEffect(buttonDiameter: 70), ), // 原有GO按钮层 Positioned( bottom: 15, child: InkWell( onTap: () {}, child: Material( type: MaterialType.circle, color: Color(0xFF246DE9), child: Padding( padding: const EdgeInsets.all(24), child: Text( 'GO', style: TextStyle( fontSize: 25, color: Colors.white, ), ), ), ), ), ), ], )

效果可调参数
- 修改动画控制器的
duration值,可以调整涟漪扩散的速度 - 修改缩放动画的
end值,可以调整涟漪最终扩散的范围 - 修改画笔的
strokeWidth值,可以调整涟漪描边的粗细 - 如果需要多层叠加涟漪,只需要在Stack里多加几个
RippleEffect组件,给对应动画设置不同的启动延迟即可。
内容的提问来源于stack exchange,提问作者Ng Lok Chun
相关产品推荐
相关产品推荐

