diff --git a/lib/helpers/widgets/custom_app_bar.dart b/lib/helpers/widgets/custom_app_bar.dart index fc2efd4..994d823 100644 --- a/lib/helpers/widgets/custom_app_bar.dart +++ b/lib/helpers/widgets/custom_app_bar.dart @@ -5,11 +5,11 @@ import 'package:on_field_work/helpers/widgets/my_spacing.dart'; import 'package:on_field_work/helpers/widgets/my_text.dart'; import 'package:on_field_work/helpers/utils/mixins/ui_mixin.dart'; -class CustomAppBar extends StatelessWidget +class CustomAppBar extends StatefulWidget with UIMixin implements PreferredSizeWidget { final String title; - final String? projectName; + final String? projectName; // If passed, show static text final VoidCallback? onBackPressed; final Color? backgroundColor; @@ -24,77 +24,186 @@ class CustomAppBar extends StatelessWidget @override Size get preferredSize => const Size.fromHeight(72); + @override + State createState() => _CustomAppBarState(); +} + +class _CustomAppBarState extends State with UIMixin { + final ProjectController projectController = Get.find(); + OverlayEntry? _overlayEntry; + final LayerLink _layerLink = LayerLink(); + + void _toggleDropdown() { + if (_overlayEntry == null) { + _overlayEntry = _createOverlayEntry(); + Overlay.of(context).insert(_overlayEntry!); + } else { + _overlayEntry?.remove(); + _overlayEntry = null; + } + } + + OverlayEntry _createOverlayEntry() { + final renderBox = context.findRenderObject() as RenderBox; + final size = renderBox.size; + final offset = renderBox.localToGlobal(Offset.zero); + + return OverlayEntry( + builder: (context) => GestureDetector( + onTap: () { + _toggleDropdown(); + }, + behavior: HitTestBehavior.translucent, + child: Stack( + children: [ + Positioned( + left: offset.dx + 16, + top: offset.dy + size.height, + width: size.width - 32, + child: Material( + elevation: 4, + borderRadius: BorderRadius.circular(5), + child: Container( + height: MediaQuery.of(context).size.height * 0.33, + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.white, + borderRadius: BorderRadius.circular(5), + ), + child: Column( + children: [ + TextField( + decoration: InputDecoration( + hintText: "Search project...", + isDense: true, + prefixIcon: const Icon(Icons.search), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(5)), + ), + ), + Expanded( + child: ListView.builder( + itemCount: projectController.projects.length, + itemBuilder: (_, index) { + final project = projectController.projects[index]; + return RadioListTile( + dense: true, + value: project.id, + groupValue: + projectController.selectedProjectId.value, + onChanged: (v) { + if (v != null) { + projectController.updateSelectedProject(v); + _toggleDropdown(); + } + }, + title: Text(project.name), + ); + }, + ), + ), + ], + ), + ), + ), + ), + ], + ), + ), + ); + } + @override Widget build(BuildContext context) { final Color effectiveBackgroundColor = - backgroundColor ?? contentTheme.primary; + widget.backgroundColor ?? contentTheme.primary; const Color onPrimaryColor = Colors.white; - const double horizontalPadding = 16.0; + + final bool showDropdown = widget.projectName == null; return AppBar( backgroundColor: effectiveBackgroundColor, elevation: 0, automaticallyImplyLeading: false, titleSpacing: 0, - shadowColor: Colors.transparent, - + shadowColor: Colors.transparent, leading: Padding( - padding: MySpacing.only(left: horizontalPadding), + padding: MySpacing.only(left: 16), child: IconButton( icon: const Icon( Icons.arrow_back_ios_new, color: onPrimaryColor, size: 20, ), - onPressed: onBackPressed ?? () => Get.back(), + onPressed: widget.onBackPressed ?? () => Get.back(), padding: EdgeInsets.zero, constraints: const BoxConstraints(), ), ), title: Padding( - padding: MySpacing.only(right: horizontalPadding, left: 8), + padding: MySpacing.only(right: 16, left: 8), child: Row( children: [ Expanded( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, children: [ MyText.titleLarge( - title, + widget.title, fontWeight: 800, color: onPrimaryColor, overflow: TextOverflow.ellipsis, maxLines: 1, ), MySpacing.height(3), - GetBuilder( - builder: (projectController) { - final displayProjectName = projectName ?? - projectController.selectedProject?.name ?? - 'Select Project'; - return Row( - children: [ - const Icon(Icons.folder_open, - size: 14, color: onPrimaryColor), - MySpacing.width(4), - Flexible( - child: MyText.bodySmall( - displayProjectName, - fontWeight: 500, - color: onPrimaryColor.withOpacity(0.8), - overflow: TextOverflow.ellipsis, - maxLines: 1, + showDropdown + ? CompositedTransformTarget( + link: _layerLink, + child: GestureDetector( + onTap: _toggleDropdown, + child: Row( + children: [ + const Icon(Icons.folder_open, + size: 14, color: onPrimaryColor), + MySpacing.width(4), + Flexible( + child: Obx(() { + final projectName = projectController + .selectedProject?.name ?? + 'Select Project'; + return MyText.bodySmall( + projectName, + fontWeight: 500, + color: onPrimaryColor.withOpacity(0.8), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ); + }), + ), + MySpacing.width(2), + const Icon(Icons.keyboard_arrow_down, + size: 18, color: onPrimaryColor), + ], ), ), - MySpacing.width(2), - const Icon(Icons.keyboard_arrow_down, - size: 18, color: onPrimaryColor), - ], - ); - }, - ), + ) + : Row( + children: [ + const Icon(Icons.folder_open, + size: 14, color: onPrimaryColor), + MySpacing.width(4), + Flexible( + child: MyText.bodySmall( + widget.projectName!, + fontWeight: 500, + color: onPrimaryColor.withOpacity(0.8), + overflow: TextOverflow.ellipsis, + maxLines: 1, + ), + ), + ], + ), ], ), ), @@ -103,7 +212,7 @@ class CustomAppBar extends StatelessWidget ), actions: [ Padding( - padding: MySpacing.only(right: horizontalPadding), + padding: MySpacing.only(right: 16), child: IconButton( icon: const Icon(Icons.home, color: onPrimaryColor, size: 24), onPressed: () => Get.offAllNamed('/dashboard'), @@ -112,4 +221,10 @@ class CustomAppBar extends StatelessWidget ], ); } + + @override + void dispose() { + _overlayEntry?.remove(); + super.dispose(); + } }