|
|
|
@ -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<double> _animation; |
|
|
|
|
|
|
|
@override |
|
|
|
void initState() { |
|
|
|
super.initState(); |
|
|
|
_animationController = AnimationController( |
|
|
|
duration: const Duration(seconds: 2), |
|
|
|
vsync: this, |
|
|
|
); |
|
|
|
_animation = Tween<double>( |
|
|
|
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; |
|
|
|
} |
|
|
|
} |
|
|
|
|