done with employee list dropdown updation

This commit is contained in:
Manish 2025-11-18 17:49:26 +05:30
parent 7c4f86dd94
commit 7a816ed26e

View File

@ -1,3 +1,4 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:on_field_work/helpers/utils/base_bottom_sheet.dart';
@ -24,33 +25,67 @@ class EmployeeSelectionBottomSheet extends StatefulWidget {
class _EmployeeSelectionBottomSheetState
extends State<EmployeeSelectionBottomSheet> {
final TextEditingController _searchController = TextEditingController();
final RxBool _isSearching = false.obs;
final RxList<EmployeeModel> _searchResults = <EmployeeModel>[].obs;
final RxList<EmployeeModel> _allResults = <EmployeeModel>[].obs;
late RxList<EmployeeModel> _selectedEmployees;
Timer? _debounce;
@override
void initState() {
super.initState();
_selectedEmployees = RxList<EmployeeModel>.from(widget.initiallySelected);
_searchEmployees('');
_performSearch('');
}
@override
void dispose() {
_debounce?.cancel();
_searchController.dispose();
super.dispose();
}
Future<void> _searchEmployees(String query) async {
// ------------------------------------------------------
// 🔥 Optimized debounce-based search
// ------------------------------------------------------
void _onSearchChanged(String query) {
_debounce?.cancel();
_debounce = Timer(const Duration(milliseconds: 300), () {
_performSearch(query.trim());
});
}
Future<void> _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<String, dynamic>))
.toList();
_searchResults.assignAll(results);
// ------------------------------------------------------
// 🔥 Auto-move selected employees to top
// ------------------------------------------------------
results.sort((a, b) {
final aSel = _selectedEmployees.contains(a) ? 0 : 1;
final bSel = _selectedEmployees.contains(b) ? 0 : 1;
if (aSel != bSel) return aSel.compareTo(bSel);
return a.name.toLowerCase().compareTo(b.name.toLowerCase());
});
_allResults.assignAll(results);
_isSearching.value = false;
}
// ------------------------------------------------------
// Handle tap & checkbox
// ------------------------------------------------------
void _toggleEmployee(EmployeeModel emp) {
if (widget.multipleSelection) {
if (_selectedEmployees.contains(emp)) {
@ -61,9 +96,14 @@ class _EmployeeSelectionBottomSheetState
} else {
_selectedEmployees.assignAll([emp]);
}
_selectedEmployees.refresh(); // important for Obx rebuild
// Re-sort list after each toggle
_performSearch(_searchController.text.trim());
}
// ------------------------------------------------------
// Submit selection
// ------------------------------------------------------
void _handleSubmit() {
if (widget.multipleSelection) {
Navigator.of(context).pop(_selectedEmployees.toList());
@ -73,11 +113,14 @@ class _EmployeeSelectionBottomSheetState
}
}
// ------------------------------------------------------
// Search bar widget
// ------------------------------------------------------
Widget _searchBar() => Padding(
padding: const EdgeInsets.symmetric(vertical: 8),
child: TextField(
controller: _searchController,
onChanged: _searchEmployees,
onChanged: _onSearchChanged,
decoration: InputDecoration(
hintText: 'Search employees...',
filled: true,
@ -88,7 +131,7 @@ class _EmployeeSelectionBottomSheetState
icon: const Icon(Icons.close, color: Colors.grey),
onPressed: () {
_searchController.clear();
_searchEmployees('');
_performSearch('');
},
)
: null,
@ -102,60 +145,52 @@ class _EmployeeSelectionBottomSheetState
),
);
// ------------------------------------------------------
// Employee list (optimized)
// ------------------------------------------------------
Widget _employeeList() => Expanded(
child: Obx(() {
if (_isSearching.value) {
return const Center(child: CircularProgressIndicator());
}
if (_searchResults.isEmpty) {
return const Center(child: Text("No employees found"));
}
final results = _allResults;
return ListView.builder(
padding: const EdgeInsets.symmetric(vertical: 4),
itemCount: _searchResults.length,
itemCount: results.length,
itemBuilder: (context, index) {
final emp = _searchResults[index];
final emp = results[index];
final isSelected = _selectedEmployees.contains(emp);
return Obx(() {
final isSelected = _selectedEmployees.contains(emp);
return ListTile(
leading: CircleAvatar(
backgroundColor: Colors.blueAccent,
child: Text(
(emp.firstName.isNotEmpty ? emp.firstName[0] : 'U')
.toUpperCase(),
style: const TextStyle(color: Colors.white),
),
return ListTile(
leading: CircleAvatar(
backgroundColor: Colors.blueAccent,
child: Text(
(emp.firstName.isNotEmpty ? emp.firstName[0] : 'U')
.toUpperCase(),
style: const TextStyle(color: Colors.white),
),
title: Text('${emp.firstName} ${emp.lastName}'),
subtitle: Text(emp.email),
trailing: Checkbox(
value: isSelected,
onChanged: (_) {
FocusScope.of(context).unfocus(); // hide keyboard
_toggleEmployee(emp);
},
fillColor: MaterialStateProperty.resolveWith<Color>(
(states) => states.contains(MaterialState.selected)
? Colors.blueAccent
: Colors.white,
),
),
title: Text('${emp.firstName} ${emp.lastName}'),
subtitle: Text(emp.email),
trailing: Checkbox(
value: isSelected,
onChanged: (_) => _toggleEmployee(emp),
fillColor: MaterialStateProperty.resolveWith<Color>(
(states) => states.contains(MaterialState.selected)
? Colors.blueAccent
: Colors.white,
),
onTap: () {
FocusScope.of(context).unfocus();
_toggleEmployee(emp);
},
contentPadding:
const EdgeInsets.symmetric(horizontal: 0, vertical: 4),
);
});
),
onTap: () => _toggleEmployee(emp),
contentPadding:
const EdgeInsets.symmetric(horizontal: 0, vertical: 4),
);
},
);
}),
);
// ------------------------------------------------------
// Build bottom sheet
// ------------------------------------------------------
@override
Widget build(BuildContext context) {
return BaseBottomSheet(
@ -164,10 +199,12 @@ class _EmployeeSelectionBottomSheetState
onSubmit: _handleSubmit,
child: SizedBox(
height: MediaQuery.of(context).size.height * 0.7,
child: Column(children: [
_searchBar(),
_employeeList(),
]),
child: Column(
children: [
_searchBar(),
_employeeList(),
],
),
),
);
}