import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:marco/controller/layout/layout_controller.dart'; import 'package:marco/helpers/widgets/my_responsive.dart'; import 'package:marco/helpers/widgets/my_text.dart'; import 'package:marco/helpers/services/storage/local_storage.dart'; import 'package:marco/model/employee_info.dart'; import 'package:marco/helpers/services/api_endpoints.dart'; import 'package:marco/images.dart'; import 'package:marco/controller/project_controller.dart'; import 'package:marco/view/layouts/user_profile_right_bar.dart'; class Layout extends StatefulWidget { final Widget? child; final Widget? floatingActionButton; const Layout({super.key, this.child, this.floatingActionButton}); @override State createState() => _LayoutState(); } class _LayoutState extends State { final LayoutController controller = LayoutController(); final EmployeeInfo? employeeInfo = LocalStorage.getEmployeeInfo(); final bool isBetaEnvironment = ApiEndpoints.baseUrl.contains("stage"); final projectController = Get.find(); bool hasMpin = true; @override void initState() { super.initState(); _checkMpinStatus(); } Future _checkMpinStatus() async { final bool mpinStatus = await LocalStorage.getIsMpin(); if (mounted) { setState(() { hasMpin = mpinStatus; }); } } @override Widget build(BuildContext context) { return MyResponsive(builder: (context, _, screenMT) { return GetBuilder( init: controller, builder: (_) { return (screenMT.isMobile || screenMT.isTablet) ? _buildScaffold(context, isMobile: true) : _buildScaffold(context); }, ); }); } Widget _buildScaffold(BuildContext context, {bool isMobile = false}) { return Scaffold( key: controller.scaffoldKey, endDrawer: const UserProfileBar(), floatingActionButton: widget.floatingActionButton, body: SafeArea( child: GestureDetector( behavior: HitTestBehavior.translucent, onTap: () { if (projectController.isProjectSelectionExpanded.value) { projectController.isProjectSelectionExpanded.value = false; } }, child: Stack( children: [ Column( children: [ _buildHeader(context, isMobile), Expanded( child: SingleChildScrollView( key: controller.scrollKey, padding: EdgeInsets.symmetric( horizontal: 0, vertical: isMobile ? 16 : 32), child: widget.child, ), ), ], ), _buildProjectDropdown(context, isMobile), ], ), ), ), ); } /// Header Section Widget _buildHeader(BuildContext context, bool isMobile) { 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( (p) => p.id == selectedProjectId, ); final hasProjects = projectController.projects.isNotEmpty; if (!hasProjects) { projectController.selectedProjectId.value = ''; } else if (selectedProject == null) { projectController .updateSelectedProject(projectController.projects.first.id); } return Card( elevation: 4, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), margin: EdgeInsets.zero, clipBehavior: Clip.antiAlias, child: Stack( children: [ Padding( padding: const EdgeInsets.all(10), child: Row( children: [ ClipRRect( borderRadius: BorderRadius.circular(8), child: Image.asset( Images.logoDark, height: 50, width: 50, fit: BoxFit.contain, ), ), const SizedBox(width: 12), Expanded( child: hasProjects ? (projectController.projects.length > 1 ? GestureDetector( onTap: () => projectController .isProjectSelectionExpanded .toggle(), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Expanded( child: Row( children: [ Expanded( child: MyText.bodyLarge( selectedProject?.name ?? "Select Project", fontWeight: 700, maxLines: 1, overflow: TextOverflow.ellipsis, ), ), Icon( isExpanded ? Icons .arrow_drop_up_outlined : Icons .arrow_drop_down_outlined, color: Colors.black, ), ], ), ), ], ), MyText.bodyMedium( "Hi, ${employeeInfo?.firstName ?? ''}", color: Colors.black54, ), ], ), ) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.bodyLarge( selectedProject?.name ?? "No Project", fontWeight: 700, maxLines: 1, overflow: TextOverflow.ellipsis, ), MyText.bodyMedium( "Hi, ${employeeInfo?.firstName ?? ''}", color: Colors.black54, ), ], )) : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.bodyLarge( "No Project Assigned", fontWeight: 700, color: Colors.redAccent, ), MyText.bodyMedium( "Hi, ${employeeInfo?.firstName ?? ''}", color: Colors.black54, ), ], ), ), if (isBetaEnvironment) Container( margin: const EdgeInsets.only(left: 8), padding: const EdgeInsets.symmetric( horizontal: 8, vertical: 2), decoration: BoxDecoration( color: Colors.deepPurple, borderRadius: BorderRadius.circular(6), ), child: MyText.bodySmall( 'BETA', color: Colors.white, fontWeight: 700, ), ), Stack( clipBehavior: Clip.none, alignment: Alignment.center, children: [ IconButton( icon: const Icon(Icons.menu), onPressed: () => controller.scaffoldKey.currentState ?.openEndDrawer(), ), if (!hasMpin) Positioned( right: 10, top: 10, child: Container( width: 14, height: 14, decoration: BoxDecoration( color: Colors.redAccent, shape: BoxShape.circle, border: Border.all(color: Colors.white, width: 2), ), ), ), ], ) ], ), ), if (isExpanded && hasProjects) Positioned( top: 70, left: 0, right: 0, child: Container( padding: const EdgeInsets.all(10), color: Colors.white, child: _buildProjectList(context, isMobile), ), ), ], ), ); }), ); } /// Loading Skeleton for Header 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, ), ], ), ), ); } /// Project List Popup Widget _buildProjectDropdown(BuildContext context, bool isMobile) { return Obx(() { if (!projectController.isProjectSelectionExpanded.value) { return const SizedBox.shrink(); } return Positioned( top: 95, left: 16, right: 16, child: Material( elevation: 4, borderRadius: BorderRadius.circular(12), child: Container( decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), ), padding: const EdgeInsets.all(10), child: _buildProjectList(context, isMobile), ), ), ); }); } Widget _buildProjectList(BuildContext context, bool isMobile) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ MyText.titleSmall("Switch Project", fontWeight: 600), const SizedBox(height: 4), ConstrainedBox( constraints: BoxConstraints( maxHeight: isMobile ? MediaQuery.of(context).size.height * 0.4 : 400, ), child: ListView.builder( shrinkWrap: true, itemCount: projectController.projects.length, itemBuilder: (context, index) { final project = projectController.projects[index]; final selectedId = projectController.selectedProjectId.value; final isSelected = project.id == selectedId; return RadioListTile( value: project.id, groupValue: selectedId, onChanged: (value) { projectController.updateSelectedProject(value!); projectController.isProjectSelectionExpanded.value = false; }, title: Text( project.name, style: TextStyle( fontWeight: isSelected ? FontWeight.bold : FontWeight.normal, color: isSelected ? Colors.blueAccent : Colors.black87, ), ), contentPadding: const EdgeInsets.symmetric(horizontal: 0), activeColor: Colors.blueAccent, tileColor: isSelected ? Colors.blueAccent.withOpacity(0.1) : Colors.transparent, shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(6), ), visualDensity: const VisualDensity(vertical: -4), ); }, ), ), ], ); } }