added loading componant

This commit is contained in:
Vaibhav Surve 2025-05-02 15:27:19 +05:30
parent cecfb294e5
commit 4207e235e5
5 changed files with 248 additions and 137 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 KiB

View File

@ -0,0 +1,105 @@
import 'dart:ui';
import 'package:flutter/material.dart';
import 'package:loading_animation_widget/loading_animation_widget.dart';
import 'package:marco/images.dart';
class LoadingComponent extends StatelessWidget {
final bool isLoading;
final Widget child;
final Color overlayColor;
final double overlayOpacity;
final double imageSize;
final bool showDots;
final String loadingText;
const LoadingComponent({
Key? key,
required this.isLoading,
required this.child,
this.overlayColor = Colors.black,
this.overlayOpacity = 0.2,
this.imageSize = 100,
this.showDots = true,
this.loadingText = 'Loading...',
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Stack(
children: [
child,
if (isLoading) _buildLoadingOverlay(),
],
);
}
Widget _buildLoadingOverlay() {
return Positioned.fill(
child: Semantics(
label: 'Loading...',
child: Stack(
children: [
BackdropFilter(
filter: ImageFilter.blur(sigmaX: 5.0, sigmaY: 5.0),
child: Container(
color: overlayColor.withOpacity(overlayOpacity),
),
),
Center(
child: _LoadingAnimation(
imageSize: imageSize,
showDots: showDots,
loadingText: loadingText,
),
),
],
),
),
);
}
}
class _LoadingAnimation extends StatelessWidget {
final double imageSize;
final bool showDots;
final String loadingText;
const _LoadingAnimation({
Key? key,
required this.imageSize,
required this.showDots,
required this.loadingText,
}) : super(key: key);
static const _textStyle = TextStyle(
fontSize: 12,
color: Colors.white,
);
@override
Widget build(BuildContext context) {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Image.asset(
Images.loadingLogo,
height: imageSize,
width: imageSize,
fit: BoxFit.contain,
),
const SizedBox(height: 8),
Text(
loadingText,
style: _textStyle,
textAlign: TextAlign.center,
),
const SizedBox(height: 4),
if (showDots)
LoadingAnimationWidget.waveDots(
color: const Color(0xFFEF0000),
size: 50,
),
],
);
}
}

View File

@ -10,7 +10,7 @@ class Images {
static String logoDark = 'assets/logo/logo_dark.png'; static String logoDark = 'assets/logo/logo_dark.png';
static String logoDarkSmall = 'assets/logo/logo_dark_small.png'; static String logoDarkSmall = 'assets/logo/logo_dark_small.png';
static String authBackground = 'assets/auth_background.jpg'; static String authBackground = 'assets/auth_background.jpg';
static String loadingLogo = 'assets/logo/loading_logo.png';
static String randomImage(List<String> images) { static String randomImage(List<String> images) {
return images[Random().nextInt(images.length)]; return images[Random().nextInt(images.length)];
} }

View File

@ -21,6 +21,7 @@ import 'package:marco/helpers/utils/permission_constants.dart';
import 'package:marco/helpers/utils/attendance_actions.dart'; import 'package:marco/helpers/utils/attendance_actions.dart';
import 'package:marco/helpers/widgets/my_refresh_wrapper.dart'; import 'package:marco/helpers/widgets/my_refresh_wrapper.dart';
import 'package:marco/model/my_paginated_table.dart'; import 'package:marco/model/my_paginated_table.dart';
import 'package:marco/helpers/widgets/my_loading_component.dart';
class AttendanceScreen extends StatefulWidget { class AttendanceScreen extends StatefulWidget {
const AttendanceScreen({super.key}); const AttendanceScreen({super.key});
@ -51,149 +52,153 @@ class _AttendanceScreenState extends State<AttendanceScreen> with UIMixin {
init: attendanceController, init: attendanceController,
tag: 'attendance_dashboard_controller', tag: 'attendance_dashboard_controller',
builder: (controller) { builder: (controller) {
return Column( return LoadingComponent(
crossAxisAlignment: CrossAxisAlignment.start, isLoading: controller.isLoading.value,
children: [ loadingText: 'Loading Attendance...',
Padding( child: Column(
padding: MySpacing.x(flexSpacing), crossAxisAlignment: CrossAxisAlignment.start,
child: Row( children: [
mainAxisAlignment: MainAxisAlignment.spaceBetween, Padding(
children: [ padding: MySpacing.x(flexSpacing),
MyText.titleMedium("Attendance", child: Row(
fontSize: 18, fontWeight: 600), mainAxisAlignment: MainAxisAlignment.spaceBetween,
MyBreadcrumb( children: [
children: [ MyText.titleMedium("Attendance",
MyBreadcrumbItem(name: 'Dashboard'), fontSize: 18, fontWeight: 600),
MyBreadcrumbItem(name: 'Attendance', active: true), MyBreadcrumb(
], children: [
), MyBreadcrumbItem(name: 'Dashboard'),
], MyBreadcrumbItem(name: 'Attendance', active: true),
), ],
),
MySpacing.height(flexSpacing),
Padding(
padding: MySpacing.x(flexSpacing / 2),
child: MyFlex(
children: [
// Popup Menu for Project Selection
MyFlexItem(
sizes: 'lg-12',
child: MyContainer.bordered(
padding: MySpacing.xy(8, 8),
child: PopupMenuButton<String>(
onSelected: (value) {
setState(() {
attendanceController.selectedProjectId = value;
attendanceController
.fetchEmployeesByProject(value);
attendanceController.fetchAttendanceLogs(value);
});
},
itemBuilder: (BuildContext context) {
if (attendanceController.projects.isEmpty) {
return [
PopupMenuItem<String>(
value: '',
child: MyText.bodySmall('No Data',
fontWeight: 600),
)
];
}
return attendanceController.projects
.map((project) {
return PopupMenuItem<String>(
value: project.id.toString(),
height: 32,
child: MyText.bodySmall(
project.name,
color: theme.colorScheme.onSurface,
fontWeight: 600,
),
);
}).toList();
},
color: theme.cardTheme.color,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
MyText.labelSmall(
attendanceController.selectedProjectId != null
? attendanceController.projects
.firstWhereOrNull((proj) =>
proj.id.toString() ==
attendanceController
.selectedProjectId)
?.name ??
'Select a Project'
: 'Select a Project',
color: theme.colorScheme.onSurface,
),
Icon(LucideIcons.chevron_down,
size: 20,
color: theme.colorScheme.onSurface),
],
),
),
), ),
), ],
),
// Tabs for Employee List, Logs, and Regularization ),
MyFlexItem( MySpacing.height(flexSpacing),
sizes: 'lg-12', Padding(
child: Obx(() { padding: MySpacing.x(flexSpacing / 2),
bool hasRegularizationPermission = child: MyFlex(
permissionController.hasPermission( children: [
Permissions.regularizeAttendance); // Popup Menu for Project Selection
MyFlexItem(
final tabs = <Tab>[ sizes: 'lg-12',
const Tab(text: 'Employee List'), child: MyContainer.bordered(
const Tab(text: 'Logs'), padding: MySpacing.xy(8, 8),
if (hasRegularizationPermission) child: PopupMenuButton<String>(
const Tab(text: 'Regularization'), onSelected: (value) {
]; setState(() {
attendanceController.selectedProjectId = value;
final views = <Widget>[ attendanceController
employeeListTab(), .fetchEmployeesByProject(value);
reportsTab(context), attendanceController.fetchAttendanceLogs(value);
if (hasRegularizationPermission) });
regularizationTab(context), },
]; itemBuilder: (BuildContext context) {
if (attendanceController.projects.isEmpty) {
return DefaultTabController( return [
length: tabs.length, PopupMenuItem<String>(
child: MyCard.bordered( value: '',
borderRadiusAll: 4, child: MyText.bodySmall('No Data',
border: fontWeight: 600),
Border.all(color: Colors.grey.withAlpha(50)), )
shadow: MyShadow( ];
elevation: 1, }
position: MyShadowPosition.bottom), return attendanceController.projects
paddingAll: 10, .map((project) {
child: Column( return PopupMenuItem<String>(
crossAxisAlignment: CrossAxisAlignment.start, value: project.id.toString(),
height: 32,
child: MyText.bodySmall(
project.name,
color: theme.colorScheme.onSurface,
fontWeight: 600,
),
);
}).toList();
},
color: theme.cardTheme.color,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [ children: [
TabBar( MyText.labelSmall(
labelColor: theme.colorScheme.primary, attendanceController.selectedProjectId != null
unselectedLabelColor: theme ? attendanceController.projects
.colorScheme.onSurface .firstWhereOrNull((proj) =>
.withAlpha(150), proj.id.toString() ==
tabs: tabs, attendanceController
), .selectedProjectId)
MySpacing.height(16), ?.name ??
SizedBox( 'Select a Project'
height: 550, : 'Select a Project',
child: TabBarView(children: views), color: theme.colorScheme.onSurface,
), ),
Icon(LucideIcons.chevron_down,
size: 20,
color: theme.colorScheme.onSurface),
], ],
), ),
), ),
); ),
}), ),
),
], // Tabs for Employee List, Logs, and Regularization
MyFlexItem(
sizes: 'lg-12',
child: Obx(() {
bool hasRegularizationPermission =
permissionController.hasPermission(
Permissions.regularizeAttendance);
final tabs = <Tab>[
const Tab(text: 'Employee List'),
const Tab(text: 'Logs'),
if (hasRegularizationPermission)
const Tab(text: 'Regularization'),
];
final views = <Widget>[
employeeListTab(),
reportsTab(context),
if (hasRegularizationPermission)
regularizationTab(context),
];
return DefaultTabController(
length: tabs.length,
child: MyCard.bordered(
borderRadiusAll: 4,
border:
Border.all(color: Colors.grey.withAlpha(50)),
shadow: MyShadow(
elevation: 1,
position: MyShadowPosition.bottom),
paddingAll: 10,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TabBar(
labelColor: theme.colorScheme.primary,
unselectedLabelColor: theme
.colorScheme.onSurface
.withAlpha(150),
tabs: tabs,
),
MySpacing.height(16),
SizedBox(
height: 550,
child: TabBarView(children: views),
),
],
),
),
);
}),
),
],
),
), ),
), ],
], ),
); );
}, },
), ),

View File

@ -99,6 +99,7 @@ flutter:
- assets/coin/ - assets/coin/
- assets/dummy/ecommerce/ - assets/dummy/ecommerce/
- assets/dummy/single_product/ - assets/dummy/single_product/
- assets/logo/loading_logo.png
# assets: # assets:
# - images/a_dot_burr.jpeg # - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg # - images/a_dot_ham.jpeg