From ad10ad3b59c9073cec895fd309b9bd4c99eab4a9 Mon Sep 17 00:00:00 2001 From: AlcalaJulian Date: Thu, 4 Dec 2025 17:32:42 +0100 Subject: [PATCH] added country_code_picker package to link phone feature --- apps/mobile_app/pubspec.lock | 24 +++++ .../presentation/link_phone_screen.dart | 30 +++---- .../presentation/link_phone_view_model.dart | 31 +++++-- .../presentation/link_phone_view_state.dart | 3 +- .../link_phone_view_state.freezed.dart | 49 ++++++----- modules/auth/pubspec.yaml | 1 + packages/design_system/lib/design_system.dart | 3 +- .../src/dropdowns/country_prefix_picker.dart | 79 +++++++++++++++++ .../lib/src/inputs/textfields.dart | 88 +++++++++++-------- packages/design_system/pubspec.yaml | 1 + 10 files changed, 221 insertions(+), 88 deletions(-) create mode 100644 packages/design_system/lib/src/dropdowns/country_prefix_picker.dart diff --git a/apps/mobile_app/pubspec.lock b/apps/mobile_app/pubspec.lock index 20e6e08c..a44e05e1 100644 --- a/apps/mobile_app/pubspec.lock +++ b/apps/mobile_app/pubspec.lock @@ -168,6 +168,14 @@ packages: url: "https://pub.dev" source: hosted version: "3.1.2" + country_code_picker: + dependency: transitive + description: + name: country_code_picker + sha256: f0411f4833b6f98e8b7215f4fa3813bcc88e50f13925f70a170dbd36e3e447f5 + url: "https://pub.dev" + source: hosted + version: "3.4.1" coverage: dependency: transitive description: @@ -214,6 +222,14 @@ packages: relative: true source: path version: "0.0.1" + diacritic: + dependency: transitive + description: + name: diacritic + sha256: "12981945ec38931748836cd76f2b38773118d0baef3c68404bdfde9566147876" + url: "https://pub.dev" + source: hosted + version: "0.1.6" dio: dependency: transitive description: @@ -432,6 +448,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.20.2" + intl_phone_field_v2: + dependency: transitive + description: + name: intl_phone_field_v2 + sha256: b1e5077e31cc8705639a69b2e0410a8ecc858c3e518726d99b378b6c35adfefb + url: "https://pub.dev" + source: hosted + version: "4.0.5" io: dependency: transitive description: diff --git a/modules/auth/lib/src/features/link_phone/presentation/link_phone_screen.dart b/modules/auth/lib/src/features/link_phone/presentation/link_phone_screen.dart index 938d7a71..a6999e39 100644 --- a/modules/auth/lib/src/features/link_phone/presentation/link_phone_screen.dart +++ b/modules/auth/lib/src/features/link_phone/presentation/link_phone_screen.dart @@ -1,4 +1,5 @@ import 'package:auth/src/features/link_phone/presentation/link_phone_view_model.dart'; +import 'package:design_system/src/dropdowns/country_prefix_picker.dart'; import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -10,13 +11,11 @@ class LinkPhoneScreen extends ConsumerWidget { const LinkPhoneScreen({super.key, required this.navigationContract}); - void _onCountryChanged(int? value) {} - @override Widget build(BuildContext context, WidgetRef ref) { final theme = ref.watch(themePortProvider); - final viewModel = ref.watch(linkPhoneViewModelProvider.notifier); + final viewModel = ref.read(linkPhoneViewModelProvider.notifier); final viewState = ref.watch(linkPhoneViewModelProvider); return Scaffold( @@ -56,19 +55,17 @@ class LinkPhoneScreen extends ConsumerWidget { Row( spacing: 10, children: [ - CustomDropdown( - value: 0, - items: const [ - Icon(Icons.outlined_flag), - Icon(Icons.outlined_flag), - Icon(Icons.outlined_flag), - ], - onChanged: _onCountryChanged, - width: 80, + CountryPrefixPicker( + initialCountryCode: viewState.dialCode, + onChanged: (country) { + viewModel.updateDialCode( + country.dialCode ?? viewState.dialCode, + ); + }, ), Expanded( child: CustomTextField( - // controller: viewModel.phoneNumberController, + controller: viewModel.phoneNumberController, hint: context.translate(I18n.phoneNumber), numeric: true, ), @@ -80,11 +77,10 @@ class LinkPhoneScreen extends ConsumerWidget { const SizedBox(height: 16), - if (viewState.errorMessage?.isNotEmpty ?? false) ...[ + if (viewState.errorMessage.isNotEmpty) ...[ const SizedBox(height: 4), Text( - // context.translate(viewState.errorMessage - '', + viewState.errorMessage, textAlign: TextAlign.center, style: const TextStyle( color: Color.fromRGBO(239, 17, 17, 1), @@ -99,7 +95,7 @@ class LinkPhoneScreen extends ConsumerWidget { onPressed: () async { await viewModel.requestCode(); final updatedState = ref.read(linkPhoneViewModelProvider); - if (updatedState.errorMessage!.isEmpty) { + if (updatedState.errorMessage.isEmpty) { navigationContract.pushTo(AppRoutes.phoneCode); } }, diff --git a/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_model.dart b/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_model.dart index c921380f..8b5a616e 100644 --- a/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_model.dart +++ b/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_model.dart @@ -17,30 +17,42 @@ class LinkPhoneViewModel extends Notifier { @override LinkPhoneViewState build() { _linkPhoneUseCase = ref.read(linkPhoneUseCaseProvider); + phoneNumberController = TextEditingController(); phoneNumberController.addListener(_onPhoneNumberChanged); + ref.onDispose(disposeControllers); + return const LinkPhoneViewState(); } void _onPhoneNumberChanged() { final raw = phoneNumberController.text; - state = state.copyWith(phoneNumber: raw, errorMessage: ''); } - Future requestCode() async { - final phone = phoneNumberController.text.trim(); + void updateDialCode(String dialCode) { + state = state.copyWith(dialCode: dialCode, errorMessage: ''); + } - if (phone.isEmpty) { + Future requestCode() async { + final trimmedNumber = state.phoneNumber.trim(); + + if (trimmedNumber.isEmpty) { state = state.copyWith(errorMessage: 'El teléfono no puede estar vacío'); return; } - state = state.copyWith(isLoading: true, errorMessage: ''); + final fullPhone = '${state.dialCode}$trimmedNumber'; + + state = state.copyWith( + isLoading: true, + errorMessage: '', + codeRequested: false, + ); try { - await _linkPhoneUseCase.requestCode(phone: phone); + await _linkPhoneUseCase.requestCode(phone: fullPhone); if (!ref.mounted) return; state = state.copyWith( @@ -50,7 +62,12 @@ class LinkPhoneViewModel extends Notifier { ); } catch (e) { if (!ref.mounted) return; - state = state.copyWith(isLoading: false, errorMessage: e.toString()); + + state = state.copyWith( + isLoading: false, + errorMessage: e.toString(), + codeRequested: false, + ); } } diff --git a/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_state.dart b/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_state.dart index 3d194348..328c4e72 100644 --- a/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_state.dart +++ b/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_state.dart @@ -6,7 +6,8 @@ part 'link_phone_view_state.freezed.dart'; abstract class LinkPhoneViewState with _$LinkPhoneViewState { const factory LinkPhoneViewState({ @Default('') String phoneNumber, - String? errorMessage, + @Default('+34') String dialCode, + @Default('') String errorMessage, @Default(false) bool isLoading, @Default(false) bool codeRequested, }) = _LinkPhoneViewState; diff --git a/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_state.freezed.dart b/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_state.freezed.dart index 1159a82f..393e0bc2 100644 --- a/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_state.freezed.dart +++ b/modules/auth/lib/src/features/link_phone/presentation/link_phone_view_state.freezed.dart @@ -14,7 +14,7 @@ T _$identity(T value) => value; /// @nodoc mixin _$LinkPhoneViewState { - String get phoneNumber; String? get errorMessage; bool get isLoading; bool get codeRequested; + String get phoneNumber; String get dialCode; String get errorMessage; bool get isLoading; bool get codeRequested; /// Create a copy of LinkPhoneViewState /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -25,16 +25,16 @@ $LinkPhoneViewStateCopyWith get copyWith => _$LinkPhoneViewS @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is LinkPhoneViewState&&(identical(other.phoneNumber, phoneNumber) || other.phoneNumber == phoneNumber)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.codeRequested, codeRequested) || other.codeRequested == codeRequested)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is LinkPhoneViewState&&(identical(other.phoneNumber, phoneNumber) || other.phoneNumber == phoneNumber)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.codeRequested, codeRequested) || other.codeRequested == codeRequested)); } @override -int get hashCode => Object.hash(runtimeType,phoneNumber,errorMessage,isLoading,codeRequested); +int get hashCode => Object.hash(runtimeType,phoneNumber,dialCode,errorMessage,isLoading,codeRequested); @override String toString() { - return 'LinkPhoneViewState(phoneNumber: $phoneNumber, errorMessage: $errorMessage, isLoading: $isLoading, codeRequested: $codeRequested)'; + return 'LinkPhoneViewState(phoneNumber: $phoneNumber, dialCode: $dialCode, errorMessage: $errorMessage, isLoading: $isLoading, codeRequested: $codeRequested)'; } @@ -45,7 +45,7 @@ abstract mixin class $LinkPhoneViewStateCopyWith<$Res> { factory $LinkPhoneViewStateCopyWith(LinkPhoneViewState value, $Res Function(LinkPhoneViewState) _then) = _$LinkPhoneViewStateCopyWithImpl; @useResult $Res call({ - String phoneNumber, String? errorMessage, bool isLoading, bool codeRequested + String phoneNumber, String dialCode, String errorMessage, bool isLoading, bool codeRequested }); @@ -62,11 +62,12 @@ class _$LinkPhoneViewStateCopyWithImpl<$Res> /// Create a copy of LinkPhoneViewState /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? phoneNumber = null,Object? errorMessage = freezed,Object? isLoading = null,Object? codeRequested = null,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? phoneNumber = null,Object? dialCode = null,Object? errorMessage = null,Object? isLoading = null,Object? codeRequested = null,}) { return _then(_self.copyWith( phoneNumber: null == phoneNumber ? _self.phoneNumber : phoneNumber // ignore: cast_nullable_to_non_nullable -as String,errorMessage: freezed == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable -as String?,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable +as String,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable +as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable +as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable as bool,codeRequested: null == codeRequested ? _self.codeRequested : codeRequested // ignore: cast_nullable_to_non_nullable as bool, )); @@ -153,10 +154,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( String phoneNumber, String? errorMessage, bool isLoading, bool codeRequested)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( String phoneNumber, String dialCode, String errorMessage, bool isLoading, bool codeRequested)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _LinkPhoneViewState() when $default != null: -return $default(_that.phoneNumber,_that.errorMessage,_that.isLoading,_that.codeRequested);case _: +return $default(_that.phoneNumber,_that.dialCode,_that.errorMessage,_that.isLoading,_that.codeRequested);case _: return orElse(); } @@ -174,10 +175,10 @@ return $default(_that.phoneNumber,_that.errorMessage,_that.isLoading,_that.codeR /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( String phoneNumber, String? errorMessage, bool isLoading, bool codeRequested) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( String phoneNumber, String dialCode, String errorMessage, bool isLoading, bool codeRequested) $default,) {final _that = this; switch (_that) { case _LinkPhoneViewState(): -return $default(_that.phoneNumber,_that.errorMessage,_that.isLoading,_that.codeRequested);case _: +return $default(_that.phoneNumber,_that.dialCode,_that.errorMessage,_that.isLoading,_that.codeRequested);case _: throw StateError('Unexpected subclass'); } @@ -194,10 +195,10 @@ return $default(_that.phoneNumber,_that.errorMessage,_that.isLoading,_that.codeR /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( String phoneNumber, String? errorMessage, bool isLoading, bool codeRequested)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String phoneNumber, String dialCode, String errorMessage, bool isLoading, bool codeRequested)? $default,) {final _that = this; switch (_that) { case _LinkPhoneViewState() when $default != null: -return $default(_that.phoneNumber,_that.errorMessage,_that.isLoading,_that.codeRequested);case _: +return $default(_that.phoneNumber,_that.dialCode,_that.errorMessage,_that.isLoading,_that.codeRequested);case _: return null; } @@ -209,11 +210,12 @@ return $default(_that.phoneNumber,_that.errorMessage,_that.isLoading,_that.codeR class _LinkPhoneViewState implements LinkPhoneViewState { - const _LinkPhoneViewState({this.phoneNumber = '', this.errorMessage, this.isLoading = false, this.codeRequested = false}); + const _LinkPhoneViewState({this.phoneNumber = '', this.dialCode = '+34', this.errorMessage = '', this.isLoading = false, this.codeRequested = false}); @override@JsonKey() final String phoneNumber; -@override final String? errorMessage; +@override@JsonKey() final String dialCode; +@override@JsonKey() final String errorMessage; @override@JsonKey() final bool isLoading; @override@JsonKey() final bool codeRequested; @@ -227,16 +229,16 @@ _$LinkPhoneViewStateCopyWith<_LinkPhoneViewState> get copyWith => __$LinkPhoneVi @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _LinkPhoneViewState&&(identical(other.phoneNumber, phoneNumber) || other.phoneNumber == phoneNumber)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.codeRequested, codeRequested) || other.codeRequested == codeRequested)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _LinkPhoneViewState&&(identical(other.phoneNumber, phoneNumber) || other.phoneNumber == phoneNumber)&&(identical(other.dialCode, dialCode) || other.dialCode == dialCode)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage)&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.codeRequested, codeRequested) || other.codeRequested == codeRequested)); } @override -int get hashCode => Object.hash(runtimeType,phoneNumber,errorMessage,isLoading,codeRequested); +int get hashCode => Object.hash(runtimeType,phoneNumber,dialCode,errorMessage,isLoading,codeRequested); @override String toString() { - return 'LinkPhoneViewState(phoneNumber: $phoneNumber, errorMessage: $errorMessage, isLoading: $isLoading, codeRequested: $codeRequested)'; + return 'LinkPhoneViewState(phoneNumber: $phoneNumber, dialCode: $dialCode, errorMessage: $errorMessage, isLoading: $isLoading, codeRequested: $codeRequested)'; } @@ -247,7 +249,7 @@ abstract mixin class _$LinkPhoneViewStateCopyWith<$Res> implements $LinkPhoneVie factory _$LinkPhoneViewStateCopyWith(_LinkPhoneViewState value, $Res Function(_LinkPhoneViewState) _then) = __$LinkPhoneViewStateCopyWithImpl; @override @useResult $Res call({ - String phoneNumber, String? errorMessage, bool isLoading, bool codeRequested + String phoneNumber, String dialCode, String errorMessage, bool isLoading, bool codeRequested }); @@ -264,11 +266,12 @@ class __$LinkPhoneViewStateCopyWithImpl<$Res> /// Create a copy of LinkPhoneViewState /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? phoneNumber = null,Object? errorMessage = freezed,Object? isLoading = null,Object? codeRequested = null,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? phoneNumber = null,Object? dialCode = null,Object? errorMessage = null,Object? isLoading = null,Object? codeRequested = null,}) { return _then(_LinkPhoneViewState( phoneNumber: null == phoneNumber ? _self.phoneNumber : phoneNumber // ignore: cast_nullable_to_non_nullable -as String,errorMessage: freezed == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable -as String?,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable +as String,dialCode: null == dialCode ? _self.dialCode : dialCode // ignore: cast_nullable_to_non_nullable +as String,errorMessage: null == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable +as String,isLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable as bool,codeRequested: null == codeRequested ? _self.codeRequested : codeRequested // ignore: cast_nullable_to_non_nullable as bool, )); diff --git a/modules/auth/pubspec.yaml b/modules/auth/pubspec.yaml index 70be6cdf..d8ff6537 100644 --- a/modules/auth/pubspec.yaml +++ b/modules/auth/pubspec.yaml @@ -32,6 +32,7 @@ dependencies: freezed_annotation: ^3.1.0 freezed: ^3.2.3 dio: ^5.9.0 + country_code_picker: ^3.4.1 dev_dependencies: flutter_test: diff --git a/packages/design_system/lib/design_system.dart b/packages/design_system/lib/design_system.dart index d58486ed..8e4c970d 100644 --- a/packages/design_system/lib/design_system.dart +++ b/packages/design_system/lib/design_system.dart @@ -9,4 +9,5 @@ export 'src/snackbars/snackbar.dart'; export 'src/buttons/primary_button.dart'; export 'src/buttons/secondary_button.dart'; export 'src/buttons/custom_text_button.dart'; -export 'src/dropdowns/dropdown.dart'; \ No newline at end of file +export 'src/dropdowns/dropdown.dart'; +export 'src/dropdowns/country_prefix_picker.dart'; diff --git a/packages/design_system/lib/src/dropdowns/country_prefix_picker.dart b/packages/design_system/lib/src/dropdowns/country_prefix_picker.dart new file mode 100644 index 00000000..eaa2b0a3 --- /dev/null +++ b/packages/design_system/lib/src/dropdowns/country_prefix_picker.dart @@ -0,0 +1,79 @@ +import 'package:country_code_picker/country_code_picker.dart'; +import 'package:flutter/material.dart'; + +class CountryPrefixPicker extends StatelessWidget { + const CountryPrefixPicker({ + super.key, + required this.onChanged, + this.initialCountryCode = '+34', + this.radius = 12, + this.width = 90, + this.height = 55, + this.borderColor = const Color(0xFF4B4B4B), + this.backgroundColor = Colors.white, + }); + + final ValueChanged onChanged; + final String initialCountryCode; + + final double radius; + final double width; + final double height; + final Color borderColor; + final Color backgroundColor; + + @override + Widget build(BuildContext context) { + return SizedBox( + width: width, + height: height, + child: CountryCodePicker( + onChanged: onChanged, + initialSelection: initialCountryCode, + showFlag: false, + showDropDownButton: false, + hideMainText: true, + padding: EdgeInsets.zero, + builder: (CountryCode? country) { + if (country == null) { + return const SizedBox.shrink(); + } + + return InputDecorator( + decoration: InputDecoration( + isDense: false, + contentPadding: const EdgeInsets.symmetric( + horizontal: 12, + vertical: 16, + ), + filled: true, + fillColor: backgroundColor, + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(radius)), + borderSide: BorderSide(color: borderColor), + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(radius)), + borderSide: BorderSide(color: borderColor), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + if (country.flagUri != null) + Image.asset( + country.flagUri!, + package: 'country_code_picker', + width: 24, + height: 24, + fit: BoxFit.cover, + ), + const Icon(Icons.arrow_drop_down, size: 24), + ], + ), + ); + }, + ), + ); + } +} diff --git a/packages/design_system/lib/src/inputs/textfields.dart b/packages/design_system/lib/src/inputs/textfields.dart index 40e36508..6c3505c4 100644 --- a/packages/design_system/lib/src/inputs/textfields.dart +++ b/packages/design_system/lib/src/inputs/textfields.dart @@ -1,18 +1,17 @@ -import 'package:design_system/design_system.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_riverpod/flutter_riverpod.dart'; -class CustomTextField extends StatefulWidget{ - bool? showPassword; +class CustomTextField extends StatefulWidget { + final bool? showPassword; final bool numeric; final String hint; final String label; final int? lines; final ValueChanged? onChanged; final int? length; + final TextEditingController? controller; - CustomTextField({ + const CustomTextField({ super.key, this.showPassword, this.numeric = false, @@ -21,63 +20,74 @@ class CustomTextField extends StatefulWidget{ this.lines, this.length, this.onChanged, + this.controller, }); @override State createState() => CustomTextFieldState(); } -class CustomTextFieldState extends State{ +class CustomTextFieldState extends State { + late bool _showPassword; + + @override + void initState() { + super.initState(); + _showPassword = widget.showPassword ?? true; + } @override Widget build(BuildContext context) { - return Column( spacing: 8, children: [ - ?widget.label == '' ? null : Align( - alignment: Alignment.bottomLeft, - child: Text( - widget.label, - style: TextStyle(fontSize: 14, letterSpacing: 0), - ) - ), + if (widget.label.isNotEmpty) + Align( + alignment: Alignment.bottomLeft, + child: Text( + widget.label, + style: const TextStyle(fontSize: 14, letterSpacing: 0), + ), + ), TextFormField( - keyboardType: widget.numeric? TextInputType.number : TextInputType.text, - obscureText: !(widget.showPassword ?? true), - enableSuggestions: widget.showPassword ?? true, - autocorrect: !(widget.showPassword ?? false), - style: TextStyle(color: Color(0xFF4B4B4B)), - inputFormatters: widget.numeric? [ - FilteringTextInputFormatter.digitsOnly - ] : [], + controller: widget.controller, + keyboardType: widget.numeric + ? TextInputType.number + : TextInputType.text, + obscureText: !_showPassword, + enableSuggestions: _showPassword, + autocorrect: !_showPassword, + style: const TextStyle(color: Color(0xFF4B4B4B)), + inputFormatters: widget.numeric + ? [FilteringTextInputFormatter.digitsOnly] + : const [], decoration: InputDecoration( counterText: "", hintText: widget.hint, - //labelText: widget.label, - //floatingLabelBehavior: FloatingLabelBehavior.always, - border: OutlineInputBorder( + border: const OutlineInputBorder( borderRadius: BorderRadius.all(Radius.circular(12)), borderSide: BorderSide(color: Color(0xFF4B4B4B)), - gapPadding: 16 + gapPadding: 16, ), - suffixIcon: widget.showPassword!=null ? IconButton( - icon: Icon(widget.showPassword! - ? Icons.visibility_off - : Icons.visibility), - onPressed: () { - setState(() { - widget.showPassword = !widget.showPassword!; - }); - }, - ) : null, + suffixIcon: widget.showPassword != null + ? IconButton( + icon: Icon( + _showPassword ? Icons.visibility_off : Icons.visibility, + ), + onPressed: () { + setState(() { + _showPassword = !_showPassword; + }); + }, + ) + : null, ), minLines: widget.lines ?? 1, maxLines: widget.lines ?? 1, maxLength: widget.length, - onChanged: widget.onChanged ?? (_)=>{}, - ) + onChanged: widget.onChanged, + ), ], ); } -} \ No newline at end of file +} diff --git a/packages/design_system/pubspec.yaml b/packages/design_system/pubspec.yaml index 5fc2d692..d6a0b4d4 100644 --- a/packages/design_system/pubspec.yaml +++ b/packages/design_system/pubspec.yaml @@ -16,6 +16,7 @@ dependencies: path: ../utils flutter_riverpod: ^3.0.3 get_it: ^9.0.5 + country_code_picker: ^3.4.1 fonts: path: ../../packages/fonts