Convert LoginTextInputBuilder functional to StatefulWidget

(cherry picked from commit b7c41f18d6e57e17e6e9f46e2b18905fca1ea85a)
This commit is contained in:
dab246
2023-08-30 09:19:00 +07:00
committed by Dat Vu
parent bac47c7a5e
commit eb997f55b7
4 changed files with 177 additions and 212 deletions
@@ -115,19 +115,22 @@ abstract class BaseLoginView extends GetWidget<LoginController> {
Widget buildInputCredentialForm(BuildContext context) { Widget buildInputCredentialForm(BuildContext context) {
return AutofillGroup( return AutofillGroup(
child: Padding(
padding: const EdgeInsetsDirectional.symmetric(horizontal: 24),
child: Column( child: Column(
children: [ children: [
buildUserNameInput(context), buildUserNameInput(context),
buildPasswordInput(context) const SizedBox(height: 24),
buildPasswordInput(context),
const SizedBox(height: 40),
], ],
), ),
),
); );
} }
Widget buildUserNameInput(BuildContext context) { Widget buildUserNameInput(BuildContext context) {
return Padding( return TypeAheadFormFieldBuilder<RecentLoginUsername>(
padding: const EdgeInsets.only(bottom: 24, right: 24, left: 24),
child: TypeAheadFormFieldBuilder<RecentLoginUsername>(
key: const Key('login_username_input'), key: const Key('login_username_input'),
controller: controller.usernameInputController, controller: controller.usernameInputController,
onTextChange: controller.setUserNameText, onTextChange: controller.setUserNameText,
@@ -151,29 +154,20 @@ abstract class BaseLoginView extends GetWidget<LoginController> {
hideOnEmpty: true, hideOnEmpty: true,
hideOnError: true, hideOnError: true,
hideOnLoading: true, hideOnLoading: true,
)
); );
} }
Widget buildPasswordInput(BuildContext context) { Widget buildPasswordInput(BuildContext context) {
return Padding( return LoginTextInputBuilder(
padding: const EdgeInsets.only(bottom: 40, right: 24, left: 24), key: const Key('login_password_input'),
child: Container( controller: controller.passwordInputController,
child: (LoginTextInputBuilder( autofillHints: const [AutofillHints.password],
context, textInputAction: TextInputAction.done,
imagePaths, hintText: AppLocalizations.of(context).password,
autocorrect: false, focusNode: controller.passFocusNode,
autofillHints: [AutofillHints.password] onTextChange: controller.setPasswordText,
) onSubmitted: (value) => controller.handleLoginPressed(),
..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()));
} }
Widget buildLoadingCircularProgress() { Widget buildLoadingCircularProgress() {
@@ -73,6 +73,7 @@ class LoginController extends ReloadableController {
final TextEditingController urlInputController = TextEditingController(); final TextEditingController urlInputController = TextEditingController();
final TextEditingController usernameInputController = TextEditingController(); final TextEditingController usernameInputController = TextEditingController();
final TextEditingController passwordInputController = TextEditingController();
final FocusNode passFocusNode = FocusNode(); final FocusNode passFocusNode = FocusNode();
LoginController( LoginController(
@@ -393,6 +394,7 @@ class LoginController extends ReloadableController {
passFocusNode.dispose(); passFocusNode.dispose();
urlInputController.dispose(); urlInputController.dispose();
usernameInputController.dispose(); usernameInputController.dispose();
passwordInputController.dispose();
super.onClose(); super.onClose();
} }
} }
@@ -22,7 +22,7 @@ class LoginInputDecorationBuilder extends InputDecorationBuilder {
labelStyle: labelStyle ?? const TextStyle(color: AppColor.textFieldLabelColor, fontSize: 16), labelStyle: labelStyle ?? const TextStyle(color: AppColor.textFieldLabelColor, fontSize: 16),
hintText: hintText, hintText: hintText,
hintStyle: hintStyle ?? const TextStyle(color: AppColor.textFieldHintColor, fontSize: 16), 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, filled: true,
fillColor: AppColor.textFieldBorderColor); fillColor: AppColor.textFieldBorderColor);
} }
@@ -1,176 +1,145 @@
import 'dart:async';
import 'package:core/presentation/extensions/color_extension.dart'; import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/resources/image_paths.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:core/presentation/views/text/text_form_field_builder.dart';
import 'package:flutter/material.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); typedef OnSubmitted = void Function(String);
class LoginTextInputBuilder { class LoginTextInputBuilder extends StatefulWidget {
Key? _key; final String? hintText;
String? _title; final String? prefixText;
String? _hintText; final TextInputAction? textInputAction;
String? _prefixText; final TextEditingController? controller;
SetErrorString? _setErrorString; final FocusNode? focusNode;
String? _errorText;
Timer? _debounce;
bool? _obscureText;
ValueChanged<String>? _onTextChange;
TextInputAction? _textInputAction;
TextEditingController? _textEditingController;
bool? _passwordInput;
OnSubmitted? _onSubmitted;
FocusNode? _focusNode;
final List<String>? autofillHints; final List<String>? autofillHints;
final bool autocorrect; final bool obscureText;
final BuildContext context; final bool passwordInput;
final ImagePaths imagePaths; final ValueChanged<String>? onTextChange;
final OnSubmitted? onSubmitted;
LoginTextInputBuilder( const LoginTextInputBuilder({
this.context, super.key,
this.imagePaths, this.hintText,
{ this.prefixText,
this.autocorrect = true, this.textInputAction,
this.autofillHints this.focusNode,
} this.controller,
); this.autofillHints,
this.onSubmitted,
void key(Key key) { this.onTextChange,
_key = key; this.passwordInput = true,
} this.obscureText = true,
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<String> 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) : '';
});
}); });
@override
State<LoginTextInputBuilder> createState() => _LoginTextInputBuilderState();
}
class _LoginTextInputBuilderState extends State<LoginTextInputBuilder> {
final imagePaths = Get.find<ImagePaths>();
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 errorText(String? errorText) { @override
_errorText = errorText; Widget build(BuildContext context) {
} return Stack(
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: <Widget>[
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, alignment: AlignmentDirectional.centerEnd,
children: [ children: [
TextFormFieldBuilder( TextFormFieldBuilder(
onTextSubmitted: _onSubmitted, onTextSubmitted: widget.onSubmitted,
onTextChange: (value) => _onTextChanged(value, setState), onTextChange: widget.onTextChange,
obscureText: _obscureText ?? false, obscureText: _obscureText,
textInputAction: _textInputAction, textInputAction: widget.textInputAction,
autofillHints: autofillHints, autofillHints: widget.autofillHints,
autocorrect: autocorrect, controller: _controller,
controller: _textEditingController, textStyle: const TextStyle(
textStyle: const TextStyle(color: AppColor.loginTextFieldHintColor, fontSize: 16, fontWeight: FontWeight.normal), color: AppColor.loginTextFieldHintColor,
focusNode: _focusNode, fontSize: 16,
fontWeight: FontWeight.normal
),
focusNode: widget.focusNode,
decoration: (LoginInputDecorationBuilder() decoration: (LoginInputDecorationBuilder()
..setHintText(_hintText) ..setHintText(widget.hintText)
..setPrefixText(_prefixText) ..setPrefixText(widget.prefixText)
..setErrorText(_errorText) ..setContentPadding(const EdgeInsetsDirectional.only(
..setContentPadding(const EdgeInsetsDirectional.only(start: 25, top: 15, bottom: 15, end: 40)) start: 25,
..setHintStyle(const TextStyle(color: AppColor.loginTextFieldHintColor, fontSize: 16, fontWeight: FontWeight.normal)) top: 15,
..setPrefixStyle(const TextStyle(color: AppColor.loginTextFieldHintColor, fontSize: 16, fontWeight: FontWeight.normal)) bottom: 15,
..setErrorTextStyle(const TextStyle(color: AppColor.loginTextFieldErrorBorder, fontSize: 13, fontWeight: FontWeight.normal)) 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( ..setFocusBorder(const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)), borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(width: 1, color: AppColor.loginTextFieldFocusedBorder))) borderSide: BorderSide(
width: 1,
color: AppColor.loginTextFieldFocusedBorder
)
))
..setEnabledBorder(const OutlineInputBorder( ..setEnabledBorder(const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)), borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(width: 1, color: AppColor.loginTextFieldBorderColor))) borderSide: BorderSide(
width: 1,
color: AppColor.loginTextFieldBorderColor
)
))
..setErrorBorder(const OutlineInputBorder( ..setErrorBorder(const OutlineInputBorder(
borderRadius: BorderRadius.all(Radius.circular(10)), borderRadius: BorderRadius.all(Radius.circular(10)),
borderSide: BorderSide(width: 1, color: AppColor.loginTextFieldErrorBorder)))) borderSide: BorderSide(
.build(), width: 1,
), color: AppColor.loginTextFieldErrorBorder
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)))
]
) )
))
).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();
} }
} }