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) {
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<RecentLoginUsername>(
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<RecentLoginUsername>(
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() {
@@ -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();
}
}
@@ -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);
}
@@ -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<String>? _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<String>? autofillHints;
final bool autocorrect;
final BuildContext context;
final ImagePaths imagePaths;
final bool obscureText;
final bool passwordInput;
final ValueChanged<String>? 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<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) {
_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) : '';
});
});
}
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)))
]
)
@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();
}
}