feat(statistics): implement shimmer loading placeholders in statistics view

This commit is contained in:
vergiLgood1 2025-05-27 17:45:43 +07:00
parent b54c204963
commit 8bafa19e31
1 changed files with 231 additions and 70 deletions

View File

@ -2,8 +2,7 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:sigap/src/features/panic/presentation/controllers/main_safety_indicator_controller.dart';
import 'package:sigap/src/features/panic/presentation/controllers/statistics_view_controller.dart';
import 'package:sigap/src/shared/widgets/loaders/custom_circular_loader.dart';
import 'package:sigap/src/utils/constants/colors.dart';
import 'package:sigap/src/utils/loaders/shimmer.dart';
import 'crime_stats_header.dart';
import 'main_safety_indicator.dart';
@ -24,7 +23,10 @@ class StatisticsView extends StatelessWidget {
// Main content
SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
child: Column(
child:
statsController.isLoading.value
? _buildShimmerLoading(context)
: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// District and date information
@ -49,7 +51,9 @@ class StatisticsView extends StatelessWidget {
progress: safetyController.progress.value,
title: safetyController.title.value,
label: safetyController.label.value,
color: _getSafetyColor(safetyController.progress.value),
color: _getSafetyColor(
safetyController.progress.value,
),
),
const SizedBox(height: 15),
@ -94,11 +98,6 @@ class StatisticsView extends StatelessWidget {
),
),
// Loading indicator
if (statsController.isLoading.value)
Center(child: TLoader.centeredLoader(color: TColors.primary),
),
// Error message
if (statsController.errorMessage.value.isNotEmpty)
Positioned(
@ -124,6 +123,168 @@ class StatisticsView extends StatelessWidget {
);
}
// Build shimmer loading placeholders
Widget _buildShimmerLoading(BuildContext context) {
return Column(
children: [
// Header shimmer
_buildHeaderShimmer(),
const SizedBox(height: 15),
// Main indicator shimmer
_buildMainIndicatorShimmer(),
const SizedBox(height: 15),
// Stats cards shimmer
Row(
children: [
Expanded(child: _buildStatCardShimmer()),
const SizedBox(width: 10),
Expanded(child: _buildStatCardShimmer()),
const SizedBox(width: 10),
Expanded(child: _buildStatCardShimmer()),
],
),
const SizedBox(height: 15),
// Recovery indicator shimmer
_buildRecoveryShimmer(),
],
);
}
// Header shimmer placeholder
Widget _buildHeaderShimmer() {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 5,
spreadRadius: 1,
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
const TShimmerEffect(width: 150, height: 20, radius: 4),
Container(
width: 24,
height: 24,
decoration: BoxDecoration(
color: Colors.grey.withOpacity(0.2),
shape: BoxShape.circle,
),
),
],
),
const SizedBox(height: 15),
Row(
children: const [
TShimmerEffect(width: 120, height: 30, radius: 5),
SizedBox(width: 10),
TShimmerEffect(width: 80, height: 30, radius: 5),
],
),
],
),
);
}
// Main safety indicator shimmer
Widget _buildMainIndicatorShimmer() {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 5,
spreadRadius: 1,
),
],
),
child: Column(
children: [
const TShimmerEffect(width: 120, height: 120, radius: 60), // Circle
const SizedBox(height: 15),
const TShimmerEffect(width: 100, height: 20, radius: 4), // Text
],
),
);
}
// Stat card shimmer
Widget _buildStatCardShimmer() {
return Container(
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 5,
spreadRadius: 1,
),
],
),
child: Column(
children: const [
TShimmerEffect(width: 60, height: 60, radius: 30), // Circle
SizedBox(height: 10),
TShimmerEffect(width: 40, height: 18, radius: 4), // Value
SizedBox(height: 5),
TShimmerEffect(width: 60, height: 14, radius: 4), // Label
],
),
);
}
// Recovery indicator shimmer
Widget _buildRecoveryShimmer() {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(15),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(10),
boxShadow: [
BoxShadow(
color: Colors.grey.withOpacity(0.1),
blurRadius: 5,
spreadRadius: 1,
),
],
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: const [
TShimmerEffect(width: 140, height: 20, radius: 4), // Title
SizedBox(height: 15),
TShimmerEffect(
width: double.infinity,
height: 12,
radius: 6,
), // Progress bar
SizedBox(height: 10),
TShimmerEffect(width: 80, height: 16, radius: 4), // Status text
],
),
);
}
// Helper to get district name from district ID
String _getDistrictName(String districtId) {
final statsController = Get.find<StatisticsViewController>();