I am implementing a Flutter app having TextFormField in my login screen to capture credentials. When I ran it on Android Emulator with 12.0 or real device with Android 12 it is showing white container instead of keyboard. It is happening with Android 12 only.
What can I try next?
Note: It is working fine on all iOS Versions.
return Form(
key: _formKeySignInInputs,
child: Stack(
children: [
SingleChildScrollView(
scrollDirection: Axis.vertical,
child: Column(
children: [
Padding(padding: EdgeInsets.fromLTRB(0, 8, 0, 8.vw),
child: buildEmailFormField()),
Padding(padding: EdgeInsets.fromLTRB(0, 0, 0, 2.vw),
child: buildPasswordFormField()),
Padding(padding: EdgeInsets.fromLTRB(2.vw, 0, 0, 4.vw),
],
),
)
],
)
);
}
TextFormField buildPasswordFormField() {
var localeContext = AppLocalizations.of(context)!;
return TextFormField(
autocorrect: false,
obscureText: true,
enableSuggestions: false,
onSaved: (newValue) => password = newValue,
onChanged: (value) {
if (value.isNotEmpty) {
removeError(error: localeContext.password_null_error);
} else if (value.length >= 4) {
removeError(error: localeContext.short_password_error);
}
return;
},
validator: (value) {
if (value!.isEmpty) {
addError(error: localeContext.password_null_error);
return "";
} else if (value.length < 4) {
addError(error: localeContext.short_password_error);
return "";
}
},
decoration: InputDecoration(
labelText: localeContext.password,
hintText: localeContext.password_hint,
floatingLabelBehavior: FloatingLabelBehavior.always,
suffixIcon: const InputFieldSurffixIcon(svgIcon: "assets/icons/password.svg"),
),
);
}
TextFormField buildEmailFormField() {
var localeContext = AppLocalizations.of(context)!;
return TextFormField(
autocorrect: false,
enableSuggestions: false,
keyboardType: TextInputType.emailAddress,
onSaved: (newValue) => email = newValue,
onChanged: (value) {
if (value.isNotEmpty) {
removeError(error: localeContext.email_null_error);
} else if (emailValidatorRegExp.hasMatch(value)) {
removeError(error: localeContext.invalid_email_error);
}
},
validator: (value) {
if (value!.isEmpty) {
addError(error: localeContext.email_null_error);
return "";
} else if (!emailValidatorRegExp.hasMatch(value)) {
addError(error: localeContext.invalid_email_error);
return "";
}
},
decoration: InputDecoration(
labelText: localeContext.email,
hintText: localeContext.email_hint,
floatingLabelBehavior: FloatingLabelBehavior.always,
suffixIcon: const InputFieldSurffixIcon(svgIcon: "assets/icons/temp/mail.svg"),
),
);
}
Related
This is the code for the textformfield I want to restrict. Users should only be able to enter values from 1-10 but I cant find how to implement that
TextFormField(
validator: (value) {
if (value.isEmpty) {
return 'Please enter the Overall Rating';
}
return null;
},
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
], // Only numbers can be entered
maxLength: 2,
maxLengthEnforced: true,
controller: overall,
decoration: InputDecoration(
hintText: "Overall Rating Out of /10",
),
),
inside the inputFormatters : you simply put below express and this should work...
// regex expression to accept number only from 1-10
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'^[1-9]$|^10$'),
),],
if you want to check the given string is a less than 11 you can do it with the help you a validator. but when using a validator you need to perform a trigger or an event need to take place
If you want to run your code that way you can use this code ...
Code with using validator(Using tigger or even)
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class Textfi extends StatelessWidget {
Textfi({Key? key}) : super(key: key);
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Form(
key: _formKey,
child: Column(children: [
const SizedBox(
height: 70,
),
TextFormField(
validator: (value) {
if (value!.isEmpty) {
return 'Please enter the Overall Rating';
} else if (int.parse(value) < 1 || int.parse(value) > 10) {
return 'The rating must be between 1 and 10';
}
return null;
},
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
], // Only numbers can be entered
maxLength: 2,
maxLengthEnforced: true,
decoration: const InputDecoration(
hintText: "Overall Rating Out of /10",
),
),
GestureDetector(
onTap: () {
if (_formKey.currentState!.validate()) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Validation done')),
);
}
},
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Container(
height: 30,
width: 80,
color: Colors.blue,
child: const Center(child: Text("Submit")),
),
),
)
]),
),
);
}
}
If you want to check the value in real time
you can't use a validator you need to restrict your input value the only way to do that is using inputFormatters:
in your case you are using inputFormatter as:
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
which will only input digits
If you want to input a restricted number you need to make use of Regex
for that change your
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
to
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(RegExp("^(1[0-0]|[1-9])\$")),
],
This will help you to only enter only numbers from 1 to 10 : -
RegExp("^(1[0-0]|[1-9])$")
**Full code **
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class Textfi extends StatelessWidget {
Textfi({Key? key}) : super(key: key);
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Form(
key: _formKey,
child: Column(children: [
const SizedBox(
height: 70,
),
TextFormField(
validator: (value) {
if (value!.isEmpty) {
return 'Please enter the Overall Rating';
} else if (int.parse(value) < 1 || int.parse(value) > 10) {
return 'The rating must be between 1 and 10';
}
return null;
},
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(RegExp("^(1[0-0]|[1-9])\$")),
],
// inputFormatters: <TextInputFormatter>[
// FilteringTextInputFormatter.digitsOnly
// ], // Only numbers can be entered
maxLength: 2,
maxLengthEnforced: true,
decoration: const InputDecoration(
hintText: "Overall Rating Out of /10",
),
),
GestureDetector(
onTap: () {
if (_formKey.currentState!.validate()) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Validation done')),
);
}
},
child: Padding(
padding: const EdgeInsets.all(20.0),
child: Container(
height: 30,
width: 80,
color: Colors.blue,
child: const Center(child: Text("Submit")),
),
),
)
]),
),
);
}
}
you can update your validator function as follows
validator: (value) {
if (value.isEmpty) {
return 'Please enter the Overall Rating';
}
if(int.parse(value) < 1 || int.parse(value) > 10) {
return 'The rating must be between 1 and 10';
}
return null;
},
you can try with form and for digit validation, you need to parse the string to int
Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
TextFormField(
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
// Only numbers can be entered
maxLength: 2,
maxLengthEnforced: true,
controller: overall,
decoration: InputDecoration(
hintText: "Overall Rating Out of /10",
),
validator: (text) {
if (text == null || text.isEmpty) {
return 'Text is empty';
}
if (int.parse(text) < 1 || int.parse(text) > 10) {
return 'The rating must be between 1 and 10';
}
return null;
},
),
TextButton(
onPressed: () {
if (_formKey.currentState.validate()) {
// TODO submit
}
},
child: Text('Submit'),
)
],
),
)
I have a TextFormField with borders. I am using a validator with some conditions to show proper error messages. My error messages are shown exactly below the TextFormField border which is what I want. In my validator, I use an if statement that should return a String, so I have to add return '' after the if statement, as shown in my code:
TextFormField buildPasswordForm() {
return TextFormField(
//key: ValueKey('passwordKey'),
keyboardType: TextInputType.visiblePassword,
obscureText: _isHidden,
decoration: InputDecoration(
//labelText: 'Passowrd',
hintText: 'Password',
floatingLabelBehavior: FloatingLabelBehavior.never,
prefixIcon: Icon(
Icons.lock_sharp,
//color: kTextColor,
),
suffixIcon: Padding(
padding: EdgeInsets.symmetric(
horizontal: getProportionateScreenWidth(12),
),
child: GestureDetector(
onTapDown: inContact,
onTapUp: outContact,
child: Icon(
Icons.remove_red_eye,
size: 26,
//color: kTextColor,
),
),
),
),
validator: (value) {
if (value.isEmpty) {
addPasswordError(error: kPasswordNullError);
return '';
} else if (value.length < 8) {
addPasswordError(error: kShortPasswordError);
return '';
}
return null;
},
onChanged: (value) {
if (value.isNotEmpty) {
removePasswordError(error: kPasswordNullError);
} else if (value.length >= 8) {
removePasswordError(error: kShortPasswordError);
}
return null;
},
onSaved: (newValue) => password = newValue,
);
}
TextFormField buildEmailForm() {
return TextFormField(
keyboardType: TextInputType.emailAddress,
//autofocus: true,
decoration: InputDecoration(
//labelText: 'Email',
hintText: 'Enter your email',
floatingLabelBehavior: FloatingLabelBehavior.always,
prefixIcon: Icon(Icons.mail),
),
validator: (value) {
if (value.isEmpty) {
addEmailError(error: kEmailNullError);
return "";
} else if (!emailValidatorRegExp.hasMatch(value)) {
addEmailError(error: kInvalidEmailError);
return "";
}
return null;
},
onChanged: (value) {
if (value.isNotEmpty) {
removeEmailError(error: kEmailNullError);
} else if (emailValidatorRegExp.hasMatch(value)) {
emailErrors.remove(kInvalidEmailError);
addEmailError(error: kInvalidEmailError);
}
return null;
},
onSaved: (newValue) => email = newValue,
);
}
}
If I add return '', the space between the error message and the bottom board is added, but if the validation is not satisfied, that is: if neither email nor password entered satisfy requirements, the screen does not go to the next one as it should. If I remove return '', then the space between the error message and the bottom borders disappears, but when I hit continue, wether fields are properly filled or not, the screen just goes to the next one.
I would love to have a solution for, and explanation of this behavior.
in my case is like this i usually separate my validator to a different class
class Validator{
static String email(String value){
String pattern = r'^[a-zA-Z0-9.]+#[a-zA-Z0-9]+\.[a-zA-Z]';
RegExp regExp = RegExp(pattern);
if(!regExp.hasMatch(value)) return 'Email is invalid';
else if(value.isEmpty) return 'Email is Empty';
else return null;
}
}
then i create a widget for my textformfield and to call the validator like this
CustomTextFieldX(
obsecure: false,
hintText: 'Enter your email',
nameTop: 'Email',
controller: registerController.emails,
keyboardType: TextInputType.emailAddress,
validators: (value) =>Validator.email(registerController.emails.text = value),
),
i hope it helps. just comment below if their something to clarify at.
I am trying to build a Sign In page with Flutter. I have used a validator to validate user inputs. I am trying to remove the error message automatically when the user fixes his input. As an example, if the user enters his email as: name#server (missing .domain) and clicks continue, s/he will get an error telling the user this is not a valid email form. if the user adds the missing part, .c (or more characters) the error message should disappear without the need to click continue again. This should go for the password field too.
Here is my code:
class _SignFormState extends State<SignForm> {
bool _isHidden = true;
final _formKey = GlobalKey<FormState>();
void inContact(TapDownDetails details) {
setState(() {
_isHidden = false;
});
}
void outContact(TapUpDetails details) {
setState(() {
_isHidden = true;
});
}
#override
Widget build(BuildContext context) {
return Form(
key: _formKey,
child: Column(
children: [
buildEmailForm(),
SizedBox(height: getProportionateScreenHeight(20)),
buildPasswordForm(),
SizedBox(height: getProportionateScreenHeight(20)),
DefaultButton(
text: 'Continue',
press: () {
if (_formKey.currentState.validate()) {
return;
}
},
),
],
),
);
}
TextFormField buildPasswordForm() {
return TextFormField(
keyboardType: TextInputType.visiblePassword,
obscureText: _isHidden,
decoration: InputDecoration(
//labelText: 'Passowrd',
hintText: 'Password',
floatingLabelBehavior: FloatingLabelBehavior.never,
prefixIcon: Icon(
Icons.lock_sharp,
//color: kTextColor,
),
suffixIcon: Padding(
padding: EdgeInsets.symmetric(
horizontal: getProportionateScreenWidth(12),
),
child: GestureDetector(
onTapDown: inContact,
onTapUp: outContact,
child: Icon(
Icons.remove_red_eye,
size: 26,
//color: kTextColor,
),
),
),
),
);
}
TextFormField buildEmailForm() {
return TextFormField(
keyboardType: TextInputType.emailAddress,
autofocus: true,
decoration: InputDecoration(
//labelText: 'Email',
hintText: 'Enter your email',
floatingLabelBehavior: FloatingLabelBehavior.always,
prefixIcon: Icon(Icons.mail),
),
validator: (value) {
if (value.isEmpty) {
return kEmailNullError;
}
if (!emailValidatorRegExp.hasMatch(value)) {
return kInvalidEmailError;
}
return null;
},
onChanged: (value) {},
);
}
}
You can use autovalidateMode: AutovalidateMode.onUserInteraction to remove the error after enters/fixes data
TextFormField(
autovalidateMode: AutovalidateMode.onUserInteraction, // <-- add this line
keyboardType: TextInputType.emailAddress,
autofocus: true, ...
I've been studying darts for a few days.
I have created a validation class which I will use in the TextFormField validator, but I don't know how to call the function
i know the documentation flutter use this
validator: (value) {
if (value.isEmpty) {
return 'Please enter some text';
}
return null;
}
but i create this class, so when I need it just call the function
class Validation {
String validatePassword(String value) {
if (value.length < 4) {
return 'Password Minimal 4 Karakter';
}
return null;
}
String validateEmail(String value) {
if (!value.contains('#')) {
return 'Email Tidak Valid';
}
return null;
}
String validatedName(String value) {
if (value.isEmpty) {
return 'Nama Tidak Boleh Kosong';
}
return null;
}
}
and this is the code that will call the validation class
import 'package:flutter/material.dart';
import 'package:flutapp/src/mixins/validation.dart';
class RegisterScreen extends StatefulWidget {
createState() {
return RegisterScreenState();
}
}
class RegisterScreenState extends State<RegisterScreen> with Validation {
final formKey = GlobalKey<FormState>();
String name = '';
String email = '';
String password = '';
#override
Widget build(BuildContext context) {
return Container(
margin: EdgeInsets.all(20.0),
child: Form(
key: formKey,
child: Column(
children: [
nameField(),
emailField(),
passwordField(),
registerButton(),
],
),
)
);
}
}
Widget nameField() {
return TextFormField(
decoration: InputDecoration(
labelText: 'Nama Lengkap'
),
validator: validateName,
);
}
Widget emailField() {
return TextFormField(
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
labelText: 'Email',
hintText: 'contoh#gmail.com'
),
);
}
Widget passwordField() {
return TextFormField(
obscureText: true,
decoration: InputDecoration(
labelText: 'Password',
hintText: 'contoh#password123'
),
);
}
Widget registerButton() {
return RaisedButton(
color: Colors.blueAccent,
onPressed: () {
},
child: Text('Register'),
);
}
any answer will be appreciated
Just call your validator in the validator property of the TextFormField.
return TextFormField(
obscureText: true,
// added this line
validator: Validation(). validatePassword,
decoration: InputDecoration(
labelText: 'Password',
hintText: 'contoh#password123'
),
);
The function has the same signature as the validator property, so you can just do this, instead of this:
return TextFormField(
obscureText: true,
// added this line
validator: (String value)=> Validation().validatePassword(value),
decoration: InputDecoration(
labelText: 'Password',
hintText: 'contoh#password123'
),
);
My forms widget is not scrolling. How to define the size of the forms widget in flutter, complete form is not shown or visible. The user has to enter the following data / information and submit
folowing is my code `
class MiMobilesPageState extends State<MiMobilesPage> {
String _companyname;
String _modelname;
String _series;
String _year;
String _serielnumber;
String _warrantydate;
String _servicecentredetails;
String _name;
String _mobilenumber;
String _address;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
Widget _buildCompanyName() {
return TextFormField(
decoration: InputDecoration(labelText: 'CompanyName'),
maxLength: 10,
validator: (String value) {
if (value.isEmpty) {
return 'Company Name is Required';
}
return null;
},
onSaved: (String value) {
_companyname = value;
},
);
}
Widget _buildModelName() {
return TextFormField(
decoration: InputDecoration(labelText: 'ModelName'),
validator: (String value) {
if (value.isEmpty) {
return 'Model Name';
}
},
onSaved: (String value) {
_modelname = value;
},
);
}
Widget _buildSeries() {
return TextFormField(
decoration: InputDecoration(labelText: 'Series'),
keyboardType: TextInputType.visiblePassword,
validator: (String value) {
if (value.isEmpty) {
return 'Series is Required';
}
return null;
},
onSaved: (String value) {
_series = value;
},
);
}
Widget _buildYear() {
return TextFormField(
decoration: InputDecoration(labelText: 'Year'),
keyboardType: TextInputType.url,
validator: (String value) {
if (value.isEmpty) {
return 'Year of MFG is Required';
}
return null;
},
onSaved: (String value) {
_year = value;
},
);
}
Widget _buildSerielNumber() {
return TextFormField(
decoration: InputDecoration(labelText: 'SerielNumber'),
keyboardType: TextInputType.phone,
maxLength: 20,
validator: (String value) {
if (value.isEmpty) {
return 'Seriel-Number is Required';
}
return null;
},
onSaved: (String value) {
_serielnumber = value;
},
);
}
Widget _buildWarrantyDate() {
return TextFormField(
decoration: InputDecoration(labelText: 'Warranty-Date'),
keyboardType: TextInputType.number,
maxLength: 10,
validator: (String value) {
int date = int.tryParse(value);
if (date == null || date <= 0) {
return 'Warranty Date must be greater than 0';
}
return null;
},
onSaved: (String value) {
_warrantydate = value;
},
);
}
Widget _buildServiceCentreDetails() {
return TextFormField(
decoration: InputDecoration(labelText: 'ServiceCentreDetails'),
maxLength: 10,
validator: (String value) {
if (value.isEmpty) {
return 'Service Centre Details are Required';
}
return null;
},
onSaved: (String value) {
_servicecentredetails = value;
},
);
}
Widget _buildName() {
return TextFormField(
decoration: InputDecoration(labelText: 'Name'),
maxLength: 10,
validator: (String value) {
if (value.isEmpty) {
return 'Name is Required';
}
return null;
},
onSaved: (String value) {
_name = value;
},
);
}
Widget _buildMobileNumber() {
return TextFormField(
decoration: InputDecoration(labelText: 'MobileNumber'),
maxLength: 10,
validator: (String value) {
if (value.isEmpty) {
return 'Mobile Number is Required';
}
return null;
},
onSaved: (String value) {
_mobilenumber = value;
},
);
}
Widget _buildAddress() {
return TextFormField(
decoration: InputDecoration(labelText: 'Address'),
maxLength: 10,
validator: (String value) {
if (value.isEmpty) {
return 'Address is Required';
}
if (!RegExp(
r"[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*#(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")
.hasMatch(value)) {
return 'Please enter a valid Address';
}
return null;
},
onSaved: (String value) {
_companyname = value;
},
);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(centerTitle: true,backgroundColor: Colors.lightBlueAccent,title: Text("Basic Details",style:
TextStyle(fontSize: 20), textAlign: TextAlign.center),
actions:<Widget>[IconButton(icon:Icon(Icons.home), onPressed: (){
//debugPrint("Add New Device Cattegorry");
Navigator.push(context,MaterialPageRoute(builder:(context){
return MyHomePage();
}
)
); //
},
) ],
),
body: Container(
margin: EdgeInsets.all(24),
child: Form(
key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget> [
_buildCompanyName(),
_buildModelName(),
_buildSeries(),
_buildYear(),
_buildSerielNumber(),
_buildWarrantyDate(),
_buildServiceCentreDetails(),
_buildName(),
_buildMobileNumber(),
_buildAddress(),
SizedBox(height:400),
RaisedButton(
child: Text(
'Submit',
style: TextStyle(color: Colors.blue, fontSize: 20),
),
onPressed: () {
if (!_formKey.currentState.validate()) {
return;
}
_formKey.currentState.save();
print(_companyname);
print(_modelname);
print(_series);
print(_year);
print(_serielnumber);
print(_warrantydate);
print(_servicecentredetails);
print(_name);
print(_mobilenumber);
print(_address);
//Send to API
},
)
],
),
),
),
);
}
}
Please guide where I am wrong what is to be included in the code and what changes have to be made?`
I'm assuming the SizedBox(height:400), was a typo, meant to be 40?
To make your Form scroll, you'll need to wrap it in a Scrollable widget usually a ListView or a SingleChildScrollView.
I added an example using your code:
body: SingleChildScrollView( // wrap with SingleChildScrollView to allow scrolls
child: Container(
margin: EdgeInsets.all(24),
child: Form(
// key: _formKey,
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
_buildCompanyName(),
_buildModelName(),
_buildSeries(),
_buildYear(),
_buildSerielNumber(),
_buildWarrantyDate(),
_buildServiceCentreDetails(),
_buildName(),
_buildMobileNumber(),
_buildAddress(),
SizedBox(height:400), // incase this is not an error as I assumed, change to 400
RaisedButton(
child: Text(
'Submit',
style: TextStyle(color: Colors.blue, fontSize: 20),
),
onPressed: () {
if (!_formKey.currentState.validate()) {
return;
}
_formKey.currentState.save();
print(_companyname);
print(_modelname);
print(_series);
print(_year);
print(_serielnumber);
print(_warrantydate);
print(_servicecentredetails);
print(_name);
print(_mobilenumber);
print(_address);
//Send to API
},
)
],
),
),
),
);
Wrap the Column in a SingleChildScrollView if you want it to scroll.
Like this
Form(
child: SingleChildScrollView(
child: Column(
children: [...]
)
)
)