import 'dart:math'; import 'package:flutter/material.dart'; import 'package:lucide_icons/lucide_icons.dart'; import 'package:provider/provider.dart'; import 'package:cloudreve4_flutter/presentation/providers/user_setting_provider.dart'; class StorageUsageCard extends StatelessWidget { const StorageUsageCard({super.key}); @override Widget build(BuildContext context) { final theme = Theme.of(context); final colorScheme = theme.colorScheme; return Consumer( builder: (context, userSetting, _) { final capacity = userSetting.capacity; final used = capacity?.used ?? 0; final total = capacity?.total ?? 0; final percentage = capacity?.usagePercentage ?? 0; return Card( child: Padding( padding: const EdgeInsets.all(20), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(LucideIcons.hardDrive, size: 20, color: colorScheme.primary), const SizedBox(width: 8), Text('存储空间', style: theme.textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600)), ], ), const SizedBox(height: 24), Center( child: SizedBox( width: 160, height: 90, child: CustomPaint( painter: _SemiCircleProgressPainter( progress: percentage / 100, color: colorScheme.primary, backgroundColor: colorScheme.primary.withValues(alpha: 0.12), ), ), ), ), const SizedBox(height: 16), Center( child: Text( '${_formatBytes(used)} / ${_formatBytes(total)}', style: theme.textTheme.bodyMedium?.copyWith( color: theme.hintColor, ), ), ), const SizedBox(height: 4), Center( child: Text( '已使用 ${percentage.toStringAsFixed(1)}%', style: theme.textTheme.bodySmall?.copyWith( color: theme.hintColor, ), ), ), ], ), ), ); }, ); } String _formatBytes(int bytes) { if (bytes < 1024) return '$bytes B'; if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(1)} KB'; if (bytes < 1024 * 1024 * 1024) return '${(bytes / (1024 * 1024)).toStringAsFixed(1)} MB'; return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(1)} GB'; } } class _SemiCircleProgressPainter extends CustomPainter { final double progress; final Color color; final Color backgroundColor; _SemiCircleProgressPainter({ required this.progress, required this.color, required this.backgroundColor, }); @override void paint(Canvas canvas, Size size) { final strokeWidth = 14.0; final center = Offset(size.width / 2, size.height); final radius = min(size.width / 2, size.height) - strokeWidth / 2; final bgPaint = Paint() ..color = backgroundColor ..strokeWidth = strokeWidth ..strokeCap = StrokeCap.round ..style = PaintingStyle.stroke; canvas.drawArc( Rect.fromCircle(center: center, radius: radius), pi, pi, false, bgPaint, ); final fgPaint = Paint() ..color = color ..strokeWidth = strokeWidth ..strokeCap = StrokeCap.round ..style = PaintingStyle.stroke; final sweepAngle = pi * progress.clamp(0.0, 1.0); if (sweepAngle > 0) { canvas.drawArc( Rect.fromCircle(center: center, radius: radius), pi, sweepAngle, false, fgPaint, ); } } @override bool shouldRepaint(covariant _SemiCircleProgressPainter oldDelegate) { return oldDelegate.progress != progress || oldDelegate.color != color; } }