import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:on_field_work/helpers/utils/base_bottom_sheet.dart'; import 'package:on_field_work/model/employees/employee_model.dart'; import 'package:on_field_work/helpers/services/api_service.dart'; class EmployeeSelectionBottomSheet extends StatefulWidget { final List initiallySelected; final bool multipleSelection; final String title; const EmployeeSelectionBottomSheet({ Key? key, this.initiallySelected = const [], this.multipleSelection = true, this.title = 'Select Employees', }) : super(key: key); @override State createState() => _EmployeeSelectionBottomSheetState(); } class _EmployeeSelectionBottomSheetState extends State { final TextEditingController _searchController = TextEditingController(); final RxBool _isSearching = false.obs; final RxList _searchResults = [].obs; late RxList _selectedEmployees; @override void initState() { super.initState(); _selectedEmployees = RxList.from(widget.initiallySelected); _searchEmployees(''); } @override void dispose() { _searchController.dispose(); super.dispose(); } Future _searchEmployees(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(); _searchResults.assignAll(results); _isSearching.value = false; } void _toggleEmployee(EmployeeModel emp) { if (widget.multipleSelection) { if (_selectedEmployees.contains(emp)) { _selectedEmployees.remove(emp); } else { _selectedEmployees.add(emp); } } else { _selectedEmployees.assignAll([emp]); } _selectedEmployees.refresh(); // important for Obx rebuild } void _handleSubmit() { if (widget.multipleSelection) { Navigator.of(context).pop(_selectedEmployees.toList()); } else { Navigator.of(context) .pop(_selectedEmployees.isNotEmpty ? _selectedEmployees.first : null); } } Widget _searchBar() => Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: TextField( controller: _searchController, onChanged: _searchEmployees, decoration: InputDecoration( hintText: 'Search employees...', filled: true, fillColor: Colors.grey.shade100, prefixIcon: const Icon(Icons.search, color: Colors.grey), suffixIcon: _searchController.text.isNotEmpty ? IconButton( icon: const Icon(Icons.close, color: Colors.grey), onPressed: () { _searchController.clear(); _searchEmployees(''); }, ) : null, contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14), border: OutlineInputBorder( borderRadius: BorderRadius.circular(30), borderSide: BorderSide.none, ), ), ), ); Widget _employeeList() => Expanded( child: Obx(() { if (_isSearching.value) { return const Center(child: CircularProgressIndicator()); } if (_searchResults.isEmpty) { return const Center(child: Text("No employees found")); } return ListView.builder( padding: const EdgeInsets.symmetric(vertical: 4), itemCount: _searchResults.length, itemBuilder: (context, index) { final emp = _searchResults[index]; 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), ), ), 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( (states) => states.contains(MaterialState.selected) ? Colors.blueAccent : Colors.white, ), ), onTap: () { FocusScope.of(context).unfocus(); _toggleEmployee(emp); }, contentPadding: const EdgeInsets.symmetric(horizontal: 0, vertical: 4), ); }); }, ); }), ); @override Widget build(BuildContext context) { return BaseBottomSheet( title: widget.title, onCancel: () => Navigator.of(context).pop(), onSubmit: _handleSubmit, child: SizedBox( height: MediaQuery.of(context).size.height * 0.7, child: Column(children: [ _searchBar(), _employeeList(), ]), ), ); } }