done with employee list dropdown updation
This commit is contained in:
parent
7c4f86dd94
commit
7a816ed26e
@ -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,24 +145,20 @@ 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];
|
||||
|
||||
return Obx(() {
|
||||
final emp = results[index];
|
||||
final isSelected = _selectedEmployees.contains(emp);
|
||||
|
||||
return ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: Colors.blueAccent,
|
||||
@ -133,29 +172,25 @@ class _EmployeeSelectionBottomSheetState
|
||||
subtitle: Text(emp.email),
|
||||
trailing: Checkbox(
|
||||
value: isSelected,
|
||||
onChanged: (_) {
|
||||
FocusScope.of(context).unfocus(); // hide keyboard
|
||||
_toggleEmployee(emp);
|
||||
},
|
||||
onChanged: (_) => _toggleEmployee(emp),
|
||||
fillColor: MaterialStateProperty.resolveWith<Color>(
|
||||
(states) => states.contains(MaterialState.selected)
|
||||
? Colors.blueAccent
|
||||
: Colors.white,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
FocusScope.of(context).unfocus();
|
||||
_toggleEmployee(emp);
|
||||
},
|
||||
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: [
|
||||
child: Column(
|
||||
children: [
|
||||
_searchBar(),
|
||||
_employeeList(),
|
||||
]),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user