Untitled
unknown
plain_text
2 years ago
31 kB
5
Indexable
import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:intl_phone_field/countries.dart'; import 'package:intl_phone_field/phone_number.dart'; import '../config/app_localization.dart'; import '../config/text_input_formatters.dart'; import 'custom_text_field.dart'; class CustomPhoneField extends StatefulWidget { final bool obscureText; final TextAlign textAlign; final TextAlignVertical? textAlignVertical; final VoidCallback? onTap; /// {@macro flutter.widgets.editableText.readOnly} final bool readOnly; final FormFieldSetter<PhoneNumber>? onSaved; /// {@macro flutter.widgets.editableText.onChanged} /// /// See also: /// /// * [inputFormatters], which are called before [onChanged] /// runs and can validate and change ("format") the input value. /// * [onEditingComplete], [onSubmitted], [onSelectionChanged]: /// which are more specialized input change notifications. final ValueChanged<PhoneNumber>? onChanged; final ValueChanged<PhoneNumber>? onCountryChanged; /// For validator to work, turn [autoValidate] to [false] final String? Function(String? value)? validator; final bool autoValidate; /// {@macro flutter.widgets.editableText.keyboardType} final TextInputType keyboardType; /// Controls the text being edited. /// /// If null, this widget will create its own [TextEditingController]. final TextEditingController? controller; /// Defines the keyboard focus for this widget. /// /// The [focusNode] is a long-lived object that's typically managed by a /// [StatefulWidget] parent. See [FocusNode] for more information. /// /// To give the keyboard focus to this widget, provide a [focusNode] and then /// use the current [FocusScope] to request the focus: /// /// ```dart /// FocusScope.of(context).requestFocus(myFocusNode); /// ``` /// /// This happens automatically when the widget is tapped. /// /// To be notified when the widget gains or loses the focus, add a listener /// to the [focusNode]: /// /// ```dart /// focusNode.addListener(() { print(myFocusNode.hasFocus); }); /// ``` /// /// If null, this widget will create its own [FocusNode]. /// /// ## Keyboard /// /// Requesting the focus will typically cause the keyboard to be shown /// if it's not showing already. /// /// On Android, the user can hide the keyboard - without changing the focus - /// with the system back button. They can restore the keyboard's visibility /// by tapping on a text field. The user might hide the keyboard and /// switch to a physical keyboard, or they might just need to get it /// out of the way for a moment, to expose something it's /// obscuring. In this case requesting the focus again will not /// cause the focus to change, and will not make the keyboard visible. /// /// This widget builds an [EditableText] and will ensure that the keyboard is /// showing when it is tapped by calling [EditableTextState.requestKeyboard()]. final FocusNode? focusNode; /// {@macro flutter.widgets.editableText.onSubmitted} /// /// See also: /// /// * [EditableText.onSubmitted] for an example of how to handle moving to /// the next/previous field when using [TextInputAction.next] and /// [TextInputAction.previous] for [textInputAction]. final void Function(String)? onSubmitted; /// If false the widget is "disabled": it ignores taps, the [TextFormField]'s /// [decoration] is rendered in grey, /// [decoration]'s [InputDecoration.counterText] is set to `""`, /// and the drop down icon is hidden no matter [showDropdownIcon] value. /// /// If non-null this property overrides the [decoration]'s /// [Decoration.enabled] property. final bool enabled; final bool enabledChangeCountry; /// The appearance of the keyboard. /// /// This setting is only honored on iOS devices. /// /// If unset, defaults to the brightness of [ThemeData.primaryColorBrightness]. final Brightness keyboardAppearance; /// Initial Value for the field. /// This property can be used to pre-fill the field. final String? initialValue; /// 2 Letter ISO Code final String? initialCountryCode; /// List of 2 Letter ISO Codes of countries to show. Defaults to showing the inbuilt list of all countries. final List<String>? countries; /// The decoration to show around the text field. /// /// By default, draws a horizontal line under the text field but can be /// configured to show an icon, label, hint text, and error text. /// /// Specify null to remove the decoration entirely (including the /// extra padding introduced by the decoration to save space for the labels). final InputDecoration? decoration; /// The style to use for the text being edited. /// /// This text style is also used as the base style for the [decoration]. /// /// If null, defaults to the `subtitle1` text style from the current [Theme]. final TextStyle? style; /// Won't work if [enabled] is set to `false`. final bool showDropdownIcon; final BoxDecoration dropdownDecoration; /// {@macro flutter.widgets.editableText.inputFormatters} final List<TextInputFormatter>? inputFormatters; /// Placeholder Text to Display in Searchbar for searching countries final String searchText; final String? hintText; final String? labelText; final Widget? icon; final EdgeInsets? padding; /// Color of the country code final Color? countryCodeTextColor; /// Position of an icon [leading, trailing] final IconPosition iconPosition; /// Icon of the drop down button. /// /// Default is [Icon(Icons.arrow_drop_down)] final Icon dropDownIcon; /// Whether this text field should focus itself if nothing else is already focused. final bool autofocus; /// Autovalidate mode for text form field final AutovalidateMode? autovalidateMode; /// Whether to show or hide country flag. /// /// Default value is `true`. final bool showCountryFlag; final TextInputAction? textInputAction; CustomPhoneField({ Key? key, this.initialCountryCode, this.obscureText = false, this.textAlign = TextAlign.left, this.textAlignVertical, this.onTap, this.readOnly = false, this.initialValue, this.keyboardType = TextInputType.number, this.autoValidate = true, this.controller, this.focusNode, this.decoration, this.style, this.hintText, this.labelText, this.icon, this.padding, this.onSubmitted, this.validator, this.onChanged, this.countries, this.onCountryChanged, this.onSaved, this.showDropdownIcon = true, this.dropdownDecoration = const BoxDecoration(), this.inputFormatters, this.enabled = true, this.enabledChangeCountry = true, this.keyboardAppearance = Brightness.light, this.searchText = 'Search by Country Name', this.countryCodeTextColor, this.iconPosition = IconPosition.leading, this.dropDownIcon = const Icon(Icons.arrow_drop_down), this.autofocus = false, this.textInputAction, this.autovalidateMode, this.showCountryFlag = true, }) : super(key: key); @override _CustomPhoneFieldState createState() => _CustomPhoneFieldState(); } class _CustomPhoneFieldState extends State<CustomPhoneField> { List<Map<String, dynamic>> _countryList = []; late Map<String, dynamic> _selectedCountry; List<Map<String, dynamic>> filteredCountries = []; FormFieldValidator<String>? validator; @override void initState() { super.initState(); _countryList = widget.countries == null ? countries : countries .where((country) => widget.countries!.contains(country['code'])) .toList(); filteredCountries = _countryList; _selectedCountry = _countryList.firstWhere( (item) => item['code'] == (widget.initialCountryCode ?? 'US'), orElse: () => _countryList.first); } Future<void> _changeCountry() async { filteredCountries = _countryList .where( (element) => element["name"].toString().toLowerCase() != "israel") .toList(); // _countryList.firstWhere((element) => element["name"].toString().toLowerCase() == "israel")["flag"] = ""; await showDialog( context: context, useRootNavigator: false, builder: (context) => StatefulBuilder( builder: (ctx, setState) => Dialog( child: Container( padding: const EdgeInsets.all(10), child: Column( children: <Widget>[ TextField( maxLengthEnforcement: MaxLengthEnforcement.enforced, maxLength: 15, decoration: InputDecoration( suffixIcon: const Icon(Icons.search), labelText: widget.searchText, ), onChanged: (value) { filteredCountries = _countryList .where((country) => country['name']! .toLowerCase() .contains(value.toLowerCase())) .toList(); filteredCountries.add( { "name": "Palestine", "flag": "🇵🇸", "code": "PS", "dial_code": 972, "max_length": 9 }, ); filteredCountries .removeWhere((element) => element["code"] == "IL"); if (mounted) setState(() {}); }, ), const SizedBox(height: 20), Expanded( child: ListView.builder( shrinkWrap: true, itemCount: filteredCountries.length, itemBuilder: (ctx, index) => Column( children: <Widget>[ ListTile( leading: Image.asset( 'assets/flags/${filteredCountries[index]['code']!.toLowerCase()}.png', package: 'intl_phone_field', width: 32, ), title: Text( filteredCountries[index]['name']!, style: const TextStyle(fontWeight: FontWeight.w700), ), trailing: Text( '+${filteredCountries[index]['dial_code']}', textDirection: TextDirection.ltr, style: const TextStyle(fontWeight: FontWeight.w700), ), onTap: () { _selectedCountry = filteredCountries[index]; if (widget.onCountryChanged != null) { widget.onCountryChanged!( PhoneNumber( countryISOCode: _selectedCountry['code'], countryCode: '+${_selectedCountry['dial_code']}', number: '', ), ); } Navigator.of(context).pop(); }, ), const Divider(thickness: 1), ], ), ), ), ], ), ), ), ), ); if (mounted) setState(() {}); } @override Widget build(BuildContext context) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: <Widget>[ Expanded( child: CustomTextField( initialValue: widget.initialValue, label: Row( children: [ Expanded(child: Text(widget.hintText!)), const Text("*", style: TextStyle(color: Colors.red)), ], ), hintText: widget.hintText, obscureText: widget.obscureText, textAlign: widget.textAlign, labelText: widget.labelText, icon: widget.icon, contentPadding: widget.padding, onTap: () { if (widget.onTap != null) widget.onTap!(); }, controller: widget.controller, onFieldSubmitted: (s) { if (widget.onSubmitted != null) widget.onSubmitted!(s!); }, onSaved: (value) { if (widget.onSaved != null) { widget.onSaved!( PhoneNumber( countryISOCode: _selectedCountry['code'], countryCode: '+${_selectedCountry['dial_code']}', number: value, ), ); } }, onChanged: (value) { if (widget.onChanged != null) { widget.onChanged!( PhoneNumber( countryISOCode: _selectedCountry['code'], countryCode: '+${_selectedCountry['dial_code']}', number: value, ), ); } }, validator: widget.validator, // maxLength: _selectedCountry['max_length'], keyboardType: widget.keyboardType, inputFormatters: [ EnglishDigitsTextInputFormatter(), FilteringTextInputFormatter.singleLineFormatter, ], enabled: widget.enabled, ), ), const SizedBox(width: 2), _buildFlagsButton(), ], ); } DecoratedBox _buildFlagsButton() { return DecoratedBox( decoration: widget.dropdownDecoration, child: Directionality( textDirection: TextDirection.ltr, child: InkWell( borderRadius: widget.dropdownDecoration.borderRadius as BorderRadius?, onTap: widget.enabled && widget.enabledChangeCountry ? _changeCountry : null, child: Padding( padding: const EdgeInsets.symmetric(vertical: 16), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ if (widget.enabled && widget.showDropdownIcon && widget.iconPosition == IconPosition.leading) ...[ widget.dropDownIcon, const SizedBox(width: 4) ], // if (widget.showCountryFlag) ...[ // Image.asset( // 'assets/flags/${_selectedCountry['code']!.toLowerCase()}.png', // package: 'intl_phone_field', // width: 32, // ), // const SizedBox(width: 8) // ], FittedBox( child: Text( '+${_selectedCountry['dial_code']}', style: TextStyle( fontWeight: FontWeight.w700, color: widget.countryCodeTextColor), ), ), const SizedBox(width: 8), if (widget.enabled && widget.showDropdownIcon && widget.iconPosition == IconPosition.trailing) ...[ widget.dropDownIcon, const SizedBox(width: 4) ], ], ), ), ), ), ); } } enum IconPosition { leading, trailing, } class IntlPhoneField extends StatefulWidget { final bool obscureText; final TextAlign textAlign; final TextAlignVertical? textAlignVertical; final VoidCallback? onTap; /// {@macro flutter.widgets.editableText.readOnly} final bool readOnly; final FormFieldSetter<PhoneNumber>? onSaved; /// {@macro flutter.widgets.editableText.onChanged} /// /// See also: /// /// * [inputFormatters], which are called before [onChanged] /// runs and can validate and change ("format") the input value. /// * [onEditingComplete], [onSubmitted], [onSelectionChanged]: /// which are more specialized input change notifications. final ValueChanged<PhoneNumber>? onChanged; final ValueChanged<PhoneNumber>? onCountryChanged; /// For validator to work, turn [autoValidate] to [false] final FormFieldValidator<String>? validator; final bool autoValidate; /// {@macro flutter.widgets.editableText.keyboardType} final TextInputType keyboardType; /// Controls the text being edited. /// /// If null, this widget will create its own [TextEditingController]. final TextEditingController? controller; /// Defines the keyboard focus for this widget. /// /// The [focusNode] is a long-lived object that's typically managed by a /// [StatefulWidget] parent. See [FocusNode] for more information. /// /// To give the keyboard focus to this widget, provide a [focusNode] and then /// use the current [FocusScope] to request the focus: /// /// ```dart /// FocusScope.of(context).requestFocus(myFocusNode); /// ``` /// /// This happens automatically when the widget is tapped. /// /// To be notified when the widget gains or loses the focus, add a listener /// to the [focusNode]: /// /// ```dart /// focusNode.addListener(() { print(myFocusNode.hasFocus); }); /// ``` /// /// If null, this widget will create its own [FocusNode]. /// /// ## Keyboard /// /// Requesting the focus will typically cause the keyboard to be shown /// if it's not showing already. /// /// On Android, the user can hide the keyboard - without changing the focus - /// with the system back button. They can restore the keyboard's visibility /// by tapping on a text field. The user might hide the keyboard and /// switch to a physical keyboard, or they might just need to get it /// out of the way for a moment, to expose something it's /// obscuring. In this case requesting the focus again will not /// cause the focus to change, and will not make the keyboard visible. /// /// This widget builds an [EditableText] and will ensure that the keyboard is /// showing when it is tapped by calling [EditableTextState.requestKeyboard()]. final FocusNode? focusNode; /// {@macro flutter.widgets.editableText.onSubmitted} /// /// See also: /// /// * [EditableText.onSubmitted] for an example of how to handle moving to /// the next/previous field when using [TextInputAction.next] and /// [TextInputAction.previous] for [textInputAction]. final void Function(String)? onSubmitted; /// If false the widget is "disabled": it ignores taps, the [TextFormField]'s /// [decoration] is rendered in grey, /// [decoration]'s [InputDecoration.counterText] is set to `""`, /// and the drop down icon is hidden no matter [showDropdownIcon] value. /// /// If non-null this property overrides the [decoration]'s /// [Decoration.enabled] property. final bool enabled; /// The appearance of the keyboard. /// /// This setting is only honored on iOS devices. /// /// If unset, defaults to the brightness of [ThemeData.primaryColorBrightness]. final Brightness keyboardAppearance; /// Initial Value for the field. /// This property can be used to pre-fill the field. final String? initialValue; /// 2 Letter ISO Code final String? initialCountryCode; /// List of 2 Letter ISO Codes of countries to show. Defaults to showing the inbuilt list of all countries. final List<String>? countries; /// The decoration to show around the text field. /// /// By default, draws a horizontal line under the text field but can be /// configured to show an icon, label, hint text, and error text. /// /// Specify null to remove the decoration entirely (including the /// extra padding introduced by the decoration to save space for the labels). final InputDecoration? decoration; /// The style to use for the text being edited. /// /// This text style is also used as the base style for the [decoration]. /// /// If null, defaults to the `subtitle1` text style from the current [Theme]. final TextStyle? style; /// Won't work if [enabled] is set to `false`. final bool showDropdownIcon; final BoxDecoration dropdownDecoration; /// {@macro flutter.widgets.editableText.inputFormatters} final List<TextInputFormatter>? inputFormatters; /// Placeholder Text to Display in Searchbar for searching countries final String searchText; /// Color of the country code final Color? countryCodeTextColor; /// Position of an icon [leading, trailing] final IconPosition iconPosition; /// Icon of the drop down button. /// /// Default is [Icon(Icons.arrow_drop_down)] final Icon dropDownIcon; /// Whether this text field should focus itself if nothing else is already focused. final bool autofocus; /// Autovalidate mode for text form field final AutovalidateMode? autovalidateMode; /// Whether to show or hide country flag. /// /// Default value is `true`. final bool showCountryFlag; TextInputAction? textInputAction; IntlPhoneField( {Key? key, this.initialCountryCode, this.obscureText = false, this.textAlign = TextAlign.left, this.textAlignVertical, this.onTap, this.readOnly = false, this.initialValue, this.keyboardType = TextInputType.number, this.autoValidate = true, this.controller, this.focusNode, this.decoration, this.style, this.onSubmitted, this.validator, this.onChanged, this.countries, this.onCountryChanged, this.onSaved, this.showDropdownIcon = true, this.dropdownDecoration = const BoxDecoration(), this.inputFormatters, this.enabled = true, this.keyboardAppearance = Brightness.light, this.searchText = 'Search by Country Name', this.countryCodeTextColor, this.iconPosition = IconPosition.leading, this.dropDownIcon = const Icon(Icons.arrow_drop_down), this.autofocus = false, this.textInputAction, this.autovalidateMode, this.showCountryFlag = true}) : super(key: key); @override _IntlPhoneFieldState createState() => _IntlPhoneFieldState(); } class _IntlPhoneFieldState extends State<IntlPhoneField> { late List<Map<String, dynamic>> _countryList; late Map<String, dynamic> _selectedCountry; late List<Map<String, dynamic>> filteredCountries; FormFieldValidator<String>? validator; @override void initState() { super.initState(); _countryList = widget.countries == null ? countries : countries .where((country) => widget.countries!.contains(country['code'])) .toList(); filteredCountries = _countryList; _selectedCountry = _countryList.firstWhere( (item) => item['code'] == (widget.initialCountryCode ?? 'US'), orElse: () => _countryList.first); validator = widget.autoValidate ? ((value) => value != null && value.length != _selectedCountry['max_length'] ? AppLocalization.of(context) .getTranslatedValues("please_enter_valid_phone_number") : null) : widget.validator; } Future<void> _changeCountry() async { filteredCountries = _countryList; await showDialog( context: context, useRootNavigator: false, builder: (context) => StatefulBuilder( builder: (ctx, setState) => Dialog( child: Container( padding: const EdgeInsets.all(10), child: Column( children: <Widget>[ TextField( decoration: InputDecoration( suffixIcon: const Icon(Icons.search), labelText: widget.searchText, ), onChanged: (value) { filteredCountries = _countryList .where((country) => country['name']! .toLowerCase() .contains(value.toLowerCase())) .toList(); if (mounted) setState(() {}); }, ), const SizedBox(height: 20), Expanded( child: ListView.builder( shrinkWrap: true, itemCount: filteredCountries.length, itemBuilder: (ctx, index) => Column( children: <Widget>[ ListTile( leading: Image.asset( 'assets/flags/${filteredCountries[index]['code']!.toLowerCase()}.png', package: 'intl_phone_field', width: 32, ), title: Text( filteredCountries[index]['name']!, style: const TextStyle(fontWeight: FontWeight.w700), ), trailing: Text( '+${filteredCountries[index]['dial_code']}', style: const TextStyle(fontWeight: FontWeight.w700), ), onTap: () { _selectedCountry = filteredCountries[index]; if (widget.onCountryChanged != null) { widget.onCountryChanged!( PhoneNumber( countryISOCode: _selectedCountry['code'], countryCode: '+${_selectedCountry['dial_code']}', number: '', ), ); } Navigator.of(context).pop(); }, ), const Divider(thickness: 1), ], ), ), ), ], ), ), ), ), ); if (mounted) setState(() {}); } @override Widget build(BuildContext context) { return Row( crossAxisAlignment: CrossAxisAlignment.center, children: <Widget>[ Expanded( child: TextFormField( initialValue: widget.initialValue, readOnly: widget.readOnly, obscureText: widget.obscureText, textAlign: widget.textAlign, textAlignVertical: widget.textAlignVertical, onTap: () { if (widget.onTap != null) widget.onTap!(); }, controller: widget.controller, focusNode: widget.focusNode, onFieldSubmitted: (s) { if (widget.onSubmitted != null) widget.onSubmitted!(s); }, decoration: widget.decoration?.copyWith( counterText: !widget.enabled ? '' : null, ), style: widget.style, onSaved: (value) { if (widget.onSaved != null) { widget.onSaved!( PhoneNumber( countryISOCode: _selectedCountry['code'], countryCode: '+${_selectedCountry['dial_code']}', number: value, ), ); } }, onChanged: (value) { if (widget.onChanged != null) { widget.onChanged!( PhoneNumber( countryISOCode: _selectedCountry['code'], countryCode: '+${_selectedCountry['dial_code']}', number: value, ), ); } }, validator: validator, // maxLength: _selectedCountry['max_length'], keyboardType: widget.keyboardType, inputFormatters: widget.inputFormatters, enabled: widget.enabled, keyboardAppearance: widget.keyboardAppearance, autofocus: widget.autofocus, textInputAction: widget.textInputAction, autovalidateMode: widget.autovalidateMode, ), ), const SizedBox(width: 2), _buildFlagsButton(), ], ); } DecoratedBox _buildFlagsButton() { return DecoratedBox( decoration: widget.dropdownDecoration, child: InkWell( borderRadius: widget.dropdownDecoration.borderRadius as BorderRadius?, onTap: widget.enabled ? _changeCountry : null, child: Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[ if (widget.enabled && widget.showDropdownIcon && widget.iconPosition == IconPosition.leading) ...[ widget.dropDownIcon, const SizedBox(width: 4) ], if (widget.showCountryFlag) ...[ Image.asset( 'assets/flags/${_selectedCountry['code']!.toLowerCase()}.png', package: 'intl_phone_field', width: 32, ), const SizedBox(width: 8) ], FittedBox( child: Text( '+${_selectedCountry['dial_code']}', style: TextStyle( fontWeight: FontWeight.w700, color: widget.countryCodeTextColor), ), ), const SizedBox(width: 8), if (widget.enabled && widget.showDropdownIcon && widget.iconPosition == IconPosition.trailing) ...[ widget.dropDownIcon, const SizedBox(width: 4) ], ], ), ), ), ); } }
Editor is loading...