done with employee list dropdown updation
This commit is contained in:
parent
d983620524
commit
d4307814af
@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:async';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:on_field_work/helpers/utils/base_bottom_sheet.dart';
|
import 'package:on_field_work/helpers/utils/base_bottom_sheet.dart';
|
||||||
@ -26,23 +27,31 @@ class _EmployeeSelectionBottomSheetState
|
|||||||
extends State<EmployeeSelectionBottomSheet> {
|
extends State<EmployeeSelectionBottomSheet> {
|
||||||
final TextEditingController _searchController = TextEditingController();
|
final TextEditingController _searchController = TextEditingController();
|
||||||
|
|
||||||
|
|
||||||
final RxBool _isSearching = false.obs;
|
final RxBool _isSearching = false.obs;
|
||||||
final RxList<EmployeeModel> _allResults = <EmployeeModel>[].obs;
|
final RxList<EmployeeModel> _allResults = <EmployeeModel>[].obs;
|
||||||
|
|
||||||
|
final RxList<EmployeeModel> _allResults = <EmployeeModel>[].obs;
|
||||||
|
|
||||||
late RxList<EmployeeModel> _selectedEmployees;
|
late RxList<EmployeeModel> _selectedEmployees;
|
||||||
|
|
||||||
Timer? _debounce;
|
Timer? _debounce;
|
||||||
|
|
||||||
|
Timer? _debounce;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_selectedEmployees = RxList<EmployeeModel>.from(widget.initiallySelected);
|
_selectedEmployees = RxList<EmployeeModel>.from(widget.initiallySelected);
|
||||||
|
|
||||||
_performSearch('');
|
_performSearch('');
|
||||||
|
|
||||||
|
_performSearch('');
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
|
_debounce?.cancel();
|
||||||
_debounce?.cancel();
|
_debounce?.cancel();
|
||||||
_searchController.dispose();
|
_searchController.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
@ -58,28 +67,36 @@ class _EmployeeSelectionBottomSheetState
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _performSearch(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 {
|
Future<void> _performSearch(String query) async {
|
||||||
_isSearching.value = true;
|
_isSearching.value = true;
|
||||||
|
|
||||||
|
|
||||||
final data = await ApiService.searchEmployeesBasic(searchString: query);
|
final data = await ApiService.searchEmployeesBasic(searchString: query);
|
||||||
|
|
||||||
|
|
||||||
final results = (data as List)
|
final results = (data as List)
|
||||||
.map((e) => EmployeeModel.fromJson(e as Map<String, dynamic>))
|
.map((e) => EmployeeModel.fromJson(e as Map<String, dynamic>))
|
||||||
.toList();
|
.toList();
|
||||||
|
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
// Auto-move selected employees to top
|
// 🔥 Auto-move selected employees to top
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
results.sort((a, b) {
|
results.sort((a, b) {
|
||||||
if (widget.multipleSelection) {
|
final aSel = _selectedEmployees.contains(a) ? 0 : 1;
|
||||||
// Only move selected employees to top in multi-select
|
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);
|
if (aSel != bSel) return aSel.compareTo(bSel);
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, keep original order (or alphabetically if needed)
|
|
||||||
return a.name.toLowerCase().compareTo(b.name.toLowerCase());
|
return a.name.toLowerCase().compareTo(b.name.toLowerCase());
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -88,6 +105,9 @@ class _EmployeeSelectionBottomSheetState
|
|||||||
_isSearching.value = false;
|
_isSearching.value = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// Handle tap & checkbox
|
||||||
|
// ------------------------------------------------------
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
// Handle tap & checkbox
|
// Handle tap & checkbox
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
@ -104,8 +124,14 @@ class _EmployeeSelectionBottomSheetState
|
|||||||
|
|
||||||
// Re-sort list after each toggle
|
// Re-sort list after each toggle
|
||||||
_performSearch(_searchController.text.trim());
|
_performSearch(_searchController.text.trim());
|
||||||
|
|
||||||
|
// Re-sort list after each toggle
|
||||||
|
_performSearch(_searchController.text.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// Submit selection
|
||||||
|
// ------------------------------------------------------
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
// Submit selection
|
// Submit selection
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
@ -118,6 +144,9 @@ class _EmployeeSelectionBottomSheetState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// Search bar widget
|
||||||
|
// ------------------------------------------------------
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
// Search bar widget
|
// Search bar widget
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
@ -126,6 +155,7 @@ class _EmployeeSelectionBottomSheetState
|
|||||||
child: TextField(
|
child: TextField(
|
||||||
controller: _searchController,
|
controller: _searchController,
|
||||||
onChanged: _onSearchChanged,
|
onChanged: _onSearchChanged,
|
||||||
|
onChanged: _onSearchChanged,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
hintText: 'Search employees...',
|
hintText: 'Search employees...',
|
||||||
filled: true,
|
filled: true,
|
||||||
@ -137,6 +167,7 @@ class _EmployeeSelectionBottomSheetState
|
|||||||
onPressed: () {
|
onPressed: () {
|
||||||
_searchController.clear();
|
_searchController.clear();
|
||||||
_performSearch('');
|
_performSearch('');
|
||||||
|
_performSearch('');
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
@ -150,40 +181,25 @@ class _EmployeeSelectionBottomSheetState
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// Employee list (optimized)
|
||||||
|
// ------------------------------------------------------
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
// Employee list (optimized)
|
// Employee list (optimized)
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
Widget _employeeList() => Expanded(
|
Widget _employeeList() => Expanded(
|
||||||
child: Obx(() {
|
child: Obx(() {
|
||||||
final results = _allResults;
|
final results = _allResults;
|
||||||
|
final results = _allResults;
|
||||||
|
|
||||||
return ListView.builder(
|
return ListView.builder(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||||
itemCount: results.length,
|
itemCount: results.length,
|
||||||
|
itemCount: results.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
final emp = results[index];
|
final emp = results[index];
|
||||||
final isSelected = _selectedEmployees.contains(emp);
|
final isSelected = _selectedEmployees.contains(emp);
|
||||||
|
|
||||||
Widget trailingWidget;
|
|
||||||
|
|
||||||
if (widget.multipleSelection) {
|
|
||||||
// Multiple selection → normal checkbox
|
|
||||||
trailingWidget = Checkbox(
|
|
||||||
value: isSelected,
|
|
||||||
onChanged: (_) => _toggleEmployee(emp),
|
|
||||||
fillColor: MaterialStateProperty.resolveWith<Color>(
|
|
||||||
(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(
|
return ListTile(
|
||||||
leading: CircleAvatar(
|
leading: CircleAvatar(
|
||||||
backgroundColor: Colors.blueAccent,
|
backgroundColor: Colors.blueAccent,
|
||||||
@ -195,7 +211,15 @@ class _EmployeeSelectionBottomSheetState
|
|||||||
),
|
),
|
||||||
title: Text('${emp.firstName} ${emp.lastName}'),
|
title: Text('${emp.firstName} ${emp.lastName}'),
|
||||||
subtitle: Text(emp.email),
|
subtitle: Text(emp.email),
|
||||||
trailing: trailingWidget,
|
trailing: Checkbox(
|
||||||
|
value: isSelected,
|
||||||
|
onChanged: (_) => _toggleEmployee(emp),
|
||||||
|
fillColor: MaterialStateProperty.resolveWith<Color>(
|
||||||
|
(states) => states.contains(MaterialState.selected)
|
||||||
|
? Colors.blueAccent
|
||||||
|
: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
onTap: () => _toggleEmployee(emp),
|
onTap: () => _toggleEmployee(emp),
|
||||||
contentPadding:
|
contentPadding:
|
||||||
const EdgeInsets.symmetric(horizontal: 0, vertical: 4),
|
const EdgeInsets.symmetric(horizontal: 0, vertical: 4),
|
||||||
@ -205,6 +229,9 @@ class _EmployeeSelectionBottomSheetState
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// ------------------------------------------------------
|
||||||
|
// Build bottom sheet
|
||||||
|
// ------------------------------------------------------
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
// Build bottom sheet
|
// Build bottom sheet
|
||||||
// ------------------------------------------------------
|
// ------------------------------------------------------
|
||||||
@ -222,6 +249,12 @@ class _EmployeeSelectionBottomSheetState
|
|||||||
_employeeList(),
|
_employeeList(),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_searchBar(),
|
||||||
|
_employeeList(),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user