diff --git a/example/lib/main.dart b/example/lib/main.dart index 3a6c1b051d..0f45ed2802 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -41,8 +41,7 @@ class MyHomePageState extends State { bool readOnly = false; bool showSegmentedControl = true; final GlobalKey _fbKey = GlobalKey(); - final GlobalKey _specifyTextFieldKey = - GlobalKey(); + final GlobalKey _specifyTextFieldKey = GlobalKey(); ValueChanged _onChanged = (val) => print(val); var genderOptions = ['Male', 'Female', 'Other']; @@ -74,16 +73,11 @@ class MyHomePageState extends State { labelText: 'Select many options', ), options: [ - FormBuilderFieldOption( - value: 'Test', child: Text('Test')), - FormBuilderFieldOption( - value: 'Test 1', child: Text('Test 1')), - FormBuilderFieldOption( - value: 'Test 2', child: Text('Test 2')), - FormBuilderFieldOption( - value: 'Test 3', child: Text('Test 3')), - FormBuilderFieldOption( - value: 'Test 4', child: Text('Test 4')), + FormBuilderFieldOption(value: 'Test', child: Text('Test')), + FormBuilderFieldOption(value: 'Test 1', child: Text('Test 1')), + FormBuilderFieldOption(value: 'Test 2', child: Text('Test 2')), + FormBuilderFieldOption(value: 'Test 3', child: Text('Test 3')), + FormBuilderFieldOption(value: 'Test 4', child: Text('Test 4')), ], ), FormBuilderChoiceChip( @@ -92,16 +86,11 @@ class MyHomePageState extends State { labelText: 'Select an option', ), options: [ - FormBuilderFieldOption( - value: 'Test', child: Text('Test')), - FormBuilderFieldOption( - value: 'Test 1', child: Text('Test 1')), - FormBuilderFieldOption( - value: 'Test 2', child: Text('Test 2')), - FormBuilderFieldOption( - value: 'Test 3', child: Text('Test 3')), - FormBuilderFieldOption( - value: 'Test 4', child: Text('Test 4')), + FormBuilderFieldOption(value: 'Test', child: Text('Test')), + FormBuilderFieldOption(value: 'Test 1', child: Text('Test 1')), + FormBuilderFieldOption(value: 'Test 2', child: Text('Test 2')), + FormBuilderFieldOption(value: 'Test 3', child: Text('Test 3')), + FormBuilderFieldOption(value: 'Test 4', child: Text('Test 4')), ], ), FormBuilderCustomField( @@ -116,8 +105,7 @@ class MyHomePageState extends State { return InputDecorator( decoration: InputDecoration( labelText: "Select option", - contentPadding: - EdgeInsets.only(top: 10.0, bottom: 0.0), + contentPadding: EdgeInsets.only(top: 10.0, bottom: 0.0), border: InputBorder.none, errorText: field.errorText, ), @@ -125,8 +113,7 @@ class MyHomePageState extends State { height: 200, child: CupertinoPicker( itemExtent: 30, - children: - allCountries.map((c) => Text(c)).toList(), + children: allCountries.map((c) => Text(c)).toList(), onSelectedItemChanged: (index) { print(index); field.didChange(allCountries[index]); @@ -156,19 +143,13 @@ class MyHomePageState extends State { if (query.length != 0) { var lowercaseQuery = query.toLowerCase(); return contacts.where((profile) { - return profile.name - .toLowerCase() - .contains(query.toLowerCase()) || - profile.email - .toLowerCase() - .contains(query.toLowerCase()); + return profile.name.toLowerCase().contains(query.toLowerCase()) || + profile.email.toLowerCase().contains(query.toLowerCase()); }).toList(growable: false) ..sort((a, b) => a.name .toLowerCase() .indexOf(lowercaseQuery) - .compareTo(b.name - .toLowerCase() - .indexOf(lowercaseQuery))); + .compareTo(b.name.toLowerCase().indexOf(lowercaseQuery))); } else { return const []; } @@ -181,8 +162,7 @@ class MyHomePageState extends State { backgroundImage: NetworkImage(profile.imageUrl), ), onDeleted: () => state.deleteChip(profile), - materialTapTargetSize: - MaterialTapTargetSize.shrinkWrap, + materialTapTargetSize: MaterialTapTargetSize.shrinkWrap, ); }, suggestionBuilder: (context, state, profile) { @@ -274,8 +254,7 @@ class MyHomePageState extends State { ), validators: [ FormBuilderValidators.requiredTrue( - errorText: - "You must accept terms and conditions to continue", + errorText: "You must accept terms and conditions to continue", ), ], ), @@ -332,15 +311,12 @@ class MyHomePageState extends State { if (query.length != 0) { var lowercaseQuery = query.toLowerCase(); return allCountries.where((country) { - return country - .toLowerCase() - .contains(lowercaseQuery); + return country.toLowerCase().contains(lowercaseQuery); }).toList(growable: false) ..sort((a, b) => a .toLowerCase() .indexOf(lowercaseQuery) - .compareTo( - b.toLowerCase().indexOf(lowercaseQuery))); + .compareTo(b.toLowerCase().indexOf(lowercaseQuery))); } else { return allCountries; } @@ -364,35 +340,29 @@ class MyHomePageState extends State { if (query.length != 0) { var lowercaseQuery = query.toLowerCase(); return contacts.where((contact) { - return contact.name - .toLowerCase() - .contains(lowercaseQuery); + return contact.name.toLowerCase().contains(lowercaseQuery); }).toList(growable: false) ..sort((a, b) => a.name .toLowerCase() .indexOf(lowercaseQuery) - .compareTo(b.name - .toLowerCase() - .indexOf(lowercaseQuery))); + .compareTo(b.name.toLowerCase().indexOf(lowercaseQuery))); } else { return contacts; } }, ), FormBuilderRadio( - decoration: - InputDecoration(labelText: 'My chosen language'), + decoration: InputDecoration(labelText: 'My chosen language'), attribute: "best_language", leadingInput: true, onChanged: _onChanged, validators: [FormBuilderValidators.required()], - options: - ["Dart", "Kotlin", "Java", "Swift", "Objective-C"] - .map((lang) => FormBuilderFieldOption( - value: lang, - child: Text('$lang'), - )) - .toList(growable: false), + options: ["Dart", "Kotlin", "Java", "Swift", "Objective-C"] + .map((lang) => FormBuilderFieldOption( + value: lang, + child: Text('$lang'), + )) + .toList(growable: false), ), FormBuilderRadio( decoration: InputDecoration(labelText: 'Pick a number'), @@ -413,8 +383,7 @@ class MyHomePageState extends State { ], ), FormBuilderSegmentedControl( - decoration: - InputDecoration(labelText: "Movie Rating (Archer)"), + decoration: InputDecoration(labelText: "Movie Rating (Archer)"), attribute: "movie_rating", textStyle: TextStyle(fontWeight: FontWeight.bold), options: List.generate(5, (i) => i + 1) @@ -456,8 +425,7 @@ class MyHomePageState extends State { isHalfAllowed: true, ), FormBuilderCheckboxList( - decoration: InputDecoration( - labelText: "The language of my people"), + decoration: InputDecoration(labelText: "The language of my people"), attribute: "languages", initialValue: ["Dart"], leadingInput: true, @@ -473,21 +441,14 @@ class MyHomePageState extends State { FormBuilderCustomField( attribute: 'custom', valueTransformer: (val) { - if (val == "Other") - return _specifyTextFieldKey.currentState.value; + if (val == "Other") return _specifyTextFieldKey.currentState.value; return val; }, formField: FormField( builder: (FormFieldState field) { - var languages = [ - "English", - "Spanish", - "Somali", - "Other" - ]; + var languages = ["English", "Spanish", "Somali", "Other"]; return InputDecorator( - decoration: InputDecoration( - labelText: "What's your preferred language?"), + decoration: InputDecoration(labelText: "What's your preferred language?"), child: Column( children: languages .map( @@ -511,8 +472,7 @@ class MyHomePageState extends State { SizedBox(width: 20), Expanded( child: TextFormField( - key: - _specifyTextFieldKey, + key: _specifyTextFieldKey, ), ), ], @@ -539,6 +499,33 @@ class MyHomePageState extends State { } ], ), + FormBuilderCountryPicker( + defaultSelectedCountryIsoCode: 'US', + attribute: "country", + cursorColor: Colors.black, + style: TextStyle(color: Colors.black, fontSize: 18), + priorityListByIsoCode: ['US'], + valueTransformer: (value) { + return value.isoCode; + }, + decoration: InputDecoration(border: OutlineInputBorder(), labelText: "Country"), + validators: [ + FormBuilderValidators.required(errorText: 'This field required.'), + ], + ), + FormBuilderPhoneField( + attribute: 'phone_number', + keyboardType: TextInputType.phone, + defaultSelectedCountryIsoCode: 'US', + cursorColor: Colors.black, + style: TextStyle(color: Colors.black, fontSize: 18), + decoration: InputDecoration(border: OutlineInputBorder(), labelText: "Phone Number"), + priorityListByIsoCode: ['US'], + validators: [ + FormBuilderValidators.numeric(errorText: 'Invalid phone number'), + FormBuilderValidators.required(errorText: 'This field reqired') + ], + ), FormBuilderSignaturePad( decoration: InputDecoration(labelText: "Signature"), attribute: "signature", diff --git a/lib/flutter_form_builder.dart b/lib/flutter_form_builder.dart index cedd63a2a4..29436e1ff6 100644 --- a/lib/flutter_form_builder.dart +++ b/lib/flutter_form_builder.dart @@ -1,9 +1,7 @@ library flutter_form_builder; -export './src/form_builder.dart'; -export './src/form_builder_custom_field.dart'; -export './src/form_builder_field_option.dart'; -export './src/form_builder_validators.dart'; +export 'package:flutter_typeahead/flutter_typeahead.dart'; + export './src/fields/form_builder_checkbox.dart'; export './src/fields/form_builder_checkbox_list.dart'; export './src/fields/form_builder_chips_choice.dart'; @@ -13,17 +11,21 @@ export './src/fields/form_builder_color_picker.dart'; export './src/fields/form_builder_date_range_picker.dart'; export './src/fields/form_builder_date_time_picker.dart'; export './src/fields/form_builder_dropdown.dart'; +export './src/fields/form_builder_image_picker.dart'; +export './src/fields/form_builder_image_picker.dart'; +export './src/fields/form_builder_phone_field.dart'; export './src/fields/form_builder_radio.dart'; export './src/fields/form_builder_range_slider.dart'; export './src/fields/form_builder_rate.dart'; export './src/fields/form_builder_segmented_control.dart'; +export './src/fields/form_builder_signature_pad.dart'; export './src/fields/form_builder_slider.dart'; export './src/fields/form_builder_stepper.dart'; export './src/fields/form_builder_switch.dart'; export './src/fields/form_builder_text_field.dart'; export './src/fields/form_builder_touch_spin.dart'; export './src/fields/form_builder_typeahead.dart'; -export './src/fields/form_builder_signature_pad.dart'; -export './src/fields/form_builder_image_picker.dart'; - -export 'package:flutter_typeahead/flutter_typeahead.dart'; +export './src/form_builder.dart'; +export './src/form_builder_custom_field.dart'; +export './src/form_builder_field_option.dart'; +export './src/form_builder_validators.dart'; diff --git a/lib/src/fields/form_builder_country_picker.dart b/lib/src/fields/form_builder_country_picker.dart new file mode 100644 index 0000000000..f2787c0cf2 --- /dev/null +++ b/lib/src/fields/form_builder_country_picker.dart @@ -0,0 +1,190 @@ +import 'package:country_pickers/country.dart'; +import 'package:country_pickers/country_pickers.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; + +class FormBuilderCountryPicker extends StatefulWidget { + final String attribute; + final List validators; + final bool readOnly; + final InputDecoration decoration; + final ValueChanged onChanged; + final ValueTransformer valueTransformer; + + final TextStyle style; + final FormFieldSetter onSaved; + + // For country dialog + final String searchText; + final EdgeInsets titlePadding; + final bool isSearchable; + final Text dialogTitle; + final String defaultSelectedCountryIsoCode; + final List priorityListByIsoCode; + final List countryFilterByIsoCode; + final TextStyle dialogTextStyle; + final bool isCupertinoPicker; + final double cupertinoPickerSheetHeight; + final Color cursorColor; + + FormBuilderCountryPicker( + {Key key, + @required this.attribute, + this.validators = const [], + this.readOnly = false, + this.decoration = const InputDecoration(), + this.style, + this.onChanged, + this.valueTransformer, + this.onSaved, + this.searchText, + this.titlePadding, + this.dialogTitle, + this.isSearchable, + @required this.defaultSelectedCountryIsoCode, + this.priorityListByIsoCode, + this.countryFilterByIsoCode, + this.dialogTextStyle, + this.isCupertinoPicker, + this.cupertinoPickerSheetHeight, + this.cursorColor}) + : assert(defaultSelectedCountryIsoCode != null), + super(key: key); + + @override + _FormBuilderCountryPickerState createState() => _FormBuilderCountryPickerState(); +} + +class _FormBuilderCountryPickerState extends State { + bool _readOnly = false; + final GlobalKey _fieldKey = GlobalKey(); + FormBuilderState _formState; + Country _selectedDialogCountry; + + void _openCupertinoCountryPicker() => showCupertinoModalPopup( + context: context, + builder: (BuildContext context) { + return CountryPickerCupertino( + pickerSheetHeight: widget.cupertinoPickerSheetHeight ?? 300.0, + onValuePicked: (Country country) => setState(() => _selectedDialogCountry = country), + itemFilter: widget.countryFilterByIsoCode != null + ? (c) => widget.countryFilterByIsoCode.contains(c.isoCode) + : null, + priorityList: widget.priorityListByIsoCode != null + ? List.generate(widget.priorityListByIsoCode.length, + (index) => CountryPickerUtils.getCountryByIsoCode(widget.priorityListByIsoCode[index])) + : null, + ); + }, + ); + + void _openCountryPickerDialog() => showDialog( + context: context, + builder: (context) => Theme( + data: Theme.of(context).copyWith( + cursorColor: Theme.of(context).primaryColor, + primaryColor: widget.cursorColor ?? Theme.of(context).primaryColor, + ), + child: CountryPickerDialog( + titlePadding: widget.titlePadding ?? EdgeInsets.all(8.0), + searchCursorColor: widget.cursorColor ?? Theme.of(context).primaryColor, + searchInputDecoration: InputDecoration(hintText: widget.searchText ?? 'Search...'), + isSearchable: widget.isSearchable ?? true, + title: widget.dialogTitle ?? + Text( + 'Select Your Country', + style: widget.dialogTextStyle ?? widget.style, + ), + onValuePicked: (Country country) => setState(() => _selectedDialogCountry = country), + itemFilter: widget.countryFilterByIsoCode != null + ? (c) => widget.countryFilterByIsoCode.contains(c.isoCode) + : null, + priorityList: widget.priorityListByIsoCode != null + ? List.generate(widget.priorityListByIsoCode.length, + (index) => CountryPickerUtils.getCountryByIsoCode(widget.priorityListByIsoCode[index])) + : null, + itemBuilder: _buildDialogItem, + ), + ), + ); + + Widget _buildDialogItem(Country country) => Container( + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: CountryPickerUtils.getDefaultFlagImage(country), + title: Text("${country.name}"), + visualDensity: VisualDensity.compact, + ), + ); + + @override + void initState() { + _formState = FormBuilder.of(context); + _formState?.registerFieldKey(widget.attribute, _fieldKey); + _selectedDialogCountry = CountryPickerUtils.getCountryByIsoCode(widget.defaultSelectedCountryIsoCode); + + super.initState(); + } + + @override + void dispose() { + _formState?.unregisterFieldKey(widget.attribute); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + _readOnly = (_formState?.readOnly == true) ? true : widget.readOnly; + + return FormField( + key: _fieldKey, + enabled: !_readOnly, + initialValue: CountryPickerUtils.getCountryByIsoCode(widget.defaultSelectedCountryIsoCode), + validator: (val) { + for (int i = 0; i < widget.validators.length; i++) { + if (widget.validators[i](val) != null) return widget.validators[i](val); + } + return null; + }, + onSaved: (val) { + dynamic transformed; + if (widget.valueTransformer != null) { + transformed = widget.valueTransformer(_selectedDialogCountry); + _formState?.setAttributeValue(widget.attribute, transformed); + } else + _formState?.setAttributeValue(widget.attribute, _selectedDialogCountry.name); + if (widget.onSaved != null) { + widget.onSaved(transformed ?? _selectedDialogCountry.name); + } + }, + builder: (FormFieldState field) { + return GestureDetector( + onTap: widget.isCupertinoPicker != null + ? (widget.isCupertinoPicker ? _openCupertinoCountryPicker : _openCountryPickerDialog) + : _openCountryPickerDialog, + child: InputDecorator( + decoration: widget.decoration.copyWith( + errorText: field.errorText, + ), + child: Row( + children: [ + CountryPickerUtils.getDefaultFlagImage(_selectedDialogCountry), + SizedBox( + width: 10, + ), + Expanded( + child: Text( + "${_selectedDialogCountry.name}", + style: widget.style, + ), + ), + ], + ), + ), + ); + }, + ); + } +} diff --git a/lib/src/fields/form_builder_phone_field.dart b/lib/src/fields/form_builder_phone_field.dart new file mode 100644 index 0000000000..cbd7b80bed --- /dev/null +++ b/lib/src/fields/form_builder_phone_field.dart @@ -0,0 +1,287 @@ +import 'package:country_pickers/country.dart'; +import 'package:country_pickers/country_pickers.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_form_builder/flutter_form_builder.dart'; +import 'package:flutter_form_builder/src/always_disabled_focus_node.dart'; + +class FormBuilderPhoneField extends StatefulWidget { + final String attribute; + final List validators; + final String initialValue; + final bool readOnly; + final InputDecoration decoration; + final ValueChanged onChanged; + final ValueTransformer valueTransformer; + + final bool autovalidate; + final int maxLines; + final TextInputType keyboardType; + final bool obscureText; + final TextStyle style; + final TextEditingController controller; + final FocusNode focusNode; + final TextCapitalization textCapitalization; + final TextInputAction textInputAction; + final StrutStyle strutStyle; + final TextDirection textDirection; + final TextAlign textAlign; + final bool autofocus; + final bool autocorrect; + final bool maxLengthEnforced; + final int maxLength; + final VoidCallback onEditingComplete; + final ValueChanged onFieldSubmitted; + final List inputFormatters; + final bool enabled; + final double cursorWidth; + final Radius cursorRadius; + final Color cursorColor; + final Brightness keyboardAppearance; + final EdgeInsets scrollPadding; + final bool enableInteractiveSelection; + final InputCounterWidgetBuilder buildCounter; + final bool expands; + final int minLines; + final bool showCursor; + final FormFieldSetter onSaved; + final VoidCallback onTap; + + // For country dialog + final String searchText; + final EdgeInsets titlePadding; + final bool isSearchable; + final Text dialogTitle; + final String defaultSelectedCountryIsoCode; + final List priorityListByIsoCode; + final List countryFilterByIsoCode; + final TextStyle dialogTextStyle; + final bool isCupertinoPicker; + final double cupertinoPickerSheetHeight; + + FormBuilderPhoneField( + {Key key, + @required this.attribute, + this.initialValue, + this.validators = const [], + this.readOnly = false, + this.decoration = const InputDecoration(), + this.autovalidate = false, + this.maxLines, + this.obscureText = false, + this.textCapitalization = TextCapitalization.none, + this.scrollPadding = const EdgeInsets.all(20.0), + this.enabled = true, + this.enableInteractiveSelection = true, + this.maxLengthEnforced = true, + this.textAlign = TextAlign.start, + this.autofocus = false, + this.autocorrect = true, + this.cursorWidth = 2.0, + this.keyboardType, + this.style, + this.controller, + this.focusNode, + this.textInputAction, + this.strutStyle, + this.textDirection, + this.maxLength, + this.onEditingComplete, + this.onFieldSubmitted, + this.inputFormatters, + this.cursorRadius, + this.cursorColor, + this.keyboardAppearance, + this.buildCounter, + this.onChanged, + this.valueTransformer, + this.expands = false, + this.minLines, + this.showCursor, + this.onSaved, + this.onTap, + this.searchText, + this.titlePadding, + this.dialogTitle, + this.isSearchable, + @required this.defaultSelectedCountryIsoCode, + this.priorityListByIsoCode, + this.countryFilterByIsoCode, + this.dialogTextStyle, + this.isCupertinoPicker, + this.cupertinoPickerSheetHeight}) + : assert(initialValue == null || controller == null || defaultSelectedCountryIsoCode != null), + super(key: key); + + @override + FormBuilderPhoneFieldState createState() => FormBuilderPhoneFieldState(); +} + +class FormBuilderPhoneFieldState extends State { + bool _readOnly = false; + TextEditingController _effectiveController = TextEditingController(); + FormBuilderState _formState; + final GlobalKey _fieldKey = GlobalKey(); + String _initialValue; + Country _selectedDialogCountry; + + void _openCupertinoCountryPicker() => showCupertinoModalPopup( + context: context, + builder: (BuildContext context) { + return CountryPickerCupertino( + pickerSheetHeight: widget.cupertinoPickerSheetHeight ?? 300.0, + onValuePicked: (Country country) => setState(() => _selectedDialogCountry = country), + itemFilter: widget.countryFilterByIsoCode != null + ? (c) => widget.countryFilterByIsoCode.contains(c.isoCode) + : null, + priorityList: widget.priorityListByIsoCode != null + ? List.generate(widget.priorityListByIsoCode.length, + (index) => CountryPickerUtils.getCountryByIsoCode(widget.priorityListByIsoCode[index])) + : null, + ); + }, + ); + + void _openCountryPickerDialog() => showDialog( + context: context, + builder: (context) => Theme( + data: Theme.of(context).copyWith( + cursorColor: widget.cursorColor, + primaryColor: widget.cursorColor ?? Theme.of(context).primaryColor, + ), + child: CountryPickerDialog( + titlePadding: widget.titlePadding ?? EdgeInsets.all(8.0), + searchCursorColor: widget.cursorColor ?? Theme.of(context).primaryColor, + searchInputDecoration: InputDecoration(hintText: widget.searchText ?? 'Search...'), + isSearchable: widget.isSearchable ?? true, + title: widget.dialogTitle ?? + Text( + 'Select Your Phone Code', + style: widget.dialogTextStyle ?? widget.style, + ), + onValuePicked: (Country country) => setState(() => _selectedDialogCountry = country), + itemFilter: widget.countryFilterByIsoCode != null + ? (c) => widget.countryFilterByIsoCode.contains(c.isoCode) + : null, + priorityList: widget.priorityListByIsoCode != null + ? List.generate(widget.priorityListByIsoCode.length, + (index) => CountryPickerUtils.getCountryByIsoCode(widget.priorityListByIsoCode[index])) + : null, + itemBuilder: _buildDialogItem), + ), + ); + + Widget _buildDialogItem(Country country) => Container( + child: ListTile( + contentPadding: EdgeInsets.zero, + leading: CountryPickerUtils.getDefaultFlagImage(country), + title: Text("${country.name}"), + visualDensity: VisualDensity.compact, + trailing: Text("+${country.phoneCode}"), + ), + ); + + @override + void initState() { + _formState = FormBuilder.of(context); + _formState?.registerFieldKey(widget.attribute, _fieldKey); + _initialValue = widget.initialValue ?? + (_formState.initialValue.containsKey(widget.attribute) + ? _formState.initialValue[widget.attribute] + : null); + if (widget.controller != null) + _effectiveController = widget.controller; + else + _effectiveController.text = "${_initialValue ?? ''}"; + + _effectiveController.addListener(() { + if (widget.onChanged != null) widget.onChanged(_effectiveController.text); + }); + _selectedDialogCountry = CountryPickerUtils.getCountryByIsoCode(widget.defaultSelectedCountryIsoCode); + super.initState(); + } + + @override + Widget build(BuildContext context) { + _readOnly = (_formState?.readOnly == true) ? true : widget.readOnly; + + return TextFormField( + key: _fieldKey, + validator: (val) { + for (int i = 0; i < widget.validators.length; i++) { + if (widget.validators[i](val) != null) return widget.validators[i](val); + } + return null; + }, + onSaved: (val) { + var transformed; + if (widget.valueTransformer != null) { + transformed = widget.valueTransformer(val); + _formState?.setAttributeValue( + widget.attribute, '+' + _selectedDialogCountry.phoneCode + transformed); + } else + _formState?.setAttributeValue(widget.attribute, '+' + _selectedDialogCountry.phoneCode + val); + if (widget.onSaved != null) { + widget.onSaved('+' + _selectedDialogCountry.phoneCode + transformed ?? + '+' + _selectedDialogCountry.phoneCode + val); + } + }, + enabled: !_readOnly, + style: widget.style, + focusNode: _readOnly ? AlwaysDisabledFocusNode() : widget.focusNode, + decoration: widget.decoration.copyWith( + enabled: !_readOnly, + prefix: GestureDetector( + onTap: widget.isCupertinoPicker != null + ? (widget.isCupertinoPicker ? _openCupertinoCountryPicker : _openCountryPickerDialog) + : _openCountryPickerDialog, + child: Text( + "+${_selectedDialogCountry.phoneCode} ", + style: widget.style, + ), + ), + ), + autovalidate: widget.autovalidate ?? false, + // initialValue: "${_initialValue ?? ''}", + maxLines: widget.maxLines, + keyboardType: widget.keyboardType, + obscureText: widget.obscureText, + onEditingComplete: widget.onEditingComplete, + controller: _effectiveController, + autocorrect: widget.autocorrect, + autofocus: widget.autofocus, + buildCounter: widget.buildCounter, + cursorColor: widget.cursorColor, + cursorRadius: widget.cursorRadius, + cursorWidth: widget.cursorWidth, + enableInteractiveSelection: widget.enableInteractiveSelection, + maxLength: widget.maxLength, + inputFormatters: widget.inputFormatters, + keyboardAppearance: widget.keyboardAppearance, + maxLengthEnforced: widget.maxLengthEnforced, + onFieldSubmitted: widget.onFieldSubmitted, + scrollPadding: widget.scrollPadding, + textAlign: widget.textAlign, + textCapitalization: widget.textCapitalization, + textDirection: widget.textDirection, + textInputAction: widget.textInputAction, + strutStyle: widget.strutStyle, + readOnly: _readOnly, + expands: widget.expands, + minLines: widget.minLines, + showCursor: widget.showCursor, + onTap: widget.onTap, + ); + } + + @override + void dispose() { + _formState?.unregisterFieldKey(widget.attribute); + if (widget.controller == null) { + _effectiveController.dispose(); + } + super.dispose(); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 6be55b2e47..bb8139d365 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,6 +22,7 @@ dependencies: flutter_touch_spin: ^1.0.1 image_picker: ^0.6.5+3 rating_bar: ^0.2.0 + country_pickers: ^1.2.1 dev_dependencies: flutter_test: