You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			184 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Dart
		
	
			
		
		
	
	
			184 lines
		
	
	
		
			7.4 KiB
		
	
	
	
		
			Dart
		
	
| 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/models/base.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 SelectionFullScreenDialog<T extends Base> extends StatefulWidget {
 | |
|   final List<T>? items;
 | |
|   final T? selectedItem; // Now nullable
 | |
|   final T? disableItem; // Now nullable
 | |
|   final String title;
 | |
|   final bool showCancel;
 | |
|   final SelectionBuilderString builderString;
 | |
|   final Function(T?) onSelect;
 | |
| 
 | |
|   const SelectionFullScreenDialog({Key? key, this.items, this.selectedItem, this.disableItem, this.title = "", required this.builderString, this.showCancel = false, required this.onSelect})
 | |
|       : super(key: key);
 | |
| 
 | |
|   @override
 | |
|   _SelectionBottomSheetState createState() => _SelectionBottomSheetState<T>();
 | |
| }
 | |
| 
 | |
| class _SelectionBottomSheetState<T extends Base> extends State<SelectionFullScreenDialog<T>> {
 | |
|   T? _selectedValue; // Now nullable
 | |
| 
 | |
|   String query = "";
 | |
| 
 | |
|   List<T>? 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 Scaffold(
 | |
|       appBar: AppBar(
 | |
|         title: widget.title.heading5(context),
 | |
|         actions: [
 | |
|           if (widget.showCancel)
 | |
|             AnimatedContainer(
 | |
|               height: 24,
 | |
|               duration: const Duration(milliseconds: 250),
 | |
|               margin: const EdgeInsets.only(right: 16),
 | |
|               decoration: BoxDecoration(color: _selectedValue != null ? const Color(0xffF63939).withOpacity(.75) : Colors.grey.withOpacity(.75), borderRadius: BorderRadius.circular(30)),
 | |
|               padding: const EdgeInsets.only(left: 4, right: 8, top: 4, bottom: 4),
 | |
|               alignment: Alignment.center,
 | |
|               child: Row(
 | |
|                 mainAxisSize: MainAxisSize.min,
 | |
|                 crossAxisAlignment: CrossAxisAlignment.center,
 | |
|                 children: [
 | |
|                   const Icon(Icons.clear, color: Colors.white, size: 16),
 | |
|                   4.width,
 | |
|                   const Text(
 | |
|                     "Clear",
 | |
|                     style: TextStyle(fontSize: 14, color: Colors.white, height: 1),
 | |
|                   )
 | |
|                 ],
 | |
|               ).onPress(_selectedValue != null
 | |
|                   ? () {
 | |
|                       Navigator.pop(context);
 | |
|                       widget.onSelect(null);
 | |
|                     }
 | |
|                   : null),
 | |
|             ),
 | |
|         ],
 | |
|       ),
 | |
|       body: Column(
 | |
|         crossAxisAlignment: CrossAxisAlignment.start,
 | |
|         children: [
 | |
|           SearchBar(
 | |
|             focusNode: searchFocusNode,
 | |
|             elevation: WidgetStateProperty.all<double>(0),
 | |
|             backgroundColor: WidgetStateProperty.all<Color?>(context.isDark ? AppColor.neutral120 : null),
 | |
|             leading: Icon(Icons.search, color: AppColor.iconColor(context)),
 | |
|             textStyle: WidgetStateProperty.all<TextStyle>(
 | |
|               TextStyle(color: AppColor.textColor(context), fontSize: 16.0),
 | |
|             ),
 | |
|             hintStyle: WidgetStateProperty.all<TextStyle>(
 | |
|               TextStyle(color: AppColor.textColor(context), fontSize: 14.0),
 | |
|             ),
 | |
|             hintText: 'Search by name',
 | |
|             onChanged: (queryString) {
 | |
|               query = queryString;
 | |
|               setState(() {});
 | |
|             },
 | |
|           ).paddingOnly(top: 16, start: 16, end: 16, bottom: 8),
 | |
|           // TextField(
 | |
|           //   onChanged: (queryString) {
 | |
|           //     query = queryString;
 | |
|           //     setState(() {});
 | |
|           //   },
 | |
|           //   style: const TextStyle(fontSize: 14),
 | |
|           //   focusNode: searchFocusNode,
 | |
|           //   decoration: InputDecoration(
 | |
|           //     hintText: 'Search by name',
 | |
|           //     labelText: 'Search',
 | |
|           //     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),
 | |
|           //   ),
 | |
|           // ),
 | |
|           8.height,
 | |
|           Expanded(
 | |
|             child: ListView.builder(
 | |
|                 itemCount: filteredList?.length,
 | |
|                 padding: EdgeInsets.zero,
 | |
|                 itemBuilder: (cxt, index) {
 | |
|                   bool isDisabledItem = widget.disableItem != null && widget.disableItem?.identifier == filteredList![index].identifier;
 | |
|                   return Theme(
 | |
|                     data: Theme.of(context).copyWith(
 | |
|                       radioTheme: RadioThemeData(
 | |
|                         fillColor: MaterialStateColor.resolveWith((states) {
 | |
|                           if (states.contains(MaterialState.selected)) {
 | |
|                             return AppColor.iconColor(context); // Active color
 | |
|                           }
 | |
|                           return Colors.grey; // Inactive color
 | |
|                         }),
 | |
|                       ),
 | |
|                     ),
 | |
|                     child: RadioListTile<T>(
 | |
|                       // Specify type for RadioListTile
 | |
|                       value: filteredList![index],
 | |
|                       dense: true,
 | |
|                       contentPadding: const EdgeInsets.only(left: 16, right: 16),
 | |
|                       groupValue: _selectedValue,
 | |
|                       activeColor: AppColor.iconColor(context),
 | |
|                       hoverColor: Colors.transparent,
 | |
|                       onChanged: isDisabledItem
 | |
|                           ? null
 | |
|                           : (value) {
 | |
|                               _selectedValue = value;
 | |
|                               searchFocusNode.unfocus();
 | |
|                               setState(() {});
 | |
|                             },
 | |
|                       title: Text(
 | |
|                         widget.builderString(filteredList![index]).cleanupWhitespace.capitalizeFirstOfEach ?? "",
 | |
|                         style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: isDisabledItem ? AppColor.neutral20 : null),
 | |
|                       ),
 | |
|                     ),
 | |
|                   );
 | |
|                 }),
 | |
|           ),
 | |
|           8.height,
 | |
|           if (_selectedValue != null)
 | |
|             FooterActionButton.footerContainer(
 | |
|                 context: context,
 | |
|                 child: AppFilledButton(
 | |
|                   label: context.translation.select,
 | |
|                   maxWidth: true,
 | |
|                   onPressed: () {
 | |
|                     Navigator.pop(context);
 | |
|                     widget.onSelect(_selectedValue);
 | |
|                   },
 | |
|                 )),
 | |
|         ],
 | |
|       ),
 | |
|     );
 | |
|   }
 | |
| }
 |