diff --git a/lib/core/widgets/intro_overlay_builder.dart b/lib/core/widgets/intro_overlay_builder.dart index bab8412b0..920737ae2 100644 --- a/lib/core/widgets/intro_overlay_builder.dart +++ b/lib/core/widgets/intro_overlay_builder.dart @@ -89,22 +89,9 @@ class IntroOverlayBuilder { } static Widget _button(String text, Function() onTap) { - return GestureDetector( + return _AnimatedBorderButton( + text: text, onTap: onTap, - child: Container( - padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 5.w), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(20.r), - border: Border.all(color: Colors.white), - ), - child: Text( - text, - style: TextStyle( - fontSize: 14.sp, - color: Colors.white, - ), - ), - ), ); } } @@ -166,3 +153,142 @@ class _AnimatedLottieWidgetState extends State<_AnimatedLottieWidget> ); } } + +// 新增的带边框流动动画的按钮组件 +class _AnimatedBorderButton extends StatefulWidget { + final String text; + final Function() onTap; + + const _AnimatedBorderButton({ + required this.text, + required this.onTap, + }); + + @override + _AnimatedBorderButtonState createState() => _AnimatedBorderButtonState(); +} + +class _AnimatedBorderButtonState extends State<_AnimatedBorderButton> + with SingleTickerProviderStateMixin { + late AnimationController _animationController; + late Animation _animation; + + @override + void initState() { + super.initState(); + _animationController = AnimationController( + duration: const Duration(seconds: 2), + vsync: this, + ); + _animation = Tween( + begin: 0.0, + end: 1.0, + ).animate(_animationController); + + _animationController.repeat(); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return GestureDetector( + onTap: widget.onTap, + child: AnimatedBuilder( + animation: _animation, + builder: (context, child) { + return CustomPaint( + painter: _FlowingBorderPainter( + progress: _animation.value, + borderRadius: 20.r, + ), + child: Container( + padding: EdgeInsets.symmetric(horizontal: 10.w, vertical: 5.w), + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(20.r), + border: Border.all(color: Colors.transparent), + ), + child: Text( + widget.text, + style: TextStyle( + fontSize: 14.sp, + color: Colors.white, + ), + ), + ), + ); + }, + ), + ); + } +} + +// 自定义绘制器,用于绘制流动的边框 +class _FlowingBorderPainter extends CustomPainter { + final double progress; + final double borderRadius; + + _FlowingBorderPainter({ + required this.progress, + required this.borderRadius, + }); + + @override + void paint(Canvas canvas, Size size) { + final rect = Rect.fromLTWH(0, 0, size.width, size.height); + final rrect = RRect.fromRectAndRadius(rect, Radius.circular(borderRadius)); + + // 计算路径总长度 + final path = Path()..addRRect(rrect); + final pathMetrics = path.computeMetrics().first; + final totalLength = pathMetrics.length; + + // 流动效果:创建一个渐变的虚线效果 + final dashLength = totalLength * 0.3; // 虚线长度 + final startOffset = (progress * totalLength) % totalLength; + + // 绘制流动的边框段 + for (int i = 0; i < 3; i++) { + final offset = (startOffset + i * totalLength / 3) % totalLength; + final endOffset = (offset + dashLength) % totalLength; + + Path extractedPath; + if (endOffset > offset) { + extractedPath = pathMetrics.extractPath(offset, endOffset); + } else { + // 处理跨越路径起点的情况 + final path1 = pathMetrics.extractPath(offset, totalLength); + final path2 = pathMetrics.extractPath(0, endOffset); + extractedPath = Path() + ..addPath(path1, Offset.zero) + ..addPath(path2, Offset.zero); + } + + // 创建渐变效果 + const gradient = LinearGradient( + colors: [ + Colors.green, + Colors.yellow, + Colors.red, + ], + stops: [0.0, 0.5, 1.0], + ); + + final gradientPaint = Paint() + ..shader = gradient.createShader(rect) + ..style = PaintingStyle.stroke + ..strokeWidth = 1.5; + + canvas.drawPath(extractedPath, gradientPaint); + } + } + + @override + bool shouldRepaint(covariant CustomPainter oldDelegate) { + return true; + } +}