feat(statistics): enhance safety indicators with animations and improve UI components
This commit is contained in:
parent
8bafa19e31
commit
7c44fa86bd
|
@ -10,7 +10,7 @@ class MainSafetyIndicatorController extends GetxController {
|
||||||
// Observable variables
|
// Observable variables
|
||||||
final RxDouble progress = 0.0.obs;
|
final RxDouble progress = 0.0.obs;
|
||||||
final RxString title = "".obs;
|
final RxString title = "".obs;
|
||||||
final RxString label = "Level".obs;
|
final RxString label = "Level of crime".obs;
|
||||||
final Rx<Color> color = Colors.purple.obs;
|
final Rx<Color> color = Colors.purple.obs;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -0,0 +1,190 @@
|
||||||
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
class MainSafetyIndicatorAnimated extends StatefulWidget {
|
||||||
|
final double progress;
|
||||||
|
final String title;
|
||||||
|
final String label;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
const MainSafetyIndicatorAnimated({
|
||||||
|
super.key,
|
||||||
|
required this.progress,
|
||||||
|
required this.title,
|
||||||
|
required this.label,
|
||||||
|
required this.color,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MainSafetyIndicatorAnimated> createState() =>
|
||||||
|
_MainSafetyIndicatorAnimatedState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MainSafetyIndicatorAnimatedState
|
||||||
|
extends State<MainSafetyIndicatorAnimated>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
late AnimationController _controller;
|
||||||
|
late Animation<double> _progressAnimation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_controller = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(milliseconds: 1500),
|
||||||
|
);
|
||||||
|
|
||||||
|
_progressAnimation = Tween<double>(
|
||||||
|
begin: 0.0,
|
||||||
|
end: widget.progress,
|
||||||
|
).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic));
|
||||||
|
|
||||||
|
// Start the animation when the widget is first built
|
||||||
|
_controller.forward();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(MainSafetyIndicatorAnimated oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
|
// If progress value changes, animate to the new value
|
||||||
|
if (oldWidget.progress != widget.progress) {
|
||||||
|
_progressAnimation = Tween<double>(
|
||||||
|
begin: oldWidget.progress,
|
||||||
|
end: widget.progress,
|
||||||
|
).animate(
|
||||||
|
CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Reset and run the animation
|
||||||
|
_controller.reset();
|
||||||
|
_controller.forward();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 15),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.grey.withOpacity(0.1),
|
||||||
|
spreadRadius: 1,
|
||||||
|
blurRadius: 5,
|
||||||
|
offset: const Offset(0, 1),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
AnimatedBuilder(
|
||||||
|
animation: _progressAnimation,
|
||||||
|
builder: (context, child) {
|
||||||
|
return SizedBox(
|
||||||
|
height: 160,
|
||||||
|
width: 160,
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
// Animated progress circle
|
||||||
|
CustomPaint(
|
||||||
|
painter: SafetyIndicatorPainter(
|
||||||
|
progress: _progressAnimation.value,
|
||||||
|
color: widget.color,
|
||||||
|
),
|
||||||
|
size: const Size(160, 160),
|
||||||
|
),
|
||||||
|
|
||||||
|
// Center text
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
widget.title,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 4),
|
||||||
|
Text(
|
||||||
|
'${(_progressAnimation.value * 100).toInt()}%',
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: widget.color,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Text(
|
||||||
|
widget.label,
|
||||||
|
style: const TextStyle(fontSize: 14, color: Colors.grey),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SafetyIndicatorPainter extends CustomPainter {
|
||||||
|
final double progress;
|
||||||
|
final Color color;
|
||||||
|
|
||||||
|
SafetyIndicatorPainter({required this.progress, required this.color});
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(Canvas canvas, Size size) {
|
||||||
|
final center = Offset(size.width / 2, size.height / 2);
|
||||||
|
final radius = math.min(size.width, size.height) / 2;
|
||||||
|
|
||||||
|
// Background circle
|
||||||
|
final backgroundPaint =
|
||||||
|
Paint()
|
||||||
|
..color = Colors.grey.withOpacity(0.15)
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = 14.0;
|
||||||
|
|
||||||
|
canvas.drawCircle(center, radius - 7, backgroundPaint);
|
||||||
|
|
||||||
|
// Progress arc
|
||||||
|
final progressPaint =
|
||||||
|
Paint()
|
||||||
|
..color = color
|
||||||
|
..style = PaintingStyle.stroke
|
||||||
|
..strokeWidth = 14.0
|
||||||
|
..strokeCap = StrokeCap.round;
|
||||||
|
|
||||||
|
final progressAngle = 2 * math.pi * progress;
|
||||||
|
|
||||||
|
canvas.drawArc(
|
||||||
|
Rect.fromCircle(center: center, radius: radius - 7),
|
||||||
|
-math.pi / 2, // Start from the top
|
||||||
|
progressAngle,
|
||||||
|
false,
|
||||||
|
progressPaint,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool shouldRepaint(covariant SafetyIndicatorPainter oldDelegate) {
|
||||||
|
return oldDelegate.progress != progress || oldDelegate.color != color;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,120 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:sigap/src/features/panic/presentation/controllers/statistics_view_controller.dart';
|
||||||
|
|
||||||
|
class RecoveryIndicatorAnimated extends StatefulWidget {
|
||||||
|
const RecoveryIndicatorAnimated({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<RecoveryIndicatorAnimated> createState() =>
|
||||||
|
_RecoveryIndicatorAnimatedState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RecoveryIndicatorAnimatedState extends State<RecoveryIndicatorAnimated>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
final statsController = Get.find<StatisticsViewController>();
|
||||||
|
late AnimationController _controller;
|
||||||
|
late Animation<double> _progressAnimation;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_controller = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(milliseconds: 1200),
|
||||||
|
);
|
||||||
|
|
||||||
|
_progressAnimation = Tween<double>(
|
||||||
|
begin: 0.0,
|
||||||
|
end: statsController.recoveryProgress.value,
|
||||||
|
).animate(CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic));
|
||||||
|
|
||||||
|
// Start the animation when the widget is first built
|
||||||
|
_controller.forward();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_controller.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Obx(() {
|
||||||
|
// Update the animation if the recovery progress changes
|
||||||
|
if (_controller.isCompleted &&
|
||||||
|
_progressAnimation.value != statsController.recoveryProgress.value) {
|
||||||
|
_progressAnimation = Tween<double>(
|
||||||
|
begin: _progressAnimation.value,
|
||||||
|
end: statsController.recoveryProgress.value,
|
||||||
|
).animate(
|
||||||
|
CurvedAnimation(parent: _controller, curve: Curves.easeOutCubic),
|
||||||
|
);
|
||||||
|
_controller.reset();
|
||||||
|
_controller.forward();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.all(15),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.white,
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
boxShadow: [
|
||||||
|
BoxShadow(
|
||||||
|
color: Colors.grey.withOpacity(0.1),
|
||||||
|
spreadRadius: 1,
|
||||||
|
blurRadius: 5,
|
||||||
|
offset: const Offset(0, 1),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text(
|
||||||
|
'Area Recovery Status',
|
||||||
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
AnimatedBuilder(
|
||||||
|
animation: _progressAnimation,
|
||||||
|
builder: (context, child) {
|
||||||
|
return Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
LinearProgressIndicator(
|
||||||
|
value: _progressAnimation.value,
|
||||||
|
backgroundColor: Colors.grey[200],
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
|
_getStatusColor(),
|
||||||
|
),
|
||||||
|
minHeight: 10,
|
||||||
|
borderRadius: BorderRadius.circular(5),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
Text(
|
||||||
|
statsController.recoveryTime.value,
|
||||||
|
style: TextStyle(
|
||||||
|
color: _getStatusColor(),
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Color _getStatusColor() {
|
||||||
|
final progress = statsController.recoveryProgress.value;
|
||||||
|
if (progress >= 0.8) return Colors.green;
|
||||||
|
if (progress >= 0.5) return Colors.orange;
|
||||||
|
if (progress >= 0.3) return Colors.deepOrange;
|
||||||
|
return Colors.red;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'dart:math' as math;
|
import 'dart:math' as math;
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
class StatIndicatorCardAnimated extends StatefulWidget {
|
class StatIndicatorCardAnimated extends StatefulWidget {
|
||||||
final double progress;
|
final double progress;
|
||||||
final String value;
|
final String value;
|
||||||
|
@ -8,28 +9,30 @@ class StatIndicatorCardAnimated extends StatefulWidget {
|
||||||
final Color color;
|
final Color color;
|
||||||
|
|
||||||
const StatIndicatorCardAnimated({
|
const StatIndicatorCardAnimated({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.progress,
|
required this.progress,
|
||||||
required this.value,
|
required this.value,
|
||||||
required this.label,
|
required this.label,
|
||||||
required this.color,
|
required this.color,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<StatIndicatorCardAnimated> createState() => _StatIndicatorCardAnimatedState();
|
State<StatIndicatorCardAnimated> createState() => _StatIndicatorCardAnimatedState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _StatIndicatorCardAnimatedState extends State<StatIndicatorCardAnimated>
|
class _StatIndicatorCardAnimatedState extends State<StatIndicatorCardAnimated>
|
||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
late AnimationController _controller;
|
late AnimationController _controller;
|
||||||
late Animation<double> _progressAnimation;
|
late Animation<double> _progressAnimation;
|
||||||
|
String _prevValue = '';
|
||||||
|
late AnimatedNumber _numberAnimation;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_controller = AnimationController(
|
_controller = AnimationController(
|
||||||
vsync: this,
|
vsync: this,
|
||||||
duration: const Duration(milliseconds: 1200),
|
duration: const Duration(milliseconds: 1000),
|
||||||
);
|
);
|
||||||
|
|
||||||
_progressAnimation = Tween<double>(
|
_progressAnimation = Tween<double>(
|
||||||
|
@ -40,14 +43,48 @@ class _StatIndicatorCardAnimatedState extends State<StatIndicatorCardAnimated>
|
||||||
curve: Curves.easeOutCubic,
|
curve: Curves.easeOutCubic,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
_prevValue = widget.value;
|
||||||
|
_numberAnimation = AnimatedNumber(
|
||||||
|
from: _parseValue(widget.value),
|
||||||
|
to: _parseValue(widget.value),
|
||||||
|
controller: _controller,
|
||||||
|
);
|
||||||
|
|
||||||
// Start the animation when the widget is first built
|
// Start the animation when the widget is first built
|
||||||
_controller.forward();
|
_controller.forward();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double _parseValue(String value) {
|
||||||
|
// Try to extract a number from the value string
|
||||||
|
if (value.contains('%')) {
|
||||||
|
// If it's a percentage, extract the number
|
||||||
|
return double.tryParse(value.replaceAll(RegExp(r'[^0-9.]'), '')) ?? 0;
|
||||||
|
} else {
|
||||||
|
// Otherwise, try to parse the entire string
|
||||||
|
return double.tryParse(value) ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String _formatNumber(double value) {
|
||||||
|
// If original value contained a percentage sign, add it back
|
||||||
|
if (widget.value.contains('%')) {
|
||||||
|
return '${value.toInt()}%';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, if it's an integer, show it without decimals
|
||||||
|
if (value == value.truncateToDouble()) {
|
||||||
|
return value.toInt().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// For other values, format with appropriate decimals
|
||||||
|
return value.toStringAsFixed(1);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(StatIndicatorCardAnimated oldWidget) {
|
void didUpdateWidget(StatIndicatorCardAnimated oldWidget) {
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
// If the progress value changes, animate to the new value
|
|
||||||
|
// If progress value changes, animate to the new value
|
||||||
if (oldWidget.progress != widget.progress) {
|
if (oldWidget.progress != widget.progress) {
|
||||||
_progressAnimation = Tween<double>(
|
_progressAnimation = Tween<double>(
|
||||||
begin: oldWidget.progress,
|
begin: oldWidget.progress,
|
||||||
|
@ -56,9 +93,27 @@ class _StatIndicatorCardAnimatedState extends State<StatIndicatorCardAnimated>
|
||||||
parent: _controller,
|
parent: _controller,
|
||||||
curve: Curves.easeOutCubic,
|
curve: Curves.easeOutCubic,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
// Reset and run the animation
|
||||||
_controller.reset();
|
_controller.reset();
|
||||||
_controller.forward();
|
_controller.forward();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If value changes, animate the number
|
||||||
|
if (oldWidget.value != widget.value) {
|
||||||
|
_numberAnimation = AnimatedNumber(
|
||||||
|
from: _parseValue(_prevValue),
|
||||||
|
to: _parseValue(widget.value),
|
||||||
|
controller: _controller,
|
||||||
|
);
|
||||||
|
_prevValue = widget.value;
|
||||||
|
|
||||||
|
// If the animation wasn't triggered by progress change
|
||||||
|
if (oldWidget.progress == widget.progress) {
|
||||||
|
_controller.reset();
|
||||||
|
_controller.forward();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -90,6 +145,8 @@ class _StatIndicatorCardAnimatedState extends State<StatIndicatorCardAnimated>
|
||||||
AnimatedBuilder(
|
AnimatedBuilder(
|
||||||
animation: _progressAnimation,
|
animation: _progressAnimation,
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
|
final displayValue = _formatNumber(_numberAnimation.value);
|
||||||
|
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
height: 60,
|
height: 60,
|
||||||
width: 60,
|
width: 60,
|
||||||
|
@ -98,19 +155,21 @@ class _StatIndicatorCardAnimatedState extends State<StatIndicatorCardAnimated>
|
||||||
progress: _progressAnimation.value,
|
progress: _progressAnimation.value,
|
||||||
color: widget.color,
|
color: widget.color,
|
||||||
),
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
displayValue,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: widget.color,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 8),
|
||||||
// Value text
|
|
||||||
Text(
|
|
||||||
widget.value,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
// Label text
|
// Label text
|
||||||
Text(
|
Text(
|
||||||
widget.label,
|
widget.label,
|
||||||
|
@ -118,6 +177,7 @@ class _StatIndicatorCardAnimatedState extends State<StatIndicatorCardAnimated>
|
||||||
fontSize: 12,
|
fontSize: 12,
|
||||||
color: Colors.grey[600],
|
color: Colors.grey[600],
|
||||||
),
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -125,6 +185,27 @@ class _StatIndicatorCardAnimatedState extends State<StatIndicatorCardAnimated>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Animation helper for smooth number transitions
|
||||||
|
class AnimatedNumber {
|
||||||
|
final double from;
|
||||||
|
final double to;
|
||||||
|
final AnimationController controller;
|
||||||
|
late Animation<double> animation;
|
||||||
|
|
||||||
|
AnimatedNumber({
|
||||||
|
required this.from,
|
||||||
|
required this.to,
|
||||||
|
required this.controller,
|
||||||
|
}) {
|
||||||
|
animation = Tween<double>(
|
||||||
|
begin: from,
|
||||||
|
end: to,
|
||||||
|
).animate(CurvedAnimation(parent: controller, curve: Curves.easeOutCubic));
|
||||||
|
}
|
||||||
|
|
||||||
|
double get value => animation.value;
|
||||||
|
}
|
||||||
|
|
||||||
class DonutChartPainter extends CustomPainter {
|
class DonutChartPainter extends CustomPainter {
|
||||||
final double progress;
|
final double progress;
|
||||||
final Color color;
|
final Color color;
|
||||||
|
|
|
@ -5,9 +5,9 @@ import 'package:sigap/src/features/panic/presentation/controllers/statistics_vie
|
||||||
import 'package:sigap/src/utils/loaders/shimmer.dart';
|
import 'package:sigap/src/utils/loaders/shimmer.dart';
|
||||||
|
|
||||||
import 'crime_stats_header.dart';
|
import 'crime_stats_header.dart';
|
||||||
import 'main_safety_indicator.dart';
|
import 'main_safety_indicator_animated.dart';
|
||||||
import 'recovery_indicator.dart';
|
import 'recovery_indicator.dart';
|
||||||
import 'stat_indicator_card.dart';
|
import 'stat_indicator_card_animated.dart';
|
||||||
|
|
||||||
class StatisticsView extends StatelessWidget {
|
class StatisticsView extends StatelessWidget {
|
||||||
const StatisticsView({super.key});
|
const StatisticsView({super.key});
|
||||||
|
@ -46,8 +46,8 @@ class StatisticsView extends StatelessWidget {
|
||||||
|
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
|
|
||||||
// Main indicator - Area Safety Level
|
// Main indicator - Area Safety Level with animation - Full Width
|
||||||
MainSafetyIndicator(
|
MainSafetyIndicatorAnimated(
|
||||||
progress: safetyController.progress.value,
|
progress: safetyController.progress.value,
|
||||||
title: safetyController.title.value,
|
title: safetyController.title.value,
|
||||||
label: safetyController.label.value,
|
label: safetyController.label.value,
|
||||||
|
@ -58,11 +58,11 @@ class StatisticsView extends StatelessWidget {
|
||||||
|
|
||||||
const SizedBox(height: 15),
|
const SizedBox(height: 15),
|
||||||
|
|
||||||
// Secondary indicators row with donut charts
|
// Secondary indicators row with animated donut charts
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: StatIndicatorCard(
|
child: StatIndicatorCardAnimated(
|
||||||
progress: statsController.reportsProgress.value,
|
progress: statsController.reportsProgress.value,
|
||||||
value: statsController.reportsValue.value,
|
value: statsController.reportsValue.value,
|
||||||
label: 'Reports',
|
label: 'Reports',
|
||||||
|
@ -71,7 +71,7 @@ class StatisticsView extends StatelessWidget {
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: StatIndicatorCard(
|
child: StatIndicatorCardAnimated(
|
||||||
progress: statsController.zoneMinProgress.value,
|
progress: statsController.zoneMinProgress.value,
|
||||||
value: statsController.zoneMinValue.value,
|
value: statsController.zoneMinValue.value,
|
||||||
label: 'Safety Score',
|
label: 'Safety Score',
|
||||||
|
@ -80,7 +80,7 @@ class StatisticsView extends StatelessWidget {
|
||||||
),
|
),
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: StatIndicatorCard(
|
child: StatIndicatorCardAnimated(
|
||||||
progress: statsController.mindfulProgress.value,
|
progress: statsController.mindfulProgress.value,
|
||||||
value: statsController.mindfulValue.value,
|
value: statsController.mindfulValue.value,
|
||||||
label: 'Solved Rate',
|
label: 'Solved Rate',
|
||||||
|
|
Loading…
Reference in New Issue