import 'package:flutter/material.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/widgets/app_icon.dart'; import '../../../core/widgets/pressable.dart'; import '../../../core/widgets/surface_card.dart'; import '../../../data/models/system_status.dart'; /// Quiet battery / Wi-Fi readout. It is information, not a toolbar. class StatusCluster extends StatelessWidget { const StatusCluster({ super.key, required this.status, this.compact = false, this.showBattery = true, this.onOpenSettings, }); final SystemStatus status; final bool compact; final bool showBattery; final VoidCallback? onOpenSettings; @override Widget build(BuildContext context) { final colors = context.colors; final theme = Theme.of(context); final batteryColor = status.batteryLevel <= 15 && !status.charging ? colors.danger : (status.charging ? colors.accent : colors.ink); return SurfaceCard( padding: EdgeInsets.symmetric( horizontal: compact ? 16 : 20, vertical: compact ? 10 : 12, ), radius: 999, color: colors.surface.withValues(alpha: 0.82), showShadow: false, child: Row( mainAxisSize: MainAxisSize.min, children: [ Semantics( label: status.wifi ? 'Wi-Fi connected' : 'Wi-Fi disconnected', child: AppIcon( status.wifi ? 'assets/icons/wifi.svg' : 'assets/icons/wifi-off.svg', size: compact ? 18 : 20, color: status.wifi ? colors.ink : colors.inkFaint, ), ), if (onOpenSettings != null) ...[ SizedBox(width: compact ? 10 : 12), Pressable( onTap: onOpenSettings!, semanticLabel: 'Open settings', pressedScale: 0.92, child: Container( width: compact ? 28 : 30, height: compact ? 28 : 30, decoration: BoxDecoration( color: colors.surfaceStrong, shape: BoxShape.circle, border: Border.all(color: colors.border), ), alignment: Alignment.center, child: AppIcon( 'assets/icons/settings.svg', size: compact ? 15 : 17, color: colors.inkMuted, ), ), ), ], if (showBattery) ...[ SizedBox(width: compact ? 12 : 16), _BatteryGlyph( level: status.batteryLevel, charging: status.charging, color: batteryColor, ), const SizedBox(width: 8), Text( '${status.batteryLevel}%', style: theme.textTheme.labelLarge?.copyWith( color: colors.ink, fontSize: compact ? 13 : 14, ), ), if (status.charging) ...[ const SizedBox(width: 6), Icon( Icons.bolt_rounded, size: compact ? 16 : 18, color: colors.accent, ), ], ], ], ), ); } } class _BatteryGlyph extends StatelessWidget { const _BatteryGlyph({ required this.level, required this.charging, required this.color, }); final int level; final bool charging; final Color color; @override Widget build(BuildContext context) { return SizedBox( width: 28, height: 15, child: CustomPaint( painter: _BatteryPainter( level: level, color: color, charging: charging, ), ), ); } } class _BatteryPainter extends CustomPainter { _BatteryPainter({ required this.level, required this.color, required this.charging, }); final int level; final Color color; final bool charging; @override void paint(Canvas canvas, Size size) { final body = Rect.fromLTWH(0, 1, size.width - 4, size.height - 2); final radius = Radius.circular(body.height / 2); final outline = Paint() ..color = color.withValues(alpha: 0.52) ..style = PaintingStyle.stroke ..strokeWidth = 1.3; final cap = Paint()..color = color.withValues(alpha: 0.52); canvas.drawRRect(RRect.fromRectAndRadius(body, radius), outline); canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromLTWH( size.width - 3.5, size.height * 0.34, 2.5, size.height * 0.32, ), const Radius.circular(1), ), cap, ); final inset = body.deflate(2.4); final fillWidth = inset.width * (level.clamp(0, 100) / 100); if (fillWidth > 0) { final fill = Paint()..color = color; canvas.drawRRect( RRect.fromRectAndRadius( Rect.fromLTWH(inset.left, inset.top, fillWidth, inset.height), const Radius.circular(2.4), ), fill, ); } } @override bool shouldRepaint(covariant _BatteryPainter oldDelegate) { return oldDelegate.level != level || oldDelegate.color != color || oldDelegate.charging != charging; } }