import 'package:flutter/material.dart'; import 'package:test_sa/extensions/context_extension.dart'; import 'package:test_sa/extensions/int_extensions.dart'; import 'package:test_sa/extensions/text_extensions.dart'; import 'package:test_sa/extensions/widget_extensions.dart'; import 'package:test_sa/modules/cm_module/views/components/action_button/footer_action_button.dart'; import 'package:test_sa/new_views/app_style/app_color.dart'; import 'package:test_sa/new_views/common_widgets/app_filled_button.dart'; typedef SelectionBuilderString = String Function(dynamic); class MultipleSelectionBottomSheet extends StatefulWidget { final List? items; final List selectedItem; // Now nullable final String title; final SelectionBuilderString builderString; final bool showCancel; final Function(List) onSelect; const MultipleSelectionBottomSheet({Key? key, this.items = const [], this.selectedItem = const [], this.title = "", required this.builderString, this.showCancel = false, required this.onSelect}) : super(key: key); @override _MultipleSelectionBottomSheetState createState() => _MultipleSelectionBottomSheetState(); } class _MultipleSelectionBottomSheetState extends State> { late List _selectedValue; // Now nullable String query = ""; List? get filteredList => widget.items?.where((element) => widget.builderString(element).toLowerCase().contains(query.toLowerCase())).toList(); @override void initState() { _selectedValue = widget.selectedItem; super.initState(); } FocusNode searchFocusNode = FocusNode(); @override void dispose() { searchFocusNode.dispose(); super.dispose(); } @override Widget build(BuildContext context) { return Container( height: MediaQuery.of(context).size.height * .7, color: Theme.of(context).scaffoldBackgroundColor, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ widget.title.heading5(context).expanded, if (widget.showCancel) AnimatedOpacity( opacity: _selectedValue != null ? 1 : 0, duration: const Duration(milliseconds: 250), child: Container( height: 30, decoration: BoxDecoration(color: const Color(0xffF63939).withOpacity(.75), borderRadius: BorderRadius.circular(30)), padding: const EdgeInsets.only(left: 8, right: 12, top: 4, bottom: 4), alignment: Alignment.center, child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.clear, color: Colors.white, size: 16), 4.width, const Text( "Clear", style: TextStyle(fontSize: 14, color: Colors.white), ) ], ), ).onPress(_selectedValue.isNotEmpty ? () { Navigator.pop(context); widget.onSelect([]); } : null), ), ], ).paddingOnly(top: 16, start: 16, end: 16, bottom: 0), TextField( onChanged: (queryString) { query = queryString; setState(() {}); }, style: const TextStyle(fontSize: 14), focusNode: searchFocusNode, decoration: InputDecoration( hintText: 'Search by name', labelText: 'Search', labelStyle: TextStyle(color: AppColor.textColor(context)), filled: true, fillColor: AppColor.fieldBgColor(context), hintStyle: const TextStyle(fontSize: 14), focusedBorder: OutlineInputBorder( borderSide: BorderSide(color: AppColor.blueStatus(context), width: 2.0), borderRadius: const BorderRadius.all(Radius.circular(12.0)), ), enabledBorder: OutlineInputBorder( borderSide: BorderSide(color: AppColor.blueStatus(context), width: 1.0), borderRadius: const BorderRadius.all(Radius.circular(12.0)), ), contentPadding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 16.0), ), ).paddingOnly(top: 16, start: 16, end: 16, bottom: 16), Expanded( // Wrap ListView with Expanded child: ListView.builder( itemCount: filteredList?.length, padding: EdgeInsets.zero, itemBuilder: (cxt, index) => Theme( data: Theme.of(context).copyWith( radioTheme: RadioThemeData( fillColor: MaterialStateColor.resolveWith((states) { if (states.contains(MaterialState.selected)) { return AppColor.textColor(context); // Active color } return Colors.grey; // Inactive color }), ), ), child: CheckboxListTile( value: checkItContains(filteredList![index]), dense: true, // groupValue: _selectedValue, activeColor: AppColor.textColor(context), contentPadding: const EdgeInsets.only(left: 16, right: 16), onChanged: (value) { if (value == true) { _selectedValue.add(filteredList![index]); } else if (value == false) { _selectedValue.remove(filteredList![index]); } searchFocusNode.unfocus(); setState(() {}); }, title: Text( widget.builderString(filteredList![index]).cleanupWhitespace.capitalizeFirstOfEach ?? "", style: Theme.of(context).textTheme.bodyLarge, ), ), ), ), ), 8.height, if (_selectedValue.isNotEmpty) FooterActionButton.footerContainer( context: context, child: AppFilledButton( label: context.translation.select, maxWidth: true, onPressed: () { Navigator.pop(context); widget.onSelect(_selectedValue); }, )), ], ), ); } bool? checkItContains(T) { return widget.items?.contains(T); } }