124 lines
2.9 KiB
Dart
124 lines
2.9 KiB
Dart
import 'dart:math';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import '../../../constants/app_constants.dart';
|
|
|
|
/// 流动边框装饰器
|
|
class FlowingBorderDecoration extends Decoration {
|
|
final Animation<double> animation;
|
|
final Color color;
|
|
final double width;
|
|
|
|
const FlowingBorderDecoration({
|
|
required this.animation,
|
|
required this.color,
|
|
this.width = 4.0,
|
|
});
|
|
|
|
@override
|
|
BoxPainter createBoxPainter([VoidCallback? onChanged]) {
|
|
return _FlowingBorderPainter(
|
|
animation: animation,
|
|
color: color,
|
|
width: width,
|
|
);
|
|
}
|
|
}
|
|
|
|
class _FlowingBorderPainter extends BoxPainter {
|
|
final Animation<double> animation;
|
|
final Color color;
|
|
final double width;
|
|
|
|
_FlowingBorderPainter({
|
|
required this.animation,
|
|
required this.color,
|
|
required this.width,
|
|
});
|
|
|
|
@override
|
|
void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) {
|
|
final size = configuration.size!;
|
|
final rect = offset & size;
|
|
final radius = Radius.circular(16);
|
|
|
|
final paint = Paint()
|
|
..color = color
|
|
..strokeWidth = width
|
|
..style = PaintingStyle.stroke
|
|
..shader = LinearGradient(
|
|
colors: [color.withAlpha(0), color, color.withAlpha(0)],
|
|
stops: const [0.0, 0.5, 1.0],
|
|
transform: GradientRotation(2 * pi * animation.value),
|
|
).createShader(rect);
|
|
|
|
canvas.drawRRect(RRect.fromRectAndRadius(rect, radius), paint);
|
|
}
|
|
}
|
|
|
|
/// 流动边框容器
|
|
class FlowingBorderContainer extends StatefulWidget {
|
|
final Widget child;
|
|
final Duration duration;
|
|
final Color color;
|
|
final double width;
|
|
final bool autoStart;
|
|
|
|
const FlowingBorderContainer({
|
|
super.key,
|
|
required this.child,
|
|
this.duration = const Duration(seconds: 10),
|
|
this.color = AppConstants.primaryColor,
|
|
this.width = 4.0,
|
|
this.autoStart = true,
|
|
});
|
|
|
|
@override
|
|
State<FlowingBorderContainer> createState() => _FlowingBorderContainerState();
|
|
}
|
|
|
|
class _FlowingBorderContainerState extends State<FlowingBorderContainer>
|
|
with SingleTickerProviderStateMixin {
|
|
late AnimationController _controller;
|
|
late Animation<double> _animation;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_controller = AnimationController(duration: widget.duration, vsync: this);
|
|
_animation = Tween<double>(
|
|
begin: 0,
|
|
end: 1,
|
|
).animate(CurvedAnimation(parent: _controller, curve: Curves.linear));
|
|
|
|
if (widget.autoStart) {
|
|
_controller.repeat();
|
|
}
|
|
}
|
|
|
|
@override
|
|
void dispose() {
|
|
_controller.dispose();
|
|
super.dispose();
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return AnimatedBuilder(
|
|
animation: _animation,
|
|
builder: (context, child) {
|
|
return Container(
|
|
padding: EdgeInsets.all(widget.width),
|
|
decoration: FlowingBorderDecoration(
|
|
animation: _animation,
|
|
color: widget.color,
|
|
width: widget.width,
|
|
),
|
|
child: widget.child,
|
|
);
|
|
},
|
|
);
|
|
}
|
|
}
|