如何制作条纹背景动画?求Flutter按钮点击动画实现方案
一、条纹背景动画实现
可以通过CustomPainter结合AnimationController实现流动/滚动的条纹背景,以下是完整可运行示例:
import 'package:flutter/material.dart'; class StripedBackground extends StatefulWidget { const StripedBackground({super.key}); @override State<StripedBackground> createState() => _StripedBackgroundState(); } class _StripedBackgroundState extends State<StripedBackground> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<double> _offsetAnimation; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(seconds: 3), vsync: this, )..repeat(); _offsetAnimation = Tween<double>(begin: 0, end: 1).animate(_controller); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return AnimatedBuilder( animation: _offsetAnimation, builder: (context, child) { return CustomPaint( painter: StripedPainter(offset: _offsetAnimation.value), child: child, ); }, child: const Center(child: Text("条纹背景动画")), ); } } class StripedPainter extends CustomPainter { final double offset; StripedPainter({required this.offset}); @override void paint(Canvas canvas, Size size) { final paint = Paint() ..style = PaintingStyle.fill ..shader = LinearGradient( colors: [Colors.grey.shade200, Colors.white, Colors.grey.shade200], stops: const [0.3, 0.5, 0.7], begin: Alignment.topLeft, end: Alignment.bottomRight, transform: GradientRotation(offset * 3.1415), ).createShader(Rect.fromLTWH(0, 0, size.width, size.height)); canvas.drawRect(Rect.fromLTWH(0, 0, size.width, size.height), paint); } @override bool shouldRepaint(covariant StripedPainter oldDelegate) { return oldDelegate.offset != offset; } }
如果需要横向滚动的条纹,可修改渐变的transform为水平偏移,调整Tween范围即可。
二、按钮点击动画实现
你需要的点击扩散条纹波纹效果,无需第三方插件,用GestureDetector+Stack+AnimationController就能实现,示例如下:
import 'package:flutter/material.dart'; class StripedButton extends StatefulWidget { final String text; final VoidCallback onTap; const StripedButton({super.key, required this.text, required this.onTap}); @override State<StripedButton> createState() => _StripedButtonState(); } class _StripedButtonState extends State<StripedButton> with SingleTickerProviderStateMixin { late AnimationController _controller; late Animation<double> _scaleAnimation; late Animation<double> _opacityAnimation; Offset? _tapPosition; @override void initState() { super.initState(); _controller = AnimationController( duration: const Duration(milliseconds: 600), vsync: this, ); _scaleAnimation = Tween<double>(begin: 0, end: 2).animate( CurvedAnimation(parent: _controller, curve: Curves.easeOut), ); _opacityAnimation = Tween<double>(begin: 0.5, end: 0).animate( CurvedAnimation(parent: _controller, curve: Curves.easeOut), ); } @override void dispose() { _controller.dispose(); super.dispose(); } void _handleTapDown(TapDownDetails details) { setState(() { _tapPosition = details.localPosition; }); _controller.reset(); _controller.forward(); widget.onTap(); } @override Widget build(BuildContext context) { return GestureDetector( onTapDown: _handleTapDown, child: Stack( alignment: Alignment.center, children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 32, vertical: 12), decoration: BoxDecoration( color: Colors.blue.shade500, borderRadius: BorderRadius.circular(8), ), child: Text( widget.text, style: const TextStyle(color: Colors.white, fontSize: 16), ), ), if (_tapPosition != null) AnimatedBuilder( animation: _controller, builder: (context, child) { return CustomPaint( painter: RippleStripedPainter( center: _tapPosition!, radius: _scaleAnimation.value * MediaQuery.of(context).size.shortestSide, opacity: _opacityAnimation.value, ), ); }, ), ], ), ); } } class RippleStripedPainter extends CustomPainter { final Offset center; final double radius; final double opacity; RippleStripedPainter({required this.center, required this.radius, required this.opacity}); @override void paint(Canvas canvas, Size size) { final paint = Paint() ..style = PaintingStyle.stroke ..strokeWidth = 2 ..shader = LinearGradient( colors: [ Colors.white.withOpacity(opacity), Colors.white.withOpacity(0), ], begin: Alignment.topLeft, end: Alignment.bottomRight, ).createShader(Rect.fromCircle(center: center, radius: radius)); // 绘制8条交叉条纹波纹 for (int i = 0; i < 8; i++) { final angle = (i * 45) * 3.1415 / 180; final startX = center.dx + radius * cos(angle); final startY = center.dy + radius * sin(angle); final endX = center.dx - radius * cos(angle); final endY = center.dy - radius * sin(angle); canvas.drawLine(Offset(startX, startY), Offset(endX, endY), paint); } } @override bool shouldRepaint(covariant RippleStripedPainter oldDelegate) { return oldDelegate.radius != radius || oldDelegate.opacity != opacity; } }
使用该按钮的示例代码:
StripedButton( text: "点击测试", onTap: () { // 自定义点击逻辑 }, )
你可以根据需求调整颜色、动画时长、条纹数量、波纹大小等参数,所有实现均基于Flutter原生API,无需额外插件。
内容的提问来源于stack exchange,提问作者Atman Bhatt
相关产品推荐
相关产品推荐

