Files
flutterApp/lib/features/remote_control/presentation/widgets/status_chip.dart

125 lines
3.6 KiB
Dart
Raw Normal View History

2026-01-18 20:21:14 +08:00
import 'package:flutter/material.dart';
2026-01-21 13:27:55 +08:00
import 'package:flutter_svg/flutter_svg.dart';
2026-01-18 20:21:14 +08:00
class StatusChip extends StatefulWidget {
final String text;
final Color color;
2026-01-21 13:27:55 +08:00
final IconData? icon;
final String? svgPath;
final Color? svgColor;
final bool breathing;
2026-01-18 20:21:14 +08:00
final VoidCallback? onTap;
const StatusChip({
super.key,
required this.text,
required this.color,
2026-01-21 13:27:55 +08:00
this.icon,
this.svgPath,
this.svgColor,
2026-01-18 20:21:14 +08:00
this.breathing = false,
this.onTap,
2026-01-21 13:27:55 +08:00
}) : assert(icon != null || svgPath != null, '必须提供 icon 或 svgPath 其中之一');
2026-01-18 20:21:14 +08:00
@override
State<StatusChip> createState() => _StatusChipState();
}
class _StatusChipState extends State<StatusChip>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _opacityAnimation;
@override
void initState() {
super.initState();
_controller = AnimationController(
vsync: this,
duration: const Duration(milliseconds: 1000),
);
if (widget.breathing) _controller.repeat(reverse: true);
2026-01-21 13:27:55 +08:00
// 动画区间从 0.3 到 1.0
2026-01-18 20:21:14 +08:00
_opacityAnimation = Tween<double>(
2026-01-21 13:27:55 +08:00
begin: 0.3,
2026-01-18 20:21:14 +08:00
end: 1.0,
2026-01-21 13:27:55 +08:00
).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut));
}
@override
void didUpdateWidget(StatusChip oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.breathing != oldWidget.breathing) {
widget.breathing ? _controller.repeat(reverse: true) : _controller.stop();
}
2026-01-18 20:21:14 +08:00
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
2026-01-21 13:27:55 +08:00
// 【关键】获取颜色传入时的透明度(比如你传了 0.6,baseOpacity 就是 0.6)
final double baseOpacity = widget.color.opacity;
2026-01-18 20:21:14 +08:00
return AnimatedBuilder(
animation: _opacityAnimation,
builder: (context, child) {
2026-01-21 13:27:55 +08:00
// 【关键】如果不呼吸,直接用 baseOpacity,不再强制给 1.0
final currentAlpha = widget.breathing
? _opacityAnimation.value * baseOpacity
: baseOpacity;
2026-01-18 20:21:14 +08:00
return GestureDetector(
onTap: widget.onTap,
child: Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
2026-01-21 13:27:55 +08:00
// 这里应用计算后的透明度
2026-01-18 20:21:14 +08:00
color: widget.color.withOpacity(currentAlpha),
2026-01-21 13:27:55 +08:00
borderRadius: BorderRadius.circular(12),
2026-01-18 20:21:14 +08:00
border: Border.all(
2026-01-21 13:27:55 +08:00
color: Colors.white.withOpacity(0.15),
2026-01-18 20:21:14 +08:00
width: 0.5,
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
2026-01-21 13:27:55 +08:00
if (widget.svgPath != null)
SvgPicture.asset(
widget.svgPath!,
width: 14,
height: 14,
colorFilter: ColorFilter.mode(
widget.svgColor ?? Colors.white,
BlendMode.srcIn,
),
)
else
Icon(
widget.icon,
color: widget.svgColor ?? Colors.white,
size: 14,
),
const SizedBox(width: 5),
2026-01-18 20:21:14 +08:00
Text(
widget.text,
style: const TextStyle(
color: Colors.white,
2026-01-21 13:27:55 +08:00
fontSize: 13,
fontWeight: FontWeight.w600,
2026-01-18 20:21:14 +08:00
),
),
],
),
),
);
},
);
}
}