I'm trying to create a custom TextFormField that will accepts parameters that I needed like labelText, controller or prefixIcon etc. What I don't know is how to pass a function to an OnChange and onSubmitted events. Please check the sample code below.
utils.dart
class TextFieldTemplate extends StatelessWidget {
final String initialValue;
final String labelText;
final String hintText;
final IconData prefixIcon;
final TextEditingController controller;
final Function onTap;
TextFieldTemplate(
{this.initialValue,
this.labelText,
this.prefixIcon,
this.controller,
});
#override
Widget build(BuildContext context) {
return TextFormField(
onChanged: (){},
onSubmitted: (){},
controller: controller,
decoration: InputDecoration(
contentPadding: decorationPadding,
labelText: labelText,
prefixIcon: prefixIcon != null
? Icon(
prefixIcon,
)
: null,
),
);
}
}
Usage for the TextFieldTemplate:
var _personName = TextEditingController();
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
childtren[
TextFieldTemplate(
labelText: 'Person Name',
controller: _personName,
prefixIcon: Icons.person,
// onSubmitted : ???
// OnChange : ???
),
]
),
);
}
You can pass onChanged and onSubmitted parameter like this
Function(String) onChanged;
Function(String) onSubmitted;
And use like this
TextFieldTemplate(
labelText: 'Person Name',
controller: _personName,
prefixIcon: Icons.person,
onSubmitted: (value){},
onChanged: (value){},
),
Related
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 code a textformfield widget and add validator then I called it from another screen but validator doesn't work. how to add validator in widget and call it from another class?
Input Field widget(this is what I called from loginscreen)
import 'package:flutter/material.dart';
import 'constants.dart';
class InputField extends StatelessWidget {
final TextEditingController controller;
final IconData? icon;
final String? text;
final String? errorText;
final TextInputType textInputType;
final TextAlign textAlign;
final Function function;
const InputField({
Key? key,
required this.controller,
this.icon,
this.text,
required this.textInputType,
this.errorText,
required this.textAlign,
required this.function,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return Container(
child: TextFormField(
controller: controller,
keyboardType: textInputType,
textInputAction: TextInputAction.next,
validator: (value){
function(value);
},
textAlign: textAlign,
decoration: InputDecoration(
prefixIcon: Icon(icon,color: kPrimaryColor,),
contentPadding: const EdgeInsets.all(5),
hintStyle: TextStyle(
fontFamily: 'InriaSans',
fontStyle: FontStyle.italic,
fontWeight: FontWeight.bold
),
hintText: text,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),),
),
),
margin: EdgeInsets.symmetric(vertical: 5, horizontal: 40),
);
}
}
LOGIN SCREEN
I called input field in this login screen.
Widget _buildEmail(){
return InputField(
controller: emailController,
icon: Icons.email,
text: 'User email',
textAlign: TextAlign.left,
textInputType: TextInputType.emailAddress,
function: (){
return Validator.emailValidate(emailController.text);
},
);
}
I have a form with TextFormFields. I can give focus to next field using Tab key as usual. But, it skips DateTimeFormField. How can I make it show the Date picker instead of skipping it when it is its turn?
Did some search but couldn't find anything near it.
Here is my code samples. I simply use TextFormField's and DateTimeFormField inside a Column. But there is no focus option for DateTimeFormField.
class FormDialog extends ConsumerWidget {
const FormDialog({
Key? key,
}) : super(key: key);
#override
Widget build(BuildContext context, WidgetRef ref) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const SizedBox(
height: 12,
),
CustomTextField(
labelText: firstName,
textInputAction: TextInputAction.next,
),
const SizedBox(
height: 12,
),
CustomTextField(
labelText: lastName,
textInputAction: TextInputAction.next,
),
const SizedBox(
height: 12,
),
CustomDateField(
labelText: dateOfBirth,
),
const SizedBox(
height: 12,
),
CustomTextField.email(
labelText: parentEmail,
),
],
);
}
}
class CustomDateField extends StatelessWidget {
const CustomDateField({
Key? key,
this.labelText,
this.hintText,
}) : super(key: key);
final String? labelText;
final String? hintText;
#override
Widget build(BuildContext context) {
return DateTimeFormField(
mode: DateTimeFieldPickerMode.date,
dateFormat: DateFormat.yMd(),
decoration: InputDecoration(
labelText: labelText,
hintText: hintText,
border: const OutlineInputBorder(),
helperText: ' ',
),
);
}
}
class CustomTextField extends StatelessWidget {
const CustomTextField({
Key? key,
this.labelText,
this.icon,
this.textInputAction,
}) : super(key: key);
final String? labelText;
final TextFieldIcon? icon;
final TextInputAction? textInputAction;
#override
Widget build(BuildContext context) {
return TextFormField(
autovalidateMode: AutovalidateMode.onUserInteraction,
decoration: InputDecoration(
hintText: labelText,
border: const OutlineInputBorder(),
prefixIcon: icon,
helperText: ' ',
),
textInputAction: textInputAction,
);
}
factory CustomTextField.email({
String? errorMessage,
String? labelText,
}) =>
CustomTextField(
labelText: labelText ?? email,
icon: const TextFieldIcon.email(),
textInputAction: TextInputAction.next,
);
}
According to the package's Github repository, it is built upon FormField, creating and initializing a FocusNode and passing the focus node to the widget might resolve the issue. If not you might need to implement your own form field.
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 2 widgets, one is the main one with a bunch of other widgets loaded on it. The other is the TextFormField that I need to validate.
Main widget:
RoundedInputField(
onChanged: (value) {
_email = value;
},
),
RoundedButton(
text: "LOGIN",
press: () async {}, // This is the button that should validate the input
);
Field widget:
class RoundedInputField extends StatefulWidget {
final String hintText;
final IconData icon;
final ValueChanged<String> onChanged;
const RoundedInputField({
Key key,
this.hintText,
this.icon = Icons.alternate_email,
this.onChanged,
}) : super(key: key);
#override
_RoundedInputFieldState createState() => _RoundedInputFieldState();
}
class _RoundedInputFieldState extends State<RoundedInputField> {
TextEditingController email = new TextEditingController();
#override
Widget build(BuildContext context) {
return TextFieldContainer(
child: TextFormField(
controller: email,
keyboardType: TextInputType.emailAddress, //TextInputType.phone,
onChanged: widget.onChanged,
decoration: InputDecoration(
hintText: "Your Email",
icon: Icon(
widget.icon,
color: Theme.of(context).primaryColorDark,
),
border: InputBorder.none,
),
validator: (value) {
return Helpers.CheckInput(value);
},
),
);
}
}
How can I validate the input clicking on the other LOGIN widget?
final _formKey = GlobalKey<FormState>();
...
Form(
key: _formKey,
child: Column(children: <Widget>[
RoundedInputField(
onChanged: (value) {
_email = value;
},
),
RoundedPasswordField(
onChanged: (value) {
_password = value;
},
),
RoundedButton(
text: "LOGIN",
press: () async {}
),