diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a54593..618f98a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## [9.2.2] +- added language selection to demo +- fixed a bug where country selection language + ## [9.2.1] - fix readme diff --git a/README.md b/README.md index 142dc7e..da966d3 100644 --- a/README.md +++ b/README.md @@ -187,7 +187,6 @@ This package uses the `flutter_country_selector` package under the hood, which e - hi - hu - it - - ku - nb - nl - pt diff --git a/demo.gif b/demo.gif index a32dfeb..b13fe23 100644 Binary files a/demo.gif and b/demo.gif differ diff --git a/example/lib/main.dart b/example/lib/main.dart index b398280..e4b0fa6 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -10,6 +10,32 @@ void main() { // For a simpler example see the README class PhoneFieldView extends StatelessWidget { + static const supportedLocales = [ + Locale('ar'), + // not supported by material yet + // Locale('ckb'), + Locale('de'), + Locale('el'), + Locale('en'), + Locale('es'), + Locale('fa'), + Locale('fr'), + Locale('hi'), + Locale('hu'), + Locale('it'), + // not supported by material yet + // Locale('ku'), + Locale('nb'), + Locale('nl'), + Locale('pt'), + Locale('ru'), + Locale('sv'), + Locale('tr'), + Locale('uz'), + Locale('zh'), + // ... + ]; + final PhoneController controller; final FocusNode focusNode; final CountrySelectorNavigator selectorNavigator; @@ -17,7 +43,7 @@ class PhoneFieldView extends StatelessWidget { final bool outlineBorder; final bool isCountryButtonPersistant; final bool mobileOnly; - final bool useRtl; + final Locale locale; const PhoneFieldView({ Key? key, @@ -28,7 +54,7 @@ class PhoneFieldView extends StatelessWidget { required this.outlineBorder, required this.isCountryButtonPersistant, required this.mobileOnly, - required this.useRtl, + required this.locale, }) : super(key: key); PhoneNumberInputValidator? _getValidator(BuildContext context) { @@ -44,36 +70,42 @@ class PhoneFieldView extends StatelessWidget { @override Widget build(BuildContext context) { return AutofillGroup( - child: Directionality( - textDirection: useRtl ? TextDirection.rtl : TextDirection.ltr, - child: PhoneFormField( - focusNode: focusNode, - controller: controller, - isCountryButtonPersistent: isCountryButtonPersistant, - autofocus: false, - autofillHints: const [AutofillHints.telephoneNumber], - countrySelectorNavigator: selectorNavigator, - decoration: InputDecoration( - label: withLabel ? const Text('Phone') : null, - border: outlineBorder - ? const OutlineInputBorder() - : const UnderlineInputBorder(), - hintText: withLabel ? '' : 'Phone', - ), - enabled: true, - countryButtonStyle: const CountryButtonStyle( - showFlag: true, - showIsoCode: false, - showDialCode: true, - showDropdownIcon: true, - ), - validator: _getValidator(context), - autovalidateMode: AutovalidateMode.onUserInteraction, - cursorColor: Theme.of(context).colorScheme.primary, - // ignore: avoid_print - onSaved: (p) => print('saved $p'), - // ignore: avoid_print - onChanged: (p) => print('changed $p'), + child: Localizations.override( + context: context, + locale: locale, + child: Builder( + builder: (context) { + final label = PhoneFieldLocalization.of(context).phoneNumber; + return PhoneFormField( + focusNode: focusNode, + controller: controller, + isCountryButtonPersistent: isCountryButtonPersistant, + autofocus: false, + autofillHints: const [AutofillHints.telephoneNumber], + countrySelectorNavigator: selectorNavigator, + decoration: InputDecoration( + label: withLabel ? Text(label) : null, + border: outlineBorder + ? const OutlineInputBorder() + : const UnderlineInputBorder(), + hintText: withLabel ? '' : label, + ), + enabled: true, + countryButtonStyle: const CountryButtonStyle( + showFlag: true, + showIsoCode: false, + showDialCode: true, + showDropdownIcon: true, + ), + validator: _getValidator(context), + autovalidateMode: AutovalidateMode.onUserInteraction, + cursorColor: Theme.of(context).colorScheme.primary, + // ignore: avoid_print + onSaved: (p) => print('saved $p'), + // ignore: avoid_print + onChanged: (p) => print('changed $p'), + ); + }, ), ), ); @@ -87,19 +119,8 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { return MaterialApp( localizationsDelegates: PhoneFieldLocalization.delegates, - supportedLocales: const [ - Locale('en', ''), - Locale('fr', ''), - Locale('es', ''), - Locale('el', ''), - Locale('de', ''), - Locale('it', ''), - Locale('ru', ''), - Locale('sv', ''), - Locale('tr', ''), - Locale('zh', ''), - // ... - ], + supportedLocales: PhoneFieldView.supportedLocales, + locale: const Locale('en'), title: 'Phone field demo', theme: ThemeData( brightness: Brightness.dark, @@ -125,9 +146,9 @@ class PhoneFormFieldScreenState extends State { bool mobileOnly = true; bool isCountryButtonPersistent = true; bool withLabel = true; - bool useRtl = false; CountrySelectorNavigator selectorNavigator = const CountrySelectorNavigator.page(); + Locale locale = const Locale('en'); final formKey = GlobalKey(); @override @@ -179,12 +200,30 @@ class PhoneFormFieldScreenState extends State { onChanged: (v) => setState(() => mobileOnly = v), title: const Text('Mobile phone number only'), ), - SwitchListTile( - value: useRtl, - onChanged: (v) { - setState(() => useRtl = v); - }, - title: const Text('RTL'), + ListTile( + title: Wrap( + alignment: WrapAlignment.spaceBetween, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + const Text('Language: '), + DropdownButton( + value: locale, + onChanged: (Locale? value) { + if (value != null) { + setState(() => locale = value); + } + }, + items: [ + for (final locale + in PhoneFieldView.supportedLocales) + DropdownMenuItem( + value: locale, + child: Text(locale.toLanguageTag()), + ), + ], + ), + ], + ), ), ListTile( title: Wrap( @@ -244,7 +283,7 @@ class PhoneFormFieldScreenState extends State { isCountryButtonPersistant: isCountryButtonPersistent, mobileOnly: mobileOnly, - useRtl: useRtl, + locale: locale, ), ], ), diff --git a/l10n/ku.arb b/l10n/ku.arb index 48e68dd..6b382b9 100644 --- a/l10n/ku.arb +++ b/l10n/ku.arb @@ -1,10 +1,10 @@ { "@@locale": "ku", - "invalidPhoneNumber": "ژمارەی تەلەفۆنی نادروست", - "invalidCountry": "وڵاتێکی نادروست", - "invalidMobilePhoneNumber": "ژمارەی مۆبایل نادروستە", - "invalidFixedLinePhoneNumber": "ژمارەی تەلەفۆنی هێڵی جێگیر نادروستە", - "requiredPhoneNumber": "ژمارەی تەلەفۆنی پێویست", + "invalidPhoneNumber": "Hejmara têlefonê nederbasdar e", + "invalidCountry": "Welatê nederbasdar", + "invalidMobilePhoneNumber": "Hejmara têlefona desta nederbasdar e", + "invalidFixedLinePhoneNumber": "Hejmara têlefonê ya xeta sabît nederbasdar e", + "requiredPhoneNumber": "Hejmara têlefonê ya pêwîst", "selectACountrySemanticLabel": "Welatek hilbijêre, bijartina niha: {countryName} {dialCode}", "@selectACountrySemanticLabel": { "description": "semantic description of the country button", diff --git a/lib/src/country_selector_navigator.dart b/lib/src/country_selector_navigator.dart index 74f61a2..53bd032 100644 --- a/lib/src/country_selector_navigator.dart +++ b/lib/src/country_selector_navigator.dart @@ -43,25 +43,35 @@ abstract class CountrySelectorNavigator { Future show(BuildContext context); - CountrySelectorSheet _getCountrySelector({ + Localizations _getCountrySelectorSheet({ + /// the context of the input + /// used to have the country selection sheet + /// in the same language as the input if the language + /// was overriden locally with Localizations.override + /// see: https://github.com/flutter/flutter/issues/145824 + required BuildContext inputContext, required ValueChanged onCountrySelected, ScrollController? scrollController, }) { - return CountrySelector.sheet( - countries: countries ?? IsoCode.values, - favoriteCountries: favorites ?? [], - onCountrySelected: onCountrySelected, - showDialCode: showDialCode, - noResultMessage: noResultMessage, - scrollController: scrollController, - searchAutofocus: searchAutofocus, - subtitleStyle: subtitleStyle, - titleStyle: titleStyle, - searchBoxDecoration: searchBoxDecoration, - searchBoxTextStyle: searchBoxTextStyle, - searchBoxIconColor: searchBoxIconColor, - scrollPhysics: scrollPhysics, - flagSize: flagSize, + return Localizations.override( + context: inputContext, + locale: Localizations.localeOf(inputContext), + child: CountrySelector.sheet( + countries: countries ?? IsoCode.values, + favoriteCountries: favorites ?? [], + onCountrySelected: onCountrySelected, + showDialCode: showDialCode, + noResultMessage: noResultMessage, + scrollController: scrollController, + searchAutofocus: searchAutofocus, + subtitleStyle: subtitleStyle, + titleStyle: titleStyle, + searchBoxDecoration: searchBoxDecoration, + searchBoxTextStyle: searchBoxTextStyle, + searchBoxIconColor: searchBoxIconColor, + scrollPhysics: scrollPhysics, + flagSize: flagSize, + ), ); } @@ -186,7 +196,8 @@ class DialogNavigator extends CountrySelectorNavigator { child: SizedBox( width: width, height: height, - child: _getCountrySelector( + child: _getCountrySelectorSheet( + inputContext: context, onCountrySelected: (country) => Navigator.of(context, rootNavigator: true).pop(country), ), @@ -216,20 +227,25 @@ class PageNavigator extends CountrySelectorNavigator { final ThemeData? appBarTheme; - CountrySelectorPage _getCountrySelectorPage({ + Localizations _getCountrySelectorPage({ required ValueChanged onCountrySelected, + required BuildContext inputContext, ScrollController? scrollController, }) { - return CountrySelector.page( - onCountrySelected: onCountrySelected, - scrollController: scrollController, - countries: countries ?? IsoCode.values, - favoriteCountries: favorites ?? [], - noResultMessage: noResultMessage, - searchAutofocus: searchAutofocus, - showDialCode: showDialCode, - titleStyle: titleStyle, - subtitleStyle: subtitleStyle, + return Localizations.override( + context: inputContext, + locale: Localizations.localeOf(inputContext), + child: CountrySelector.page( + onCountrySelected: onCountrySelected, + scrollController: scrollController, + countries: countries ?? IsoCode.values, + favoriteCountries: favorites ?? [], + noResultMessage: noResultMessage, + searchAutofocus: searchAutofocus, + showDialCode: showDialCode, + titleStyle: titleStyle, + subtitleStyle: subtitleStyle, + ), ); } @@ -241,6 +257,7 @@ class PageNavigator extends CountrySelectorNavigator { MaterialPageRoute( builder: (ctx) => _getCountrySelectorPage( onCountrySelected: (country) => Navigator.pop(context, country), + inputContext: context, ), ), ); @@ -274,7 +291,8 @@ class BottomSheetNavigator extends CountrySelectorNavigator { builder: (_) => MediaQuery( data: MediaQueryData.fromView(View.of(context)), child: SafeArea( - child: _getCountrySelector( + child: _getCountrySelectorSheet( + inputContext: context, onCountrySelected: (country) { selected = country; Navigator.pop(context, country); @@ -315,7 +333,8 @@ class ModalBottomSheetNavigator extends CountrySelectorNavigator { context: context, builder: (_) => SizedBox( height: height ?? MediaQuery.of(context).size.height - 90, - child: _getCountrySelector( + child: _getCountrySelectorSheet( + inputContext: context, onCountrySelected: (country) => Navigator.pop(context, country), ), ), @@ -370,65 +389,14 @@ class DraggableModalBottomSheetNavigator extends CountrySelectorNavigator { minChildSize: minChildSize, maxChildSize: maxChildSize, expand: false, - builder: (context, scrollController) { - return _CountrySelectorWidget( - scrollController: scrollController, - borderRadius: effectiveBorderRadius, - child: _getCountrySelector( - onCountrySelected: (country) => Navigator.pop(context, country), - scrollController: scrollController, - ), - ); - }, + builder: (context, scrollController) => _getCountrySelectorSheet( + inputContext: context, + onCountrySelected: (country) => Navigator.pop(context, country), + scrollController: scrollController, + ), ), useRootNavigator: useRootNavigator, isScrollControlled: true, ); } } - -class _CountrySelectorWidget extends StatefulWidget { - final ScrollController scrollController; - final BorderRadiusGeometry borderRadius; - final Widget child; - - const _CountrySelectorWidget({ - required this.scrollController, - required this.child, - required this.borderRadius, - }); - - @override - State<_CountrySelectorWidget> createState() => _CountrySelectorWidgetState(); -} - -class _CountrySelectorWidgetState extends State<_CountrySelectorWidget> { - @override - initState() { - super.initState(); - widget.scrollController.addListener(_onScrollListener); - } - - @override - dispose() { - widget.scrollController.removeListener(_onScrollListener); - super.dispose(); - } - - _onScrollListener() { - FocusManager.instance.primaryFocus?.unfocus(); - } - - @override - Widget build(BuildContext context) { - return Container( - decoration: ShapeDecoration( - color: Theme.of(context).canvasColor, - shape: RoundedRectangleBorder( - borderRadius: widget.borderRadius, - ), - ), - child: widget.child, - ); - } -} diff --git a/lib/src/localization/generated/phone_field_localization_impl_ku.dart b/lib/src/localization/generated/phone_field_localization_impl_ku.dart index f896b5e..934db5a 100644 --- a/lib/src/localization/generated/phone_field_localization_impl_ku.dart +++ b/lib/src/localization/generated/phone_field_localization_impl_ku.dart @@ -5,20 +5,20 @@ class PhoneFieldLocalizationImplKu extends PhoneFieldLocalizationImpl { PhoneFieldLocalizationImplKu([super.locale = 'ku']); @override - String get invalidPhoneNumber => 'ژمارەی تەلەفۆنی نادروست'; + String get invalidPhoneNumber => 'Hejmara têlefonê nederbasdar e'; @override - String get invalidCountry => 'وڵاتێکی نادروست'; + String get invalidCountry => 'Welatê nederbasdar'; @override - String get invalidMobilePhoneNumber => 'ژمارەی مۆبایل نادروستە'; + String get invalidMobilePhoneNumber => 'Hejmara têlefona desta nederbasdar e'; @override String get invalidFixedLinePhoneNumber => - 'ژمارەی تەلەفۆنی هێڵی جێگیر نادروستە'; + 'Hejmara têlefonê ya xeta sabît nederbasdar e'; @override - String get requiredPhoneNumber => 'ژمارەی تەلەفۆنی پێویست'; + String get requiredPhoneNumber => 'Hejmara têlefonê ya pêwîst'; @override String selectACountrySemanticLabel(String countryName, String dialCode) { diff --git a/lib/src/phone_form_field_state.dart b/lib/src/phone_form_field_state.dart index 3b0f9d7..b2f6c4d 100644 --- a/lib/src/phone_form_field_state.dart +++ b/lib/src/phone_form_field_state.dart @@ -64,7 +64,7 @@ class PhoneFormFieldState extends FormFieldState { super.reset(); } - void _selectCountry() async { + void _selectCountry(BuildContext context) async { if (!widget.isCountrySelectionEnabled) { return; } @@ -173,7 +173,7 @@ class PhoneFormFieldState extends FormFieldState { builder: (context, _) => CountryButton( key: const ValueKey('country-code-chip'), isoCode: controller.value.isoCode, - onTap: widget.enabled ? _selectCountry : null, + onTap: widget.enabled ? () => _selectCountry(context) : null, padding: _computeCountryButtonPadding(context), showFlag: widget.countryButtonStyle.showFlag, showIsoCode: widget.countryButtonStyle.showIsoCode, diff --git a/pubspec.yaml b/pubspec.yaml index 614a24b..82cb2ea 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: phone_form_field description: Flutter phone input integrated with flutter internationalization -version: 9.2.1 +version: 9.2.2 homepage: https://github.com/cedvdb/phone_form_field environment: