feat: Implement loading skeletons in dashboard and layout screens for better UX
This commit is contained in:
parent
660bd3cdf1
commit
ef6521faa2
@ -319,7 +319,7 @@ class ApiService {
|
||||
};
|
||||
|
||||
final response = await _postRequest(
|
||||
ApiEndpoints.commentTask,
|
||||
ApiEndpoints.reportTask,
|
||||
body,
|
||||
customTimeout: extendedTimeout,
|
||||
);
|
||||
|
@ -133,8 +133,21 @@ class _DashboardScreenState extends State<DashboardScreen> with UIMixin {
|
||||
return GetBuilder<ProjectController>(
|
||||
id: 'dashboard_controller',
|
||||
builder: (controller) {
|
||||
final bool isLoading = controller.isLoading.value;
|
||||
final bool isProjectSelected = controller.selectedProject != null;
|
||||
|
||||
if (isLoading) {
|
||||
return Wrap(
|
||||
spacing: 10,
|
||||
runSpacing: 10,
|
||||
children: List.generate(
|
||||
4,
|
||||
(index) =>
|
||||
_buildStatCardSkeleton(MediaQuery.of(context).size.width / 3),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
@ -184,6 +197,33 @@ class _DashboardScreenState extends State<DashboardScreen> with UIMixin {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatCardSkeleton(double width) {
|
||||
return MyCard.bordered(
|
||||
width: width,
|
||||
height: 100,
|
||||
paddingAll: 5,
|
||||
borderRadiusAll: 10,
|
||||
border: Border.all(color: Colors.grey.withOpacity(0.15)),
|
||||
shadow: MyShadow(elevation: 1.5, position: MyShadowPosition.bottom),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
MyContainer.rounded(
|
||||
paddingAll: 12,
|
||||
color: Colors.grey.shade300,
|
||||
child: const SizedBox(width: 18, height: 18),
|
||||
),
|
||||
MySpacing.height(8),
|
||||
Container(
|
||||
height: 12,
|
||||
width: 60,
|
||||
color: Colors.grey.shade300,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildStatCard(_StatItem statItem, double width, bool isEnabled) {
|
||||
return Opacity(
|
||||
opacity: isEnabled ? 1.0 : 0.4,
|
||||
|
@ -68,7 +68,6 @@ class _LayoutState extends State<Layout> {
|
||||
),
|
||||
],
|
||||
),
|
||||
// Project dropdown overlay
|
||||
Obx(() {
|
||||
if (!projectController.isProjectSelectionExpanded.value) {
|
||||
return const SizedBox.shrink();
|
||||
@ -102,6 +101,12 @@ class _LayoutState extends State<Layout> {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
child: Obx(() {
|
||||
final isLoading = projectController.isLoading.value;
|
||||
|
||||
if (isLoading) {
|
||||
return _buildLoadingSkeleton();
|
||||
}
|
||||
|
||||
final isExpanded = projectController.isProjectSelectionExpanded.value;
|
||||
final selectedProjectId = projectController.selectedProjectId?.value;
|
||||
final selectedProject = projectController.projects.firstWhereOrNull(
|
||||
@ -220,8 +225,6 @@ class _LayoutState extends State<Layout> {
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Expanded Project List inside card — only show if projects exist
|
||||
if (isExpanded && hasProjects)
|
||||
Positioned(
|
||||
top: 70,
|
||||
@ -240,6 +243,56 @@ class _LayoutState extends State<Layout> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLoadingSkeleton() {
|
||||
return Card(
|
||||
elevation: 4,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
margin: EdgeInsets.zero,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
height: 50,
|
||||
width: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.shade300,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
height: 18,
|
||||
width: 140,
|
||||
color: Colors.grey.shade300,
|
||||
),
|
||||
const SizedBox(height: 6),
|
||||
Container(
|
||||
height: 14,
|
||||
width: 100,
|
||||
color: Colors.grey.shade200,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Container(
|
||||
height: 30,
|
||||
width: 30,
|
||||
color: Colors.grey.shade300,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildProjectList(BuildContext context, bool isMobile) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
Loading…
x
Reference in New Issue
Block a user