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,65 +115,59 @@ abstract class BaseLoginView extends GetWidget<LoginController> {
Widget buildInputCredentialForm(BuildContext context) { Widget buildInputCredentialForm(BuildContext context) {
return AutofillGroup( return AutofillGroup(
child: Column( child: Padding(
children: [ padding: const EdgeInsetsDirectional.symmetric(horizontal: 24),
buildUserNameInput(context), child: Column(
buildPasswordInput(context) children: [
], buildUserNameInput(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), key: const Key('login_username_input'),
child: TypeAheadFormFieldBuilder<RecentLoginUsername>( controller: controller.usernameInputController,
key: const Key('login_username_input'), onTextChange: controller.setUserNameText,
controller: controller.usernameInputController, textInputAction: TextInputAction.next,
onTextChange: controller.setUserNameText, autocorrect: false,
textInputAction: TextInputAction.next, autofillHints: const [AutofillHints.email],
autocorrect: false, keyboardType: TextInputType.emailAddress,
autofillHints: const [AutofillHints.email], decoration: (LoginInputDecorationBuilder()
keyboardType: TextInputType.emailAddress, ..setLabelText(AppLocalizations.of(context).email)
decoration: (LoginInputDecorationBuilder() ..setHintText(AppLocalizations.of(context).email))
..setLabelText(AppLocalizations.of(context).email) .build(),
..setHintText(AppLocalizations.of(context).email)) debounceDuration: const Duration(milliseconds: 300),
.build(), suggestionsCallback: controller.getAllRecentLoginUsernameAction,
debounceDuration: const Duration(milliseconds: 300), itemBuilder: (context, loginUsername) => RecentItemTileWidget(loginUsername, imagePath: imagePaths),
suggestionsCallback: controller.getAllRecentLoginUsernameAction, onSuggestionSelected: (recentUsername) {
itemBuilder: (context, loginUsername) => RecentItemTileWidget(loginUsername, imagePath: imagePaths), controller.setUsername(recentUsername.username);
onSuggestionSelected: (recentUsername) { controller.passFocusNode.requestFocus();
controller.setUsername(recentUsername.username); },
controller.passFocusNode.requestFocus(); suggestionsBoxDecoration: const SuggestionsBoxDecoration(borderRadius: BorderRadius.all(Radius.circular(14))),
}, noItemsFoundBuilder: (context) => const SizedBox(),
suggestionsBoxDecoration: const SuggestionsBoxDecoration(borderRadius: BorderRadius.all(Radius.circular(14))), hideOnEmpty: true,
noItemsFoundBuilder: (context) => const SizedBox(), hideOnError: true,
hideOnEmpty: true, hideOnLoading: true,
hideOnError: 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,
this.onTextChange,
this.passwordInput = true,
this.obscureText = true,
});
@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 key(Key key) {
_key = key;
} }
void title(String? title) { @override
_title = title; Widget build(BuildContext context) {
} return Stack(
alignment: AlignmentDirectional.centerEnd,
void hintText(String? hintText) { children: [
_hintText = hintText; TextFormFieldBuilder(
} onTextSubmitted: widget.onSubmitted,
onTextChange: widget.onTextChange,
void labelText(String? labelText) { obscureText: _obscureText,
} textInputAction: widget.textInputAction,
autofillHints: widget.autofillHints,
void prefixText(String? prefixText) { controller: _controller,
_prefixText = prefixText; textStyle: const TextStyle(
} color: AppColor.loginTextFieldHintColor,
fontSize: 16,
void textInputAction(TextInputAction inputAction) { fontWeight: FontWeight.normal
_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) : '';
});
});
}
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: <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,
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)))
]
)
), ),
], 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();
} }
} }