// ignore_for_file: public_member_api_docs, sort_constructors_first // ignore_for_file: invalid_use_of_visible_for_testing_member import 'dart:convert'; import 'dart:io'; import 'package:dio/dio.dart'; import 'package:dotted_border/dotted_border.dart'; import 'package:ebroker/exports/main_export.dart'; import 'package:ebroker/utils/Extensions/extensions.dart'; import 'package:ebroker/utils/helper_utils.dart'; import 'package:ebroker/utils/responsiveSize.dart'; import 'package:ebroker/utils/ui_utils.dart'; import 'package:file_icon/src/data.dart' as d; import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_svg/flutter_svg.dart'; import '../../../../utils/AppIcon.dart'; import '../custom_text_form_field.dart'; /// Note: Here i have used abstract factory pattern and builder pattern /// You can learn design patterns from internet /// so don't be confuse List kDoNotReBuildThese = []; List kDoNotReBuildDropdown = []; abstract class AbstractField { final BuildContext context; static Map fieldsData = {}; AbstractField(this.context); Widget createField(Map parameters); } class AbstractTextField extends AbstractField { AbstractTextField(BuildContext context) : super( context, ); // TextEditingController? _controller; ///Here Builder pattern to set values, /// because when if we get it from constructor it will be messed in Factory class so it ///You can uncomment it if you want to use controller out side of the class // AbstractTextField setController(TextEditingController controller) { // _controller = controller; // return this; // } @override Widget createField(parameters) { return Column( children: [ Row( children: [ Container( width: 48.rw(context), height: 48.rh(context), decoration: BoxDecoration( color: context.color.tertiaryColor.withOpacity(0.1), borderRadius: BorderRadius.circular(10), ), child: Container( height: 24, width: 24, child: FittedBox( fit: BoxFit.none, child: UiUtils.imageType(parameters['image'], color: Constant.adaptThemeColorSvg ? context.color.tertiaryColor : null, width: 24, height: 24, fit: BoxFit.cover), ), ), ), SizedBox( width: 10.rw(context), ), Text(parameters['name']) .size(context.font.large) .bold(weight: FontWeight.w500) .color(context.color.textColorDark) ], ), SizedBox( height: 14.rh(context), ), CustomTextFieldDynamic( action: TextInputAction.next, initController: parameters['value'] != null ? true : false, value: parameters['value'].toString(), hintText: "writeSomething".translate(context), id: parameters['id'], ) ], ); } } class AbstractTextAreaField extends AbstractField { AbstractTextAreaField(BuildContext context) : super( context, ); // TextEditingController? _controller; /// Here Builder pattern to set values, /// because when if we get it from constructor it will be messed in Factory class so it /// You can uncomment it if you want to use controller out side of class //AbstractTextAreaField setController(TextEditingController controller) { // // _controller = controller; // return this; //} @override Widget createField(parameters) { return Padding( padding: const EdgeInsets.symmetric( vertical: .0, ), child: Column( children: [ Row( children: [ Container( width: 48.rw(context), height: 48.rh(context), decoration: BoxDecoration( color: context.color.tertiaryColor.withOpacity(0.1), borderRadius: BorderRadius.circular(10), ), child: Container( height: 24, width: 24, child: FittedBox( fit: BoxFit.none, child: UiUtils.imageType(parameters['image'], color: Constant.adaptThemeColorSvg ? context.color.tertiaryColor : null, width: 24, height: 24, fit: BoxFit.cover), ), ), ), SizedBox( width: 10.rw(context), ), Text(parameters['name']) .size(context.font.large) .color( context.color.textColorDark, ) .bold(weight: FontWeight.w500) ], ), SizedBox( height: 14.rh(context), ), CustomTextFormField( hintText: "Write something...", minLine: 4, maxLine: 100, validator: CustomTextFieldValidator.maxFifty, controller: parameters['value'] != null ? TextEditingController(text: parameters['value'].toString()) : null, onChange: (value) { AbstractField.fieldsData.addAll({parameters['id']: value}); }, ) ], ), ); // return TextFormField( // maxLines: null, // minLines: 5, // onChanged: (value) { // AbstractField.fieldsData.addAll({parameters['id']: value}); // }, // decoration: InputDecoration(hintText: parameters['name']), // ); } } class AbstractNumberField extends AbstractField { AbstractNumberField(BuildContext context) : super( context, ); // TextEditingController? _controller; ///Here, Builder pattern to set values, /// because when if we get it from constructor it will be messed in Factory class so it ///You can uncomment it if you want to use controller out side of class // AbstractNumberField setController(TextEditingController controller) { // _controller = controller; // return this; // } @override Widget createField(parameters) { return Padding( padding: const EdgeInsets.symmetric(vertical: 0.0), child: Column( children: [ Row( children: [ Container( width: 48.rw(context), height: 48.rh(context), decoration: BoxDecoration( color: context.color.tertiaryColor.withOpacity(0.1), borderRadius: BorderRadius.circular(10), ), child: SizedBox( height: 24, width: 24, child: FittedBox( fit: BoxFit.none, child: UiUtils.imageType(parameters['image'], color: Constant.adaptThemeColorSvg ? context.color.tertiaryColor : null, width: 24, height: 24, fit: BoxFit.cover), ), ), ), SizedBox( width: 10.rw(context), ), Text(parameters['name']) .size(context.font.large) .bold(weight: FontWeight.w500) .color(context.color.textColorDark) ], ), SizedBox( height: 14.rh(context), ), CustomTextFieldDynamic( initController: parameters['value'] != null ? true : false, value: parameters['value'].toString(), hintText: "addNumerical".translate(context), formaters: [ FilteringTextInputFormatter.allow( RegExp("[0-9]"), ), ], action: TextInputAction.next, keyboardType: TextInputType.number, id: parameters['id'], ), ], )); } } class AbstractDropdown extends AbstractField { Function(dynamic onData)? _onChange; List kDropdownSelected = []; UniqueKey _key = UniqueKey(); List? _items; dynamic selectedItem; AbstractDropdown( BuildContext context, ) : super(context); ///We can say it method chaining ///Here this is builder pattern used here it will return it self after assign value so new class has already assigned value AbstractDropdown setOnChange(Function(dynamic onChange) onChange) { _onChange = onChange; return this; } AbstractDropdown setItems(List items) { _items = items; return this; } AbstractDropdown setSelectedItem(dynamic item) { if (item != null) { if (!kDoNotReBuildDropdown.contains(_key)) { kDoNotReBuildDropdown.add(_key); dropDownItemChange.value = item; } } return this; } late ValueNotifier dropDownItemChange = ValueNotifier(_items?.first); @override Widget createField(parameters) { return CustomDropdownState( key: _key, dropDownItemChange: dropDownItemChange, parameters: parameters, items: _items ?? [], ); return ValueListenableBuilder( valueListenable: dropDownItemChange, builder: (context, value, child) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 48.rw(context), height: 48.rh(context), decoration: BoxDecoration( color: context.color.tertiaryColor.withOpacity( 0.1, ), borderRadius: BorderRadius.circular( 10, ), ), child: UiUtils.imageType(parameters['image'], fit: BoxFit.none), ), SizedBox( width: 10.rw( context, ), ), Text( parameters['name'], ) .size( context.font.large, ) .color( context.color.textColorDark, ) ], ), SizedBox( height: 10.rh(context), ), Padding( padding: const EdgeInsets.symmetric( horizontal: 3, ), child: Container( decoration: BoxDecoration( color: context.color.secondaryColor, borderRadius: BorderRadius.circular( 10, ), border: Border.all( width: 1.5, color: context.color.borderColor, )), child: Padding( padding: const EdgeInsets.all( 8.0, ), child: SizedBox( width: double.infinity, child: DropdownButton( value: value, isExpanded: true, padding: EdgeInsets.symmetric(vertical: 5), icon: SvgPicture.asset(AppIcons.downArrow), isDense: true, borderRadius: BorderRadius.circular( 10, ), underline: const SizedBox.shrink(), items: _items ?.map((e) => DropdownMenuItem( value: e, child: Text( e, ), )) .toList(), onChanged: (dynamic v) { dropDownItemChange.value = v; AbstractField.fieldsData.addAll( { parameters['id']: v, }, ); _onChange?.call(v); }, ), ), ), ), ), SizedBox( height: 10.rh( context, ), ) ], ); }); } @override bool operator ==(Object other) => identical(this, other) || other is AbstractDropdown && runtimeType == other.runtimeType && _onChange == other._onChange && kDropdownSelected == other.kDropdownSelected && _key == other._key && _items == other._items && selectedItem == other.selectedItem && dropDownItemChange == other.dropDownItemChange; @override int get hashCode => _onChange.hashCode ^ kDropdownSelected.hashCode ^ _key.hashCode ^ _items.hashCode ^ selectedItem.hashCode ^ dropDownItemChange.hashCode; } class AbstractRadioButton extends AbstractField { AbstractRadioButton(BuildContext context) : super(context); List? _radioValues; // late ValueNotifier selectedRadio; AbstractRadioButton setValues( List values, ) { _radioValues = values; // selectedRadio = ValueNotifier(_radioValues?.first); return this; } @override Widget createField(parameters) { return CustomRadioButtonWidget( parameters: parameters, radioValues: _radioValues, ); // return ValueListenableBuilder( // valueListenable: selectedRadio, // builder: (context, value, child) { // return Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // Row( // children: [ // Container( // width: 48.rw(context), // height: 48.rh(context), // decoration: BoxDecoration( // color: context.color.teritoryColor.withOpacity(0.1), // borderRadius: BorderRadius.circular(10), // ), // child: UiUtils.imageType(parameters['image'], // fit: BoxFit.none), // ), // SizedBox( // width: 10.rw(context), // ), // Text(parameters['name']) // .size(context.font.large) // .color(context.color.textColorDark) // ], // ), // SizedBox( // height: 10.rh(context), // ), // Wrap( // alignment: WrapAlignment.start, // runAlignment: WrapAlignment.start, // crossAxisAlignment: WrapCrossAlignment.start, // children: List.generate( // _radioValues?.length ?? 0, // (index) => Row( // mainAxisSize: MainAxisSize.min, // children: [ // Radio( // value: _radioValues?[index], // groupValue: value, // fillColor: MaterialStatePropertyAll( // context.color.teritoryColor), // onChanged: (dynamic e) { // selectedRadio.value = e; // AbstractField.fieldsData // .addAll({parameters['id']: e}); // }, // ), // Text(_radioValues?[index]) // ], // ))) // ], // ); // // return Column( // // crossAxisAlignment: CrossAxisAlignment.start, // // children: [ // // Text(parameters['name']), // // Wrap( // // children: _radioValues // // ?.map((item) { // // return Row( // // mainAxisSize: MainAxisSize.min, // // children: [ // // Text(item), // // Radio( // // value: item, // // groupValue: value, // // onChanged: (dynamic e) { // // selectedRadio.value = e; // // AbstractField.fieldsData // // .addAll({parameters['id']: e}); // // }), // // ], // // ); // // }) // // .toList() // // .cast() ?? // // [], // // ), // // ], // // ); // }); } } class AbstractCheckBoxButton extends AbstractField { AbstractCheckBoxButton(BuildContext context) : super(context); List? _checkValues; bool initComplete = false; AbstractCheckBoxButton setCheckBoxValues(List values) { _checkValues = values; return this; } @override Widget createField(parameters) { return CustomCheckBox( parameters: parameters, checkValues: _checkValues, index: 0, initComplete: true, ); } void dispose() { // checked.dispose(); } } class AbstractPickFileButton extends AbstractField { AbstractPickFileButton(BuildContext context) : super(context); ValueNotifier filePicked = ValueNotifier(false); ValueNotifier picked = ValueNotifier(null); Future pickFile() async { FilePickerResult? picker = await FilePicker.platform.pickFiles(); if (picker != null) { filePicked.value = true; File file = File( picker.files.single.path!, ); picked.value = file.path; return file; } filePicked.value = false; picked.value = null; return null; } @override Widget createField(parameters) { picked.value = parameters['value']; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 48.rw(context), height: 48.rh(context), decoration: BoxDecoration( color: context.color.tertiaryColor.withOpacity(0.1), borderRadius: BorderRadius.circular(10), ), child: Container( height: 24, width: 24, child: FittedBox( fit: BoxFit.none, child: UiUtils.imageType(parameters['image'], color: Constant.adaptThemeColorSvg ? context.color.tertiaryColor : null, width: 24, height: 24, fit: BoxFit.cover), ), ), ), SizedBox( width: 10.rw(context), ), Text(parameters['name']) .size(context.font.large) .bold(weight: FontWeight.w500) .color(context.color.textColorDark), ], ), SizedBox( height: 14.rh(context), ), GestureDetector( onTap: () async { File? file = await pickFile(); if (file != null) { MultipartFile multipartFile = await MultipartFile.fromFile(file.path); /// Add data to static Map AbstractField.fieldsData .addAll({parameters['id']: multipartFile}); } }, child: DottedBorder( borderType: BorderType.RRect, radius: const Radius.circular(10), color: context.color.textLightColor, strokeCap: StrokeCap.round, padding: const EdgeInsets.all(5), dashPattern: const [3, 3], child: Container( width: double.infinity, height: 43, decoration: BoxDecoration(borderRadius: BorderRadius.circular(10)), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.add), const SizedBox( width: 5, ), const Text("Add File") .color(context.color.textLightColor) .size(context.font.large) ], ), )), ), ValueListenableBuilder( valueListenable: picked, builder: (context, String? pickedFilePath, c) { if (pickedFilePath == null) { return const SizedBox.shrink(); } return Container( child: Row( children: [ Icon( IconData( d.iconSetMap[".${pickedFilePath.split(".").last}"]! .codePoint, fontFamily: 'Seti', fontPackage: 'file_icon', ), color: context.color.tertiaryColor, size: 35, ), const SizedBox( width: 5, ), Expanded( flex: 2, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(pickedFilePath.split("/").last) .setMaxLines(lines: 1) .bold(weight: FontWeight.w500), if (!(pickedFilePath.startsWith("http") || pickedFilePath.startsWith("https"))) Text(HelperUtils.getFileSizeString( bytes: File(pickedFilePath).lengthSync(), ).toUpperCase()) .size(context.font.smaller) ], )), const Spacer(flex: 1), IconButton( onPressed: () { AbstractField.fieldsData.remove(parameters['id']); picked.value = null; }, icon: Icon(Icons.close)) ], ), ); }) ], ); // return MaterialButton( // onPressed: () async { // File? file = await pickFile(); // if (file != null) { // MultipartFile multipartFile = await MultipartFile.fromFile(file.path); // /// Add data to static Map // AbstractField.fieldsData.addAll({parameters['id']: multipartFile}); // } // }, // color: Colors.grey.shade200, // child: const Text("Pick"), // ); } } ///Factory class which will return class according to type class FieldFactory { static AbstractField getField(BuildContext context, String fieldType) { if (fieldType == 'textbox') { return AbstractTextField(context); } else if (fieldType == 'dropdown') { return AbstractDropdown(context); } else if (fieldType == 'radiobutton') { return AbstractRadioButton(context); } else if (fieldType == 'number') { return AbstractNumberField( context, ); } else if (fieldType == "checkbox") { return AbstractCheckBoxButton(context); } else if (fieldType == "textarea") { return AbstractTextAreaField(context); } else if (fieldType == "file") { return AbstractPickFileButton(context); } throw Exception('Invalid field type: $fieldType'); } } class CustomRadioButtonWidget extends StatefulWidget { final dynamic parameters; final dynamic radioValues; const CustomRadioButtonWidget({super.key, this.parameters, this.radioValues}); @override State createState() => _CustomRadioButtonWidgetState(); } class _CustomRadioButtonWidgetState extends State { late ValueNotifier selectedRadio; bool isInitialized = false; @override void initState() { selectedRadio = ValueNotifier(widget.radioValues?.first); if (widget.parameters['value'] != null && isInitialized == false) { selectedRadio.value = widget.parameters['value']; isInitialized = true; } super.initState(); } @override Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: selectedRadio, builder: (context, value, child) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 48.rw(context), height: 48.rh(context), decoration: BoxDecoration( color: context.color.tertiaryColor.withOpacity(0.1), borderRadius: BorderRadius.circular(10), ), child: Container( height: 24, width: 24, child: FittedBox( fit: BoxFit.none, child: UiUtils.imageType(widget.parameters['image'], color: Constant.adaptThemeColorSvg ? context.color.tertiaryColor : null, width: 24, height: 24, fit: BoxFit.cover), ), ), ), SizedBox( width: 10.rw(context), ), Text(widget.parameters['name']) .size(context.font.large) .bold(weight: FontWeight.w500) .color(context.color.textColorDark) ], ), SizedBox( height: 14.rh(context), ), Wrap( alignment: WrapAlignment.start, runAlignment: WrapAlignment.start, crossAxisAlignment: WrapCrossAlignment.start, children: List.generate(widget.radioValues?.length ?? 0, (index) { return Padding( padding: EdgeInsetsDirectional.only( start: index == 0 ? 0 : 4, end: 4, bottom: 4, top: 4, ), child: InkWell( borderRadius: BorderRadius.circular(10), onTap: () { selectedRadio.value = widget.radioValues?[index]; AbstractField.fieldsData.addAll({ widget.parameters['id']: widget.radioValues?[index] }); }, child: Container( decoration: BoxDecoration( border: Border.all( color: context.color.borderColor, width: 1.5), color: selectedRadio.value == widget.radioValues?[index] ? context.color.tertiaryColor.withOpacity(0.1) : context.color.secondaryColor, borderRadius: BorderRadius.circular(10)), child: Padding( padding: const EdgeInsets.symmetric( vertical: 10, horizontal: 15), child: Text(widget.radioValues?[index]).color( selectedRadio.value == widget.radioValues?[index] ? context.color.tertiaryColor : context.color.textLightColor)), ), ), ); return Row( mainAxisSize: MainAxisSize.min, children: [ Radio( value: widget.radioValues?[index], groupValue: value, fillColor: MaterialStatePropertyAll( context.color.tertiaryColor), onChanged: (dynamic e) { selectedRadio.value = e; AbstractField.fieldsData .addAll({widget.parameters['id']: e}); }, ), Text(widget.radioValues?[index]) ], ); })) ], ); // return Column( // crossAxisAlignment: CrossAxisAlignment.start, // children: [ // Text(parameters['name']), // Wrap( // children: _radioValues // ?.map((item) { // return Row( // mainAxisSize: MainAxisSize.min, // children: [ // Text(item), // Radio( // value: item, // groupValue: value, // onChanged: (dynamic e) { // selectedRadio.value = e; // AbstractField.fieldsData // .addAll({parameters['id']: e}); // }), // ], // ); // }) // .toList() // .cast() ?? // [], // ), // ], // ); }); } } class CustomCheckBox extends StatefulWidget { final dynamic parameters; final dynamic checkValues; final dynamic initComplete; final dynamic index; const CustomCheckBox( {super.key, this.parameters, this.checkValues, this.initComplete, this.index}); @override State createState() => _CustomCheckBoxState(); } class _CustomCheckBoxState extends State { final ValueNotifier checked = ValueNotifier([]); @override void initState() { // log("VA ${widget.parameters}"); if (widget.parameters.containsKey("value")) { List valueList = widget.parameters['value'].toString().split(","); if (valueList.isNotEmpty) { checked.value.addAll(valueList); // checked.value.add(widget.checkValues?[widget.index]); var entries = checked.value.asMap().entries; Map data = Map.fromEntries(entries); Map stringedData = data.map((key, value) { return MapEntry(key.toString(), value); }); AbstractField.fieldsData .addAll({widget.parameters['id']: json.encode(stringedData)}); } } super.initState(); } @override Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: checked, builder: (context, List value, Widget? c) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 48.rw(context), height: 48.rh(context), decoration: BoxDecoration( color: context.color.tertiaryColor.withOpacity(0.1), borderRadius: BorderRadius.circular(10), ), child: Container( height: 24, width: 24, child: FittedBox( fit: BoxFit.none, child: UiUtils.imageType(widget.parameters['image'], color: Constant.adaptThemeColorSvg ? context.color.tertiaryColor : null, width: 24, height: 24, fit: BoxFit.cover), ), ), ), SizedBox( width: 10.rw(context), ), Text(widget.parameters['name']) .size(context.font.large) .bold(weight: FontWeight.w500) .color(context.color.textColorDark) ], ), SizedBox( height: 14.rh(context), ), Wrap( alignment: WrapAlignment.start, runAlignment: WrapAlignment.start, crossAxisAlignment: WrapCrossAlignment.start, children: List.generate( widget.checkValues?.length ?? 0, (index) { //this variable will prevent adding when state change ///this will work like init state text if (widget.initComplete == false && (!kDoNotReBuildThese.contains(widget.parameters['id']))) { kDoNotReBuildThese.add(widget.parameters['id']); // widget.initComplete = true; } return Padding( padding: EdgeInsetsDirectional.only( start: index == 0 ? 0 : 4, bottom: 4, top: 4, end: 4), child: InkWell( borderRadius: BorderRadius.circular(10), onTap: () { if (checked.value .contains(widget.checkValues?[index])) { checked.value.remove(widget.checkValues?[index]); // ignore: invalid_use_of_protected_member checked.notifyListeners(); } else { checked.value.add(widget.checkValues?[index]); // ignore: invalid_use_of_protected_member checked.notifyListeners(); } var entries = checked.value.asMap().entries; Map data = Map.fromEntries(entries); Map temp = {}; data.forEach((key, value) { temp[key.toString()] = value; }); AbstractField.fieldsData.addAll( {widget.parameters['id']: json.encode(temp)}); }, child: Container( decoration: BoxDecoration( border: Border.all( color: context.color.borderColor, width: 1.5), color: value.contains(widget.checkValues?[index]) ? context.color.tertiaryColor.withOpacity(0.1) : context.color.secondaryColor, borderRadius: BorderRadius.circular(10)), child: Padding( padding: const EdgeInsets.symmetric( vertical: 8.0, horizontal: 14), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon( value.contains(widget.checkValues?[index]) ? Icons.done : Icons.add, color: value.contains(widget.checkValues?[index]) ? context.color.tertiaryColor : context.color.textColorDark, ), const SizedBox( width: 5, ), Text(widget.checkValues?[index]).color( value.contains(widget.checkValues?[index]) ? context.color.tertiaryColor : context.color.textLightColor) ], ), ), ), ), ); return Row( mainAxisSize: MainAxisSize.min, children: [ Checkbox( fillColor: MaterialStatePropertyAll( context.color.tertiaryColor), value: value.contains(widget.checkValues?[index]), onChanged: (v) { if (checked.value .contains(widget.checkValues?[index])) { checked.value.remove(widget.checkValues?[index]); // ignore: invalid_use_of_protected_member checked.notifyListeners(); } else { checked.value.add(widget.checkValues?[index]); // ignore: invalid_use_of_protected_member checked.notifyListeners(); } var entries = checked.value.asMap().entries; Map data = Map.fromEntries(entries); Map temp = {}; data.forEach((key, value) { temp[key.toString()] = value; }); AbstractField.fieldsData.addAll( {widget.parameters['id']: json.encode(temp)}); }, ), Text(widget.checkValues?[index]) ], ); }, ), ) ], ); }, ); } } class CustomTextFieldDynamic extends StatefulWidget { final String? value; final bool initController; final dynamic id; final String hintText; final TextInputType? keyboardType; final TextInputAction? action; final List? formaters; const CustomTextFieldDynamic({ Key? key, required this.initController, required this.value, this.id, required this.hintText, this.keyboardType, this.action, this.formaters, }) : super(key: key); @override State createState() => CustomTextFieldDynamicState(); } class CustomTextFieldDynamicState extends State { TextEditingController? _controller; @override void initState() { if (widget.initController) { _controller = TextEditingController(text: widget.value); } super.initState(); } @override Widget build(BuildContext context) { return CustomTextFormField( hintText: widget.hintText, action: widget.action, formaters: widget.formaters, validator: CustomTextFieldValidator.nullCheck, keyboard: widget.keyboardType, controller: _controller, onChange: (value) { AbstractField.fieldsData.addAll({widget.id: value}); }, ); } } class CustomDropdownState extends StatefulWidget { final ValueNotifier dropDownItemChange; final Map parameters; final List items; CustomDropdownState( {Key? key, required this.dropDownItemChange, required this.parameters, required this.items}) : super(key: key); @override State createState() => _CustomDropdownStateState(); } class _CustomDropdownStateState extends State with AutomaticKeepAliveClientMixin { List>? wid = []; @override void initState() { wid = widget.items .map((e) => DropdownMenuItem( value: e, child: Text( e, ), )) .toList(); super.initState(); } @override Widget build(BuildContext context) { super.build(context); return ValueListenableBuilder( valueListenable: widget.dropDownItemChange, builder: (context, value, child) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 48.rw(context), height: 48.rh(context), decoration: BoxDecoration( color: context.color.tertiaryColor.withOpacity( 0.1, ), borderRadius: BorderRadius.circular( 10, ), ), child: Container( height: 24, width: 24, child: FittedBox( fit: BoxFit.none, child: UiUtils.imageType(widget.parameters['image'], color: Constant.adaptThemeColorSvg ? context.color.tertiaryColor : null, width: 24, height: 24, fit: BoxFit.cover), ), ), ), SizedBox( width: 10.rw( context, ), ), Text( widget.parameters['name'], ) .size( context.font.large, ) .bold(weight: FontWeight.w500) .color( context.color.textColorDark, ) ], ), SizedBox( height: 14.rh(context), ), Padding( padding: const EdgeInsets.symmetric( horizontal: 0, ), child: Container( decoration: BoxDecoration( color: context.color.secondaryColor, borderRadius: BorderRadius.circular( 10, ), border: Border.all( width: 1.5, color: context.color.borderColor, )), child: Padding( padding: const EdgeInsets.all( 8.0, ), child: SizedBox( width: double.infinity, child: DropdownButton( value: value, dropdownColor: context.color.secondaryColor, isExpanded: true, padding: const EdgeInsets.symmetric(vertical: 5), icon: SvgPicture.asset(AppIcons.downArrow), isDense: true, borderRadius: BorderRadius.circular( 10, ), style: TextStyle( color: context.color.textLightColor, fontSize: context.font.large), underline: const SizedBox.shrink(), items: wid, onChanged: (dynamic v) { widget.dropDownItemChange.value = v; AbstractField.fieldsData.addAll( { widget.parameters['id']: v, }, ); }, ), ), ), ), ), ], ); }); } @override bool get wantKeepAlive => true; }