Files
be_happy_public/lib/presentation/components/scooter/battery_indicator.dart
2026-05-12 12:02:40 +03:00

170 lines
4.3 KiB
Dart

import 'dart:math';
import 'package:flutter/material.dart';
class BatteryIndicator extends StatelessWidget {
final double percent;
const BatteryIndicator({super.key, required this.percent});
@override
Widget build(BuildContext context) {
return SizedBox(
height: 400,
child: Stack(
alignment: Alignment.center,
children: [
CustomPaint(
size: const Size(320, 320),
painter: _BatteryRingPainter(percent: percent),
),
Column(
mainAxisSize: MainAxisSize.min,
children: [
Text(
'Заряд батареи',
style: const TextStyle(
color: Colors.white,
fontSize: 16,
),
),
const SizedBox(height: 8),
Text(
'${(percent * 100).toInt()}%',
style: const TextStyle(
color: Colors.white,
fontSize: 48,
fontWeight: FontWeight.w300,
),
),
],
),
],
),
);
}
}
class _BatteryRingPainter extends CustomPainter {
final double percent;
_BatteryRingPainter({required this.percent});
// 🔹 Возвращает цвета и stops для текущего диапазона
(List<Color>, List<double>?) _getGradientForPercent() {
final p = percent * 100;
if (p >= 51) {
return (
const [
Color(0xFF86EFAC),
Color(0xFF67E8F9),
Color(0xFF86EFAC),
],
const [0.0, 0.5, 1.0],
);
} else if (p >= 16) {
return (
const [
Color(0xFFF1FF8B),
Color(0xFF8BFFAA),
Color(0xFFF1FF8B),
],
const [0.0, 0.5, 1.0],
);
} else {
return (
const [
Color(0xFFFF5757),
Color(0xFFF1FF8B),
Color(0xFFFF5757),
],
const [0.0, 0.5, 1.0],
);
}
}
@override
void paint(Canvas canvas, Size size) {
final center = size.center(Offset.zero);
final radius = size.width / 2 - 20;
// Фоновое кольцо
final backgroundPaint = Paint()
..color = Colors.white.withOpacity(0.08)
..style = PaintingStyle.stroke
..strokeWidth = 40;
canvas.drawCircle(center, radius, backgroundPaint);
// Деления
final tickPaint = Paint()
..color = Colors.white.withOpacity(0.15)
..strokeWidth = 2;
for (int i = 0; i < 100; i++) {
final angle = 2 * pi * i / 100 - pi / 2;
final isMajor = i % 10 == 0;
final innerRadius = radius - 40;
final outerRadius = isMajor ? radius - 28 : radius - 32;
final p1 = Offset(
center.dx + cos(angle) * innerRadius,
center.dy + sin(angle) * innerRadius,
);
final p2 = Offset(
center.dx + cos(angle) * outerRadius,
center.dy + sin(angle) * outerRadius,
);
canvas.drawLine(p1, p2, tickPaint);
}
// Получаем градиент по проценту
final (colors, stops) = _getGradientForPercent();
// Glow дуга
final glowPaint = Paint()
..shader = SweepGradient(
colors: colors,
stops: stops,
).createShader(Rect.fromCircle(center: center, radius: radius))
..style = PaintingStyle.stroke
..strokeWidth = 25
..maskFilter = const MaskFilter.blur(BlurStyle.normal, 85)
..strokeCap = StrokeCap.round;
canvas.drawArc(
Rect.fromCircle(center: center, radius: radius),
-pi / 2,
2 * pi * percent,
false,
glowPaint,
);
// Основная дуга
final progressPaint = Paint()
..shader = SweepGradient(
colors: colors,
stops: stops,
).createShader(Rect.fromCircle(center: center, radius: radius))
..style = PaintingStyle.stroke
..strokeWidth = 20
..strokeCap = StrokeCap.round;
canvas.drawArc(
Rect.fromCircle(center: center, radius: radius),
-pi / 2,
2 * pi * percent,
false,
progressPaint,
);
// Внутренний круг
final innerCircle = Paint()
..color = const Color(0xFF16233F)
..style = PaintingStyle.fill;
canvas.drawCircle(center, radius - 60, innerCircle);
}
@override
bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
}