From d4307814afc0f4968b16239f80beaf778eda07c1 Mon Sep 17 00:00:00 2001 From: Manish Date: Tue, 18 Nov 2025 17:49:26 +0530 Subject: [PATCH] done with employee list dropdown updation --- .../multiple_select_bottomsheet.dart | 93 +++++++++++++------ 1 file changed, 63 insertions(+), 30 deletions(-) diff --git a/lib/model/employees/multiple_select_bottomsheet.dart b/lib/model/employees/multiple_select_bottomsheet.dart index f3be00c..9685a4f 100644 --- a/lib/model/employees/multiple_select_bottomsheet.dart +++ b/lib/model/employees/multiple_select_bottomsheet.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:async'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:on_field_work/helpers/utils/base_bottom_sheet.dart'; @@ -26,23 +27,31 @@ class _EmployeeSelectionBottomSheetState extends State { final TextEditingController _searchController = TextEditingController(); + final RxBool _isSearching = false.obs; final RxList _allResults = [].obs; + final RxList _allResults = [].obs; + late RxList _selectedEmployees; Timer? _debounce; + Timer? _debounce; + @override void initState() { super.initState(); _selectedEmployees = RxList.from(widget.initiallySelected); _performSearch(''); + + _performSearch(''); } @override void dispose() { + _debounce?.cancel(); _debounce?.cancel(); _searchController.dispose(); super.dispose(); @@ -58,28 +67,36 @@ class _EmployeeSelectionBottomSheetState }); } + Future _performSearch(String query) async { + // ------------------------------------------------------ + // 🔥 Optimized debounce-based search + // ------------------------------------------------------ + void _onSearchChanged(String query) { + _debounce?.cancel(); + _debounce = Timer(const Duration(milliseconds: 300), () { + _performSearch(query.trim()); + }); + } + Future _performSearch(String query) async { _isSearching.value = true; + final data = await ApiService.searchEmployeesBasic(searchString: query); + final results = (data as List) .map((e) => EmployeeModel.fromJson(e as Map)) .toList(); // ------------------------------------------------------ - // Auto-move selected employees to top + // 🔥 Auto-move selected employees to top // ------------------------------------------------------ results.sort((a, b) { - if (widget.multipleSelection) { - // Only move selected employees to top in multi-select - final aSel = _selectedEmployees.contains(a) ? 0 : 1; - final bSel = _selectedEmployees.contains(b) ? 0 : 1; + final aSel = _selectedEmployees.contains(a) ? 0 : 1; + final bSel = _selectedEmployees.contains(b) ? 0 : 1; - if (aSel != bSel) return aSel.compareTo(bSel); - } - - // Otherwise, keep original order (or alphabetically if needed) + if (aSel != bSel) return aSel.compareTo(bSel); return a.name.toLowerCase().compareTo(b.name.toLowerCase()); }); @@ -88,6 +105,9 @@ class _EmployeeSelectionBottomSheetState _isSearching.value = false; } + // ------------------------------------------------------ + // Handle tap & checkbox + // ------------------------------------------------------ // ------------------------------------------------------ // Handle tap & checkbox // ------------------------------------------------------ @@ -104,8 +124,14 @@ class _EmployeeSelectionBottomSheetState // Re-sort list after each toggle _performSearch(_searchController.text.trim()); + + // Re-sort list after each toggle + _performSearch(_searchController.text.trim()); } + // ------------------------------------------------------ + // Submit selection + // ------------------------------------------------------ // ------------------------------------------------------ // Submit selection // ------------------------------------------------------ @@ -118,6 +144,9 @@ class _EmployeeSelectionBottomSheetState } } + // ------------------------------------------------------ + // Search bar widget + // ------------------------------------------------------ // ------------------------------------------------------ // Search bar widget // ------------------------------------------------------ @@ -126,6 +155,7 @@ class _EmployeeSelectionBottomSheetState child: TextField( controller: _searchController, onChanged: _onSearchChanged, + onChanged: _onSearchChanged, decoration: InputDecoration( hintText: 'Search employees...', filled: true, @@ -137,6 +167,7 @@ class _EmployeeSelectionBottomSheetState onPressed: () { _searchController.clear(); _performSearch(''); + _performSearch(''); }, ) : null, @@ -150,40 +181,25 @@ class _EmployeeSelectionBottomSheetState ), ); + // ------------------------------------------------------ + // Employee list (optimized) + // ------------------------------------------------------ // ------------------------------------------------------ // Employee list (optimized) // ------------------------------------------------------ Widget _employeeList() => Expanded( child: Obx(() { final results = _allResults; + final results = _allResults; return ListView.builder( padding: const EdgeInsets.symmetric(vertical: 4), itemCount: results.length, + itemCount: results.length, itemBuilder: (context, index) { final emp = results[index]; final isSelected = _selectedEmployees.contains(emp); - Widget trailingWidget; - - if (widget.multipleSelection) { - // Multiple selection → normal checkbox - trailingWidget = Checkbox( - value: isSelected, - onChanged: (_) => _toggleEmployee(emp), - fillColor: MaterialStateProperty.resolveWith( - (states) => states.contains(MaterialState.selected) - ? Colors.blueAccent - : Colors.white, - ), - ); - } else { - // Single selection → check circle - trailingWidget = isSelected - ? const Icon(Icons.check_circle, color: Colors.blueAccent) - : const Icon(Icons.circle_outlined, color: Colors.grey); - } - return ListTile( leading: CircleAvatar( backgroundColor: Colors.blueAccent, @@ -195,7 +211,15 @@ class _EmployeeSelectionBottomSheetState ), title: Text('${emp.firstName} ${emp.lastName}'), subtitle: Text(emp.email), - trailing: trailingWidget, + trailing: Checkbox( + value: isSelected, + onChanged: (_) => _toggleEmployee(emp), + fillColor: MaterialStateProperty.resolveWith( + (states) => states.contains(MaterialState.selected) + ? Colors.blueAccent + : Colors.white, + ), + ), onTap: () => _toggleEmployee(emp), contentPadding: const EdgeInsets.symmetric(horizontal: 0, vertical: 4), @@ -205,6 +229,9 @@ class _EmployeeSelectionBottomSheetState }), ); + // ------------------------------------------------------ + // Build bottom sheet + // ------------------------------------------------------ // ------------------------------------------------------ // Build bottom sheet // ------------------------------------------------------ @@ -222,6 +249,12 @@ class _EmployeeSelectionBottomSheetState _employeeList(), ], ), + child: Column( + children: [ + _searchBar(), + _employeeList(), + ], + ), ), ); }