From eb997f55b72754374bad06a46bed15592b079126 Mon Sep 17 00:00:00 2001 From: dab246 Date: Wed, 30 Aug 2023 09:19:00 +0700 Subject: [PATCH] Convert LoginTextInputBuilder functional to StatefulWidget (cherry picked from commit b7c41f18d6e57e17e6e9f46e2b18905fca1ea85a) --- .../login/presentation/base_login_view.dart | 94 +++--- .../login/presentation/login_controller.dart | 2 + .../login_input_decoration_builder.dart | 2 +- .../widgets/login_text_input_builder.dart | 291 ++++++++---------- 4 files changed, 177 insertions(+), 212 deletions(-) diff --git a/lib/features/login/presentation/base_login_view.dart b/lib/features/login/presentation/base_login_view.dart index e34feda7e..86ea753a7 100644 --- a/lib/features/login/presentation/base_login_view.dart +++ b/lib/features/login/presentation/base_login_view.dart @@ -115,65 +115,59 @@ abstract class BaseLoginView extends GetWidget { Widget buildInputCredentialForm(BuildContext context) { return AutofillGroup( - child: Column( - children: [ - buildUserNameInput(context), - buildPasswordInput(context) - ], + child: Padding( + padding: const EdgeInsetsDirectional.symmetric(horizontal: 24), + child: Column( + children: [ + buildUserNameInput(context), + const SizedBox(height: 24), + buildPasswordInput(context), + const SizedBox(height: 40), + ], + ), ), ); } Widget buildUserNameInput(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(bottom: 24, right: 24, left: 24), - child: TypeAheadFormFieldBuilder( - key: const Key('login_username_input'), - controller: controller.usernameInputController, - onTextChange: controller.setUserNameText, - textInputAction: TextInputAction.next, - autocorrect: false, - autofillHints: const [AutofillHints.email], - keyboardType: TextInputType.emailAddress, - decoration: (LoginInputDecorationBuilder() - ..setLabelText(AppLocalizations.of(context).email) - ..setHintText(AppLocalizations.of(context).email)) - .build(), - debounceDuration: const Duration(milliseconds: 300), - suggestionsCallback: controller.getAllRecentLoginUsernameAction, - itemBuilder: (context, loginUsername) => RecentItemTileWidget(loginUsername, imagePath: imagePaths), - onSuggestionSelected: (recentUsername) { - controller.setUsername(recentUsername.username); - controller.passFocusNode.requestFocus(); - }, - suggestionsBoxDecoration: const SuggestionsBoxDecoration(borderRadius: BorderRadius.all(Radius.circular(14))), - noItemsFoundBuilder: (context) => const SizedBox(), - hideOnEmpty: true, - hideOnError: true, - hideOnLoading: true, - ) + return TypeAheadFormFieldBuilder( + key: const Key('login_username_input'), + controller: controller.usernameInputController, + onTextChange: controller.setUserNameText, + textInputAction: TextInputAction.next, + autocorrect: false, + autofillHints: const [AutofillHints.email], + keyboardType: TextInputType.emailAddress, + decoration: (LoginInputDecorationBuilder() + ..setLabelText(AppLocalizations.of(context).email) + ..setHintText(AppLocalizations.of(context).email)) + .build(), + debounceDuration: const Duration(milliseconds: 300), + suggestionsCallback: controller.getAllRecentLoginUsernameAction, + itemBuilder: (context, loginUsername) => RecentItemTileWidget(loginUsername, imagePath: imagePaths), + onSuggestionSelected: (recentUsername) { + controller.setUsername(recentUsername.username); + controller.passFocusNode.requestFocus(); + }, + suggestionsBoxDecoration: const SuggestionsBoxDecoration(borderRadius: BorderRadius.all(Radius.circular(14))), + noItemsFoundBuilder: (context) => const SizedBox(), + hideOnEmpty: true, + hideOnError: true, + hideOnLoading: true, ); } Widget buildPasswordInput(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(bottom: 40, right: 24, left: 24), - child: Container( - child: (LoginTextInputBuilder( - context, - imagePaths, - autocorrect: false, - autofillHints: [AutofillHints.password] - ) - ..setOnSubmitted((value) => controller.handleLoginPressed()) - ..passwordInput(true) - ..key(const Key('login_password_input')) - ..obscureText(true) - ..onChange(controller.setPasswordText) - ..textInputAction(TextInputAction.done) - ..hintText(AppLocalizations.of(context).password) - ..setFocusNode(controller.passFocusNode)) - .build())); + return LoginTextInputBuilder( + key: const Key('login_password_input'), + controller: controller.passwordInputController, + autofillHints: const [AutofillHints.password], + textInputAction: TextInputAction.done, + hintText: AppLocalizations.of(context).password, + focusNode: controller.passFocusNode, + onTextChange: controller.setPasswordText, + onSubmitted: (value) => controller.handleLoginPressed(), + ); } Widget buildLoadingCircularProgress() { diff --git a/lib/features/login/presentation/login_controller.dart b/lib/features/login/presentation/login_controller.dart index 87a401926..469cb48d4 100644 --- a/lib/features/login/presentation/login_controller.dart +++ b/lib/features/login/presentation/login_controller.dart @@ -73,6 +73,7 @@ class LoginController extends ReloadableController { final TextEditingController urlInputController = TextEditingController(); final TextEditingController usernameInputController = TextEditingController(); + final TextEditingController passwordInputController = TextEditingController(); final FocusNode passFocusNode = FocusNode(); LoginController( @@ -393,6 +394,7 @@ class LoginController extends ReloadableController { passFocusNode.dispose(); urlInputController.dispose(); usernameInputController.dispose(); + passwordInputController.dispose(); super.onClose(); } } \ No newline at end of file diff --git a/lib/features/login/presentation/widgets/login_input_decoration_builder.dart b/lib/features/login/presentation/widgets/login_input_decoration_builder.dart index a741669ca..9261ba024 100644 --- a/lib/features/login/presentation/widgets/login_input_decoration_builder.dart +++ b/lib/features/login/presentation/widgets/login_input_decoration_builder.dart @@ -22,7 +22,7 @@ class LoginInputDecorationBuilder extends InputDecorationBuilder { labelStyle: labelStyle ?? const TextStyle(color: AppColor.textFieldLabelColor, fontSize: 16), hintText: hintText, hintStyle: hintStyle ?? const TextStyle(color: AppColor.textFieldHintColor, fontSize: 16), - contentPadding: contentPadding ?? const EdgeInsets.only(left: 25, top: 15, bottom: 15, right: 25), + contentPadding: contentPadding ?? const EdgeInsetsDirectional.only(start: 25, top: 15, bottom: 15, end: 25), filled: true, fillColor: AppColor.textFieldBorderColor); } diff --git a/lib/features/login/presentation/widgets/login_text_input_builder.dart b/lib/features/login/presentation/widgets/login_text_input_builder.dart index b04d7e737..0189870d3 100644 --- a/lib/features/login/presentation/widgets/login_text_input_builder.dart +++ b/lib/features/login/presentation/widgets/login_text_input_builder.dart @@ -1,176 +1,145 @@ -import 'dart:async'; - import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/resources/image_paths.dart'; +import 'package:core/presentation/views/button/tmail_button_widget.dart'; import 'package:core/presentation/views/text/text_form_field_builder.dart'; import 'package:flutter/material.dart'; -import 'package:flutter_svg/flutter_svg.dart'; +import 'package:get/get.dart'; +import 'package:tmail_ui_user/features/login/presentation/widgets/login_input_decoration_builder.dart'; -import 'login_input_decoration_builder.dart'; - -typedef SetErrorString = String? Function(String); typedef OnSubmitted = void Function(String); -class LoginTextInputBuilder { - Key? _key; - String? _title; - String? _hintText; - String? _prefixText; - SetErrorString? _setErrorString; - String? _errorText; - Timer? _debounce; - bool? _obscureText; - ValueChanged? _onTextChange; - TextInputAction? _textInputAction; - TextEditingController? _textEditingController; - bool? _passwordInput; - OnSubmitted? _onSubmitted; - FocusNode? _focusNode; +class LoginTextInputBuilder extends StatefulWidget { + final String? hintText; + final String? prefixText; + final TextInputAction? textInputAction; + final TextEditingController? controller; + final FocusNode? focusNode; final List? autofillHints; - final bool autocorrect; - final BuildContext context; - final ImagePaths imagePaths; + final bool obscureText; + final bool passwordInput; + final ValueChanged? onTextChange; + final OnSubmitted? onSubmitted; - LoginTextInputBuilder( - this.context, - this.imagePaths, - { - this.autocorrect = true, - this.autofillHints + const LoginTextInputBuilder({ + super.key, + this.hintText, + this.prefixText, + this.textInputAction, + this.focusNode, + this.controller, + this.autofillHints, + this.onSubmitted, + this.onTextChange, + this.passwordInput = true, + this.obscureText = true, + }); + + @override + State createState() => _LoginTextInputBuilderState(); +} + +class _LoginTextInputBuilderState extends State { + + final imagePaths = Get.find(); + + late TextEditingController _controller; + late bool _obscureText; + + @override + void initState() { + super.initState(); + _obscureText = widget.obscureText; + if (widget.controller != null) { + _controller = widget.controller!; + } else { + _controller = TextEditingController(); } - ); - - void key(Key key) { - _key = key; } - void title(String? title) { - _title = title; - } - - void hintText(String? hintText) { - _hintText = hintText; - } - - void labelText(String? labelText) { - } - - void prefixText(String? prefixText) { - _prefixText = prefixText; - } - - void textInputAction(TextInputAction inputAction) { - _textInputAction = inputAction; - } - - void obscureText(bool obscureText) { - _obscureText = obscureText; - } - - void onChange(ValueChanged onChange) { - _onTextChange = onChange; - } - - void setErrorString(SetErrorString setErrorString) { - _setErrorString = setErrorString; - } - - void passwordInput(bool? passwordInput) { - _passwordInput = passwordInput; - } - - void setTextEditingController(TextEditingController textEditingController) { - _textEditingController = textEditingController; - } - - void _onTextChanged(String name, StateSetter setState) { - if (_onTextChange != null) { - _onTextChange!(name); - } - if (_debounce?.isActive ?? false) _debounce?.cancel(); - _debounce = Timer(const Duration(milliseconds: 500), () { - setState(() { - _errorText = (_setErrorString != null) ? _setErrorString!(name) : ''; - }); - }); - } - - void errorText(String? errorText) { - _errorText = errorText; - } - - void _onObscureTextChanged(bool? value, StateSetter setState) { - setState(() { - _obscureText = value == true ? false : true; - }); - } - - void setOnSubmitted(OnSubmitted onSubmitted) { - _onSubmitted = onSubmitted; - } - - void setFocusNode(FocusNode? focusNode) { - _focusNode = focusNode; - } - - Widget build() { - return StatefulBuilder(builder: (BuildContext context, StateSetter setState) { - return Wrap( - key: _key, - children: [ - Text(_title ?? '', style: const TextStyle(color: AppColor.loginTextFieldHintColor, fontSize: 14, fontWeight: FontWeight.normal)), - Padding( - padding: const EdgeInsets.only(top: 8), - child: Stack( - alignment: AlignmentDirectional.centerEnd, - children: [ - TextFormFieldBuilder( - onTextSubmitted: _onSubmitted, - onTextChange: (value) => _onTextChanged(value, setState), - obscureText: _obscureText ?? false, - textInputAction: _textInputAction, - autofillHints: autofillHints, - autocorrect: autocorrect, - controller: _textEditingController, - textStyle: const TextStyle(color: AppColor.loginTextFieldHintColor, fontSize: 16, fontWeight: FontWeight.normal), - focusNode: _focusNode, - decoration: (LoginInputDecorationBuilder() - ..setHintText(_hintText) - ..setPrefixText(_prefixText) - ..setErrorText(_errorText) - ..setContentPadding(const EdgeInsetsDirectional.only(start: 25, top: 15, bottom: 15, end: 40)) - ..setHintStyle(const TextStyle(color: AppColor.loginTextFieldHintColor, fontSize: 16, fontWeight: FontWeight.normal)) - ..setPrefixStyle(const TextStyle(color: AppColor.loginTextFieldHintColor, fontSize: 16, fontWeight: FontWeight.normal)) - ..setErrorTextStyle(const TextStyle(color: AppColor.loginTextFieldErrorBorder, fontSize: 13, fontWeight: FontWeight.normal)) - ..setFocusBorder(const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - borderSide: BorderSide(width: 1, color: AppColor.loginTextFieldFocusedBorder))) - ..setEnabledBorder(const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - borderSide: BorderSide(width: 1, color: AppColor.loginTextFieldBorderColor))) - ..setErrorBorder(const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(10)), - borderSide: BorderSide(width: 1, color: AppColor.loginTextFieldErrorBorder)))) - .build(), - ), - if (_passwordInput == true) - Transform( - transform: Matrix4.translationValues( - 0.0, - (_errorText != null && _errorText!.isNotEmpty) ? -10.0 : 0.0, - 0.0), - child: IconButton( - onPressed: () => _onObscureTextChanged(_obscureText, setState), - icon: SvgPicture.asset( - _obscureText == true ? imagePaths.icEye : imagePaths.icEyeOff, - width: 18, - height: 18, - fit: BoxFit.fill))) - ] - ) + @override + Widget build(BuildContext context) { + return Stack( + alignment: AlignmentDirectional.centerEnd, + children: [ + TextFormFieldBuilder( + onTextSubmitted: widget.onSubmitted, + onTextChange: widget.onTextChange, + obscureText: _obscureText, + textInputAction: widget.textInputAction, + autofillHints: widget.autofillHints, + controller: _controller, + textStyle: const TextStyle( + color: AppColor.loginTextFieldHintColor, + fontSize: 16, + fontWeight: FontWeight.normal ), - ], - ); - }); + focusNode: widget.focusNode, + decoration: (LoginInputDecorationBuilder() + ..setHintText(widget.hintText) + ..setPrefixText(widget.prefixText) + ..setContentPadding(const EdgeInsetsDirectional.only( + start: 25, + top: 15, + bottom: 15, + end: 40 + )) + ..setHintStyle(const TextStyle( + color: AppColor.loginTextFieldHintColor, + fontSize: 16, + fontWeight: FontWeight.normal + )) + ..setPrefixStyle(const TextStyle( + color: AppColor.loginTextFieldHintColor, + fontSize: 16, + fontWeight: FontWeight.normal + )) + ..setErrorTextStyle(const TextStyle( + color: AppColor.loginTextFieldErrorBorder, + fontSize: 13, + fontWeight: FontWeight.normal + )) + ..setFocusBorder(const OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(10)), + borderSide: BorderSide( + width: 1, + color: AppColor.loginTextFieldFocusedBorder + ) + )) + ..setEnabledBorder(const OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(10)), + borderSide: BorderSide( + width: 1, + color: AppColor.loginTextFieldBorderColor + ) + )) + ..setErrorBorder(const OutlineInputBorder( + borderRadius: BorderRadius.all(Radius.circular(10)), + borderSide: BorderSide( + width: 1, + color: AppColor.loginTextFieldErrorBorder + ) + )) + ).build(), + ), + if (widget.passwordInput) + TMailButtonWidget.fromIcon( + icon: widget.obscureText ? imagePaths.icEye : imagePaths.icEyeOff, + iconSize: 18, + margin: const EdgeInsetsDirectional.only(end: 4), + backgroundColor: Colors.transparent, + onTapActionCallback: () { + setState(() => _obscureText = !_obscureText); + }, + ) + ] + ); + } + + @override + void dispose() { + if (widget.controller == null) { + _controller.dispose(); + } + super.dispose(); } }