I am trying to manage some gap between elements and update the gap using setState(). Unfortunately, it did not work with a custom widget TextFieldInput as child of GestureDetector.
GestureDetector(
onTap: () {
setState(() {
gap = 16.00;
});
},
child: TextFieldInput(
textEditingController: _passwordController,
hintText: 'Enter your password',
textInputType: TextInputType.text,
isPass: true,
),
),
But it did work with a Text widget.
GestureDetector(
onTap: () {
setState(() {
gap = smallGap;
});
},
child: Container(
child: Padding(
padding: EdgeInsets.all(5),
child: Text('Tap here'),
),
padding: const EdgeInsets.symmetric(
vertical: 8,
),
),
)
Here is the TextFieldInput widget structure:
import 'package:flutter/material.dart';
class TextFieldInput extends StatefulWidget {
final TextEditingController textEditingController;
final bool isPass;
final String hintText;
final TextInputType textInputType;
final VoidCallback onTap;
const TextFieldInput({
super.key,
required this.textEditingController,
this.isPass = false,
required this.hintText,
required this.textInputType,
required this.onTap,
this.child,
});
final Widget? child;
#override
State<TextFieldInput> createState() => _TextFieldInputState();
}
class _TextFieldInputState extends State<TextFieldInput> {
#override
Widget build(BuildContext context) {
final inputBorder = OutlineInputBorder(
borderSide: Divider.createBorderSide(context)
);
return TextField(
controller: widget.textEditingController,
decoration: InputDecoration(
hintText: widget.hintText,
border: inputBorder,
focusedBorder: inputBorder,
enabledBorder: inputBorder,
filled: true,
contentPadding: const EdgeInsets.all(8),
),
keyboardType: widget.textInputType,
obscureText: widget.isPass,
onTap: () {},
);
}
}
But "gap" does not change even on tapping on the TextFieldInput.
Can anyone help with this ?
Try with a below code snippet:
TextField(
onTap: () {
// To Do
},
// TextField Property
);
gesture detector doesn't work with textField try adding ignorePointer
GestureDetector(
child: new IgnorePointer (
child: new TextField(
.......
))),
onTap: () {
//do some thing
},
The TextField has its onTap property, I think this is the cause of your problem. You should you onTap property of TextField or use another suitable widget
I made a custom form and when I want to use the validation. My issue is that the error message is not appearing as I wanted. Here's the screenshot of it.
So I want to change the position of the error message to be below my container. Does anyone have any idea how to do it ?
Here's the code of the form:
class AuthForm extends StatefulWidget {
final bool isPassword;
final IconData prefixIcon;
final String hintText;
late bool isPasswordVisible = isPassword;
final bool isCalendar;
final TextEditingController controller;
final bool isDropDown;
final bool isPhone;
final String? Function(String?)? validator;
AuthForm({Key? key, this.isPassword = false, required this.prefixIcon, required this.hintText,
this.isCalendar = false, required this.controller, this.isDropDown = false, this.isPhone = false, required this.validator}) : super(key: key);
#override
State<AuthForm> createState() => _AuthFormState();
}
class _AuthFormState extends State<AuthForm> {
#override
void initState() {
super.initState();
if (widget.isPhone){
getCountryCode();
}
}
start () async {
await CountryCodes.init();
}
Locale? getCountryCode () {
start();
final Locale? deviceLocale = CountryCodes.getDeviceLocale();
final CountryDetails details = CountryCodes.detailsForLocale();
return deviceLocale;
}
DateTime selectedDate = DateTime(2000,1);
Future<void> _selectDate(BuildContext context) async {
final DateTime? picked = await showDatePicker(
context: context,
initialDate: selectedDate,
firstDate: DateTime(1950, 1),
lastDate: DateTime.now());
if (picked != null && picked != selectedDate) {
setState(() {
selectedDate = picked;
});
}
}
#override
Widget build(BuildContext context) {
return Container(
width: 70.w,
height: 5.h,
decoration: BoxDecoration(
border: Border(
bottom: BorderSide(
width: 0.13.w,
color: Theme.of(context).splashColor,
),
),
),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Icon(
widget.prefixIcon,
color: Theme.of(context).primaryColor,
size: 6.w,
),
widget.isDropDown ? const DropDownBar() :
Expanded(
child: TextFormField(
//autovalidateMode: AutovalidateMode.onUserInteraction,
validator: widget.validator,
keyboardType: widget.isPhone ? TextInputType.phone : TextInputType.text,
inputFormatters: [DialCodeFormatter()],
controller: widget.controller,
textAlign: TextAlign.center,
obscureText: widget.isPasswordVisible,
style: Theme.of(context).textTheme.bodyText2,
decoration: InputDecoration(
hintText : widget.hintText,
hintStyle: Theme.of(context).textTheme.bodyText1,
border: InputBorder.none,
),
onTap: () async {
if (widget.isCalendar){
//Dismiss the keyboard
FocusScope.of(context).requestFocus(FocusNode());
//Call the calendar
await _selectDate(context);
widget.controller.text = DateFormat('dd-MM-yyyy').format(selectedDate);
}
}
),
),
//Show Icon only if the form is for a Password
Visibility(
visible: widget.isPassword,
//Maintain the space where the widget is even if it is hid
maintainAnimation: true,
maintainState: true,
maintainSize: true,
child: InkWell(
highlightColor : Colors.transparent,
splashColor: Colors.transparent,
child: Icon(
widget.isPasswordVisible ? Icons.visibility : Icons.visibility_off,
color: Theme.of(context).primaryColor,
size: 6.w,
),
onTap: () {
setState(() {
widget.isPasswordVisible = !widget.isPasswordVisible;
});
},
),
),
],
),
);
}
}
Thanks for your suggestion,
Chris
You should try below piece of code for TextFormField
TextFormField(
textInputAction: TextInputAction.next,
style: TextStyle(fontSize: 16, height: 1),
inputFormatters: <TextInputFormatter>[FilteringTextInputFormatter.digitsOnly, LengthLimitingTextInputFormatter(10)],
keyboardType: TextInputType.phone,
validator: (val) {
if (val!.isEmpty) {
return 'Please enter your mobile number';
} else if (val.length < 10) {
return 'Mobile number should be 10 digits only';
}
},
controller: bloc.mobileNumberLoginController,
decoration: InputDecoration(
filled: true,
border: OutlineInputBorder(
borderSide: BorderSide(
color: code != null ? HexColor(code!) : Colors.pink,
)),
contentPadding: EdgeInsets.only(top: 5, bottom: 5, left: 10, right: 10),
),
),
I am creating a login form which has username and password field, i want to add validation when user skip any field. I have created this reusable textfield.
class RoundedInputField extends StatelessWidget {
final String hintText;
final ValueChanged<String> onChanged;
final TextEditingController controller;
final FormFieldValidator validate;
const RoundedInputField({Key key, this.hintText,
this.onChanged,
this.controller,this.validate,
})
: super(key: key);
#override
Widget build(BuildContext context) {
return TextFieldContainer(
child: TextFormField(
onChanged: onChanged,
controller: TextEditingController(),
validator: validate,
decoration: InputDecoration(
hintText: hintText,
border: InputBorder.none,
),
),
);
}
}
and calling it like this
RoundedInputField(hintText: "Username",icon: Icons.email,fontsize: 20,
controller: TextEditingController(text: user.username),
onChanged: (value){
user.username=value;
},
validate: (value){
if(value.isEmpty){
return "This field is required";
}
},
),
but validator property is not working properly, here it is.
please help if anyone know how to fix it!
I have been using TextFormField in a reusable way like this , it has served me for all the purposes i needed , i think it works for your case too
class BoxTextField extends StatelessWidget {
final TextEditingController controller;
final FormFieldValidator<String> validator;
final bool obsecure;
final bool readOnly;
final VoidCallback onTap;
final VoidCallback onEditingCompleted;
final TextInputType keyboardType;
final ValueChanged<String> onChanged;
final bool isMulti;
final bool autofocus;
final bool enabled;
final String errorText;
final String label;
final Widget suffix;
final Widget prefix;
BoxTextField(
{Key key,
this.controller,
this.validator,
this.keyboardType = TextInputType.text,
this.obsecure = false,
this.onTap,
this.isMulti = false,
this.readOnly = false,
this.autofocus = false,
this.errorText,
#required this.label,
this.suffix,
this.prefix,
this.enabled = true,
this.onEditingCompleted,
this.onChanged})
: super(key: key);
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.symmetric(vertical: 4),
child: TextFormField(
onChanged: onChanged,
onEditingComplete: onEditingCompleted,
autofocus: autofocus,
minLines: isMulti ? 4 : 1,
maxLines: isMulti ? null : 1,
onTap: onTap,
enabled: enabled,
readOnly: readOnly,
obscureText: obsecure,
keyboardType: keyboardType,
controller: controller,
decoration: InputDecoration(
errorText: errorText,
prefixIcon: prefix,
suffixIcon: suffix,
labelStyle: TextStyle(fontSize: lableFontSize()),
labelText: label,
hintStyle: TextStyle(color: Colors.blueGrey, fontSize: 15),
contentPadding: EdgeInsets.symmetric(vertical: 8, horizontal: 20),
enabledBorder: textFieldfocused(),
border: textFieldfocused(),
focusedBorder: textFieldfocused(),
errorBorder: errorrTextFieldBorder(),
focusedErrorBorder: errorrTextFieldBorder(),
),
validator: validator),
);
}
}
This is the Usage
class LoginScreen extends StatefulWidget {
#override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
TextEditingController _emailPhone = new TextEditingController();
TextEditingController _password = new TextEditingController();
GlobalKey<FormState> _formKey = new GlobalKey<FormState>();
#override
void initState() {
super.initState();
}
String loginError;
bool loggingIn = false;
#override
Widget build(BuildContext context) {
return CustomScrollView(
slivers: [
SliverList(
delegate: SliverChildListDelegate([
Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Form(
key: _formKey,
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
height: 50,
),
BoxTextField(
validator: (str) {
if (str.isEmpty) {
return tr('common.required');
}
return null;
},
controller: _emailPhone,
label: tr('login.username'),
),
SizedBox(
height: 10,
),
BoxTextField(
label: tr('login.password'),
controller: _password,
obsecure: true,
validator: (str) {
if (str.isEmpty) {
return tr('common.required');
}
return null;
},
),
Center(
child: BoxButton(
loading: loggingIn,
lable: tr('login.btn'),
onPressed: () {
},
)),
],
)),
)
]))
],
);
}
}
Replace your code and try this:
RoundedInputField(
hintText: "Username",
icon: Icons.email,
fontsize: 20,
controller: TextEditingController(text: user.username),
onChanged: (value) {
user.username = value;
},
validate: (value) {
if (value.isEmpty) {
return "This field is required";
}
return null;
},
),
I have created a custom controller for TextFormField, but I use it in my widgets it clears the input upon releasing focus from the input. the same setup works fine with the local widget set up with the same but I want to have a common widget to use throughout the application for better code maintainability. How can have the input intact entered into the input field??
Custom Input Widget-
import 'package:HSSE/screens/constant.dart';
import 'package:flutter/material.dart';
class AppInput extends StatelessWidget {
const AppInput(
{Key key,
this.correct = false,
this.hintText,
this.inputLabel,
this.labelFont = 18.0,
this.labelFontWeight,
this.validators,
this.keyboard,
this.length,
this.icon,
this.trailingIcon,
this.readOnly = false,
this.inputController,
this.onTap = null,
this.onInputValueChange,
this.labelText})
: super(key: key);
final bool correct;
final String hintText;
final String labelText;
final String inputLabel;
final double labelFont;
final FontWeight labelFontWeight;
final Function validators;
final Function onInputValueChange;
final TextInputType keyboard;
final int length;
final Icon icon;
final Icon trailingIcon;
final bool readOnly;
final Function onTap;
final TextEditingController inputController;
#override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
border: Border.all(color: Color(0Xffcacaca), width: 0.75),
borderRadius: BorderRadius.circular(10),
),
padding: EdgeInsets.all(10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
labelText,
style: TextStyle(fontSize: labelFont),
),
TextFormField(
readOnly: readOnly,
keyboardType: keyboard,
maxLength: length,
controller: inputController,
cursorColor: apmt_color,
decoration: InputDecoration(
prefixIcon: icon,
suffixIcon: trailingIcon,
errorStyle: TextStyle(
fontSize: 16,
),
border: InputBorder.none,
hintText: hintText,
labelStyle: TextStyle(
color: Colors.black,
),
fillColor: Colors.red,
),
style: TextStyle(color: Colors.black),
onChanged: (value) {
onInputValueChange(value);
},
validator: (value) {
return validators(value);
},
)
],
),
);
}
}
When I use it into my other widget like below -
final TextEditingController emailCtrl = TextEditingController();
AppInput(
inputController: emailCtrl,
keyboard: TextInputType.emailAddress,
hintText: Localizer.of(context).translate('inspectorNameHint'),
labelText: Localizer.of(context).translate('inspectorName'),
validators: (String name) {
if (name.isEmpty)
return Localizer.of(context)
.translate('inspectorNameError');
},
)
the input gets clears when the focus gets removed from the input field it works fine with the same setup on the local widget but I want to make a common widget to optimize the code.
I am trying to create a custom textfield class so that i can easily change how it looks by only editing in one place. Currently i am facing a problem on how to make a toggle password visibility.
This is what my custom textfield class:
class CustomTextFieldOutline extends StatelessWidget {
CustomTextFieldOutline(
{this.label,
this.controller,
this.isValid,
this.invalidMsg});
final String label;
final TextEditingController controller;
final bool isValid;
final String invalidMsg;
#override
Widget build(BuildContext context) {
return TextField(
decoration: InputDecoration(
labelText: label,
errorText: isValid ? null : invalidMsg,
errorStyle: TextStyle(color: constant.colorWhite),
labelStyle: TextStyle(color: constant.colorWhite),
errorBorder: UnderlineInputBorder(
borderSide: BorderSide(color: constant.colorWhite),
),
focusedErrorBorder: UnderlineInputBorder(
borderSide: BorderSide(color: constant.colorWhite),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: constant.colorWhite),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: constant.colorWhite),
)),
style: TextStyle(color: constant.colorWhite),
controller: controller,
);
}
}
This is how i call it:
final _resetPasswordView = Container(
child: AnimatedOpacity(
opacity: _resetPasswordVisibility ? 1.0 : 0.0,
duration: Duration(milliseconds: 300),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
CustomTextFieldOutline(
label: constant.email,
controller: _emailSignInController,
isValid: _isEmailValid,
invalidMsg: _emailValidMsg,
)
],
),
),
)
This how my password textfield looks like. I am not using custome textfield class because i dont know how can i implement toggle password visibility in class:
TextField(
decoration: InputDecoration(
labelText: constant.password,
suffixIcon: GestureDetector(
onTap: () {
_togglePasswordVisibility();
},
child: Icon(
_isHidePassword ? Icons.visibility_off : Icons.visibility,
color: constant.colorWhite,
),
),
errorText: _isPasswordValid ? null : _passwordValidMsg,
errorStyle: TextStyle(color: constant.colorWhite),
labelStyle: TextStyle(color: constant.colorWhite),
errorBorder: UnderlineInputBorder(
borderSide: BorderSide(color: constant.colorWhite),
),
focusedErrorBorder: UnderlineInputBorder(
borderSide: BorderSide(color: constant.colorWhite),
),
enabledBorder: UnderlineInputBorder(
borderSide: BorderSide(color: constant.colorWhite),
),
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: constant.colorWhite),
)),
style: TextStyle(color: constant.colorWhite),
obscureText: _isHidePassword,
controller: _passwordSignUpController,
)
How can i incorporate the password textfield functionality in the custom class so that i can use it for password or email textfield?
Here is a sample from instaflutter
class CustomTextField extends StatefulWidget {
final String hint;
final TextEditingController controller;
final Color baseColor;
final Color borderColor;
final Color errorColor;
final TextInputType inputType;
final bool obscureText;
final Function validator;
final Function onChanged;
CustomTextField(
{this.hint,
this.controller,
this.onChanged,
this.baseColor,
this.borderColor,
this.errorColor,
this.inputType = TextInputType.text,
this.obscureText = false,
this.validator});
_CustomTextFieldState createState() => _CustomTextFieldState();
}
class _CustomTextFieldState extends State<CustomTextField> {
Color currentColor;
#override
void initState() {
super.initState();
currentColor = widget.borderColor;
}
#override
Widget build(BuildContext context) {
return Card(
elevation: 0.0,
color: Colors.white,
shape: RoundedRectangleBorder(
side: BorderSide(color: currentColor, width: 2.0),
borderRadius: BorderRadius.circular(20.0),
),
child: Padding(
padding: EdgeInsets.symmetric(horizontal: 10.0),
child: TextField(
obscureText: widget.obscureText,
onChanged: (text) {
if (widget.onChanged != null) {
widget.onChanged(text);
}
setState(() {
if (!widget.validator(text) || text.length == 0) {
currentColor = widget.errorColor;
} else {
currentColor = widget.baseColor;
}
});
},
//keyboardType: widget.inputType,
controller: widget.controller,
decoration: InputDecoration(
hintStyle: TextStyle(
color: widget.baseColor,
fontFamily: "OpenSans",
fontWeight: FontWeight.w300,
),
border: InputBorder.none,
hintText: widget.hint,
),
),
),
);
}
}
and then you can create your widgets
_emailField = new CustomTextField(
baseColor: Colors.grey,
borderColor: Colors.grey[400],
errorColor: Colors.red,
controller: _email,
hint: "E-mail Adress",
inputType: TextInputType.emailAddress,
validator: Validator.validateEmail,
);
_passwordField = CustomTextField(
baseColor: Colors.grey,
borderColor: Colors.grey[400],
errorColor: Colors.red,
controller: _password,
obscureText: true,
hint: "Password",
validator: Validator.validatePassword,
);
i do like this
Widget customTextField(TextInputType textInputType) {
RegExp pattern = RegExp(
r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+#[a-zA-Z0-9]+\.[a-zA-Z]+");
TextEditingController _textEditingController = TextEditingController();
if (textInputType == TextInputType.visiblePassword) {
return TextField(
controller: _textEditingController,
decoration: InputDecoration(hintText: 'Password'),
keyboardType: TextInputType.visiblePassword,
obscureText: true,
);
} else {
return TextField(
controller: _textEditingController,
decoration: InputDecoration(hintText: 'Email'),
keyboardType: TextInputType.emailAddress,
inputFormatters: [BlacklistingTextInputFormatter(pattern)],
);
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: ListView(
shrinkWrap: true,
children: <Widget>[
customTextField(TextInputType.visiblePassword),
customTextField(TextInputType.emailAddress),
],
),
),
);
}
idk how to make RegExp for email, but the results will be like this:
class CustomTextFieldOutline extends StatefulWidget {
const CustomTextFieldOutline(
{Key key, this.hintText, this.label, this.onChange})
: super(key: key);
final String hintText;
final String label;
final void Function(String value) onChange;
#override
_CustomTextFieldOutlineState createState() => _CustomTextFieldOutlineState();
}
class _CustomTextFieldOutlineState extends State<CustomTextFieldOutline> {
bool showPassword = false;
TextEditingController controller = TextEditingController();
void _onChange(String value) {
controller.text = value;
if (widget.onChange != null) {
widget.onChange(value);
}
}
void _changePwdType() {
setState(() {
showPassword = !showPassword;
});
}
#override
Widget build(BuildContext context) {
return TextField(
obscureText: showPassword,
controller: controller,
onChanged: (value) => _onChange,
decoration: InputDecoration(
suffixIcon: IconButton(
icon: Icon(showPassword ? Icons.visibility : Icons.visibility_off),
onPressed: _changePwdType,
),
hintText: widget.hintText != null ? widget.hintText : null,
labelText: widget.label != null ? widget.label : null,
),
);
}
}
You can try implement OutlineTextFormField Widget for Custom TextField of my company. I suppose it's pretty easy to extend, and it's include many utilities inside.
**
First, create outline_text_form_field.dart:
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
const int _defaultMinLinesOnMultiline = 6;
class OutlineTextFormField extends FormField<String> {
/// Creates a [FormField] that contains a [TextField].
///
/// When a [controller] is specified, [initialValue] must be null (the
/// default). If [controller] is null, then a [TextEditingController]
/// will be constructed automatically and its `text` will be initialized
/// to [initialValue] or the empty string.
///
/// For documentation about the various parameters, see the [TextField] class
/// and [new TextField], the constructor.
OutlineTextFormField({
Key? key,
required this.data,
bool updateStateIfInternalDataChanged = false,
this.controller,
String? initialValue,
FocusNode? focusNode,
InputDecoration? decoration,
String? hintText,
TextInputType? keyboardType,
TextCapitalization textCapitalization = TextCapitalization.none,
TextInputAction? textInputAction,
TextStyle? style,
StrutStyle? strutStyle,
TextDirection? textDirection,
TextAlign textAlign = TextAlign.start,
TextAlignVertical? textAlignVertical,
bool autofocus = false,
bool readOnly = false,
ToolbarOptions? toolbarOptions,
bool? showCursor,
String obscuringCharacter = '•',
bool obscureText = false,
bool autocorrect = true,
bool autoTrimWhenTyping = false,
SmartDashesType? smartDashesType,
SmartQuotesType? smartQuotesType,
bool enableSuggestions = true,
MaxLengthEnforcement? maxLengthEnforcement,
int maxLines = 1,
int? minLines, // when multiline = true, minLines = 6
bool expands = false,
int? maxLength,
bool multiline = false,
ValueChanged<String>? onChanged,
GestureTapCallback? onTap,
VoidCallback? onEditingComplete,
ValueChanged<String>? onFieldSubmitted,
FormFieldSetter<String>? onSaved,
FormFieldValidator<String>? validator,
List<TextInputFormatter>? inputFormatters,
bool? enabled,
double cursorWidth = 2.0,
double? cursorHeight,
Radius? cursorRadius,
Color? cursorColor,
Brightness? keyboardAppearance,
EdgeInsets scrollPadding = const EdgeInsets.all(20.0),
bool enableInteractiveSelection = true,
TextSelectionControls? selectionControls,
InputCounterWidgetBuilder? buildCounter,
ScrollPhysics? scrollPhysics,
Iterable<String>? autofillHints,
AutovalidateMode? autovalidateMode,
ScrollController? scrollController,
}) : assert(initialValue == null || controller == null),
assert(obscuringCharacter.length == 1),
assert(
maxLengthEnforcement == null,
'maxLengthEnforced is deprecated, use only maxLengthEnforcement',
),
assert(
(minLines == null) || (maxLines >= minLines),
"minLines can't be greater than maxLines",
),
assert(
!expands || minLines == null,
'minLines and maxLines must be null when expands is true.',
),
assert(!obscureText || maxLines == 1,
'Obscured fields cannot be multiline.'),
assert(maxLength == null || maxLength > 0),
super(
key: key,
initialValue: controller != null ? controller.text : initialValue,
onSaved: onSaved,
validator: validator,
enabled: enabled ?? decoration?.enabled ?? true,
autovalidateMode: autovalidateMode ??
(validator != null ? AutovalidateMode.onUserInteraction : null),
builder: (FormFieldState<String> field) {
if (updateStateIfInternalDataChanged &&
field.value != data) {
WidgetsBinding.instance?.addPostFrameCallback((timeStamp) {
field.didChange(data);
});
}
final _OutlineTextFormFieldState state =
field as _OutlineTextFormFieldState;
final InputDecoration effectiveDecoration = InputDecoration(
hintStyle: TextStyle(
color: Colors.grey,
fontSize: 15,
fontWeight: FontWeight.w400,
),
hintMaxLines: 2,
errorMaxLines: 2,
helperMaxLines: 2,
contentPadding: EdgeInsets.symmetric(
horizontal: 20,
vertical: 10,
),
filled: true,
fillColor: Colors.white,
floatingLabelBehavior: FloatingLabelBehavior.never,
).copyWith(hintText: hintText);
// .applyDefaults(Theme.of(field.context).inputDecorationTheme);
void onChangedHandler(String value) {
field.didChange(autoTrimWhenTyping ? value.trim() : value);
if (onChanged != null) {
onChanged(value);
}
}
return TextField(
controller: state._effectiveController,
focusNode: focusNode,
decoration:
effectiveDecoration.copyWith(errorText: field.errorText),
keyboardType: keyboardType,
textInputAction: textInputAction,
style: style ?? TextStyle(
color: Colors.black,
fontSize: 15,
fontWeight: FontWeight.w400,
),
strutStyle: strutStyle,
textAlign: textAlign,
textAlignVertical: textAlignVertical,
textDirection: textDirection,
textCapitalization: textCapitalization,
autofocus: autofocus,
toolbarOptions: toolbarOptions,
readOnly: readOnly,
showCursor: showCursor,
obscuringCharacter: obscuringCharacter,
obscureText: obscureText,
autocorrect: autocorrect,
smartDashesType: smartDashesType ??
(obscureText
? SmartDashesType.disabled
: SmartDashesType.enabled),
smartQuotesType: smartQuotesType ??
(obscureText
? SmartQuotesType.disabled
: SmartQuotesType.enabled),
enableSuggestions: enableSuggestions,
maxLengthEnforcement: maxLengthEnforcement,
maxLines: multiline ? null : maxLines,
minLines: minLines == null && multiline
? _defaultMinLinesOnMultiline
: minLines,
expands: expands,
maxLength: maxLength,
onChanged: onChangedHandler,
onTap: onTap,
onEditingComplete: onEditingComplete,
onSubmitted: onFieldSubmitted,
inputFormatters: inputFormatters,
enabled: enabled ?? decoration?.enabled ?? true,
cursorWidth: cursorWidth,
cursorHeight: cursorHeight,
cursorRadius: cursorRadius,
cursorColor: cursorColor,
scrollPadding: scrollPadding,
scrollPhysics: scrollPhysics,
keyboardAppearance: keyboardAppearance,
enableInteractiveSelection: enableInteractiveSelection,
selectionControls: selectionControls,
buildCounter: buildCounter,
autofillHints: autofillHints,
scrollController: scrollController,
);
},
);
/// Controls the text being edited.
///
/// If null, this widget will create its own [TextEditingController] and
/// initialize its [TextEditingController.text] with [initialValue].
final TextEditingController? controller;
late String data;
#override
_OutlineTextFormFieldState createState() => _OutlineTextFormFieldState();
}
class _OutlineTextFormFieldState extends FormFieldState<String> {
TextEditingController? _controller;
TextEditingController get _effectiveController =>
widget.controller ?? _controller!;
late StreamSubscription<String> dataStreamSubscription;
#override
OutlineTextFormField get widget => super.widget as OutlineTextFormField;
#override
void initState() {
super.initState();
if (widget.controller == null) {
_controller =
TextEditingController(text: widget.initialValue ?? widget.data);
}
setValue(widget.initialValue ?? widget.data);
_effectiveController.addListener(_handleControllerChanged);
dataStreamSubscription = Stream.value(widget.data).listen(_onData);
}
#override
void didUpdateWidget(OutlineTextFormField oldWidget) {
super.didUpdateWidget(oldWidget);
if (widget.controller != oldWidget.controller) {
oldWidget.controller?.removeListener(_handleControllerChanged);
widget.controller?.addListener(_handleControllerChanged);
if (oldWidget.controller != null && widget.controller == null)
_controller =
TextEditingController.fromValue(oldWidget.controller!.value);
if (widget.controller != null) {
setValue(widget.controller!.text);
if (oldWidget.controller == null) _controller = null;
}
}
}
#override
void dispose() {
dataStreamSubscription.cancel();
_effectiveController.removeListener(_handleControllerChanged);
super.dispose();
}
#override
void didChange(String? value) {
super.didChange(value);
if (_effectiveController.text != value) {
_effectiveController.text = value ?? '';
_effectiveController.selection =
TextSelection.collapsed(offset: value?.length ?? 0);
}
widget.data = _effectiveController.text;
}
#override
void reset() {
// setState will be called in the superclass, so even though state is being
// manipulated, no setState call is needed here.
_effectiveController.text = widget.initialValue ?? widget.data;
super.reset();
}
void _handleControllerChanged() {
// Suppress changes that originated from within this class.
//
// In the case where a controller has been passed in to this widget, we
// register this change listener. In these cases, we'll also receive change
// notifications for changes originating from within this class -- for
// example, the reset() method. In such cases, the FormField value will
// already have been set.
if (_effectiveController.text != value)
didChange(_effectiveController.text);
}
void _onData(String value) {
if (_effectiveController.text != value) _effectiveController.text = value;
}
}
** After you can use it:
String? validateStringBlank(
String? value,
String msg, [
bool autoToLowerCase = true,
]) =>
(value == null || value.trim().isEmpty)
? "Vui lòng nhập ${autoToLowerCase ? msg.toLowerCase() : msg}"
: null;
OutlineTextFormField(
data: "Apple",
hintText: "Enter company name",
validator: (value) =>
validateStringBlank(value,"Company Name"),
),