I'm navigating to the next page within my Flutter app.
On the next page there are a few Forms where a user can enter text.
final _form2Key = GlobalKey<FormState>();
Padding(
padding: EdgeInsets.only(top: 8.0),
child: Container(
width: screenWidth / 1.1,
height: screenHeight / 5.5,
child: Form(
key: _form2Key,
autovalidate: true,
child: TextFormField(
autofocus: true,
validator: (val) {
if (val.trim().length < 3 || val.isEmpty) {
return "Too short";
} else if (val.trim().length > 200) {
return "Too long";
} else {
return null;
}
},
onSaved: (val) => description = val,
decoration: InputDecoration(
border: OutlineInputBorder(),
filled: true,
fillColor: Colors.white,
labelText: "",
labelStyle: TextStyle(fontSize: 15.0),
hintText: " ",
),
),
),
),
Everything 'works', but after navigating to this new page, the keyboard shows up automatically and is focussed on the textfield.
Is it possible to avoid the keyboard popping up and only show the keyboard when the user presses the form?
Thanks in advance!
your keyboard is automatically popping up because you have set autofocus to true, please modify to false or remove that property to avoid keyboard from automatically popping up
final _form2Key = GlobalKey<FormState>();
Padding(
padding: EdgeInsets.only(top: 8.0),
child: Container(
width: screenWidth / 1.1,
height: screenHeight / 5.5,
child: Form(
key: _form2Key,
autovalidate: true,
child: TextFormField(
autofocus: false, // modified
validator: (val) {
if (val.trim().length < 3 || val.isEmpty) {
return "Too short";
} else if (val.trim().length > 200) {
return "Too long";
} else {
return null;
}
},
onSaved: (val) => description = val,
decoration: InputDecoration(
border: OutlineInputBorder(),
filled: true,
fillColor: Colors.white,
labelText: "",
labelStyle: TextStyle(fontSize: 15.0),
hintText: " ",
),
),
),
),
),
Related
I want to build a textFormFeild so that it is like the one in the image below, in addition to that I can control its properties such as default textFormFeild and it is responsive.
In the case of focus, the background color is changed. I want to build a costume.
Empty case:
Fill case:
Code:
TextFormField(
keyboardType: TextInputType.emailAddress,
decoration: const InputDecoration(hintText: 'Enter Your Email'),
)
Try below code hope its help to you, I have try same
Variable Declaration:
TextEditingController textEditingController = TextEditingController();
bool isTextEmpty = false;
Widget:
Container(
padding:const EdgeInsets.only(top: 10, left: 15),
height: 60,
decoration: BoxDecoration(
color: isTextEmpty ? Colors.white : Colors.grey.shade200,
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: isTextEmpty ? Colors.red : Colors.grey.shade300,
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('Email'),
TextField(
controller: textEditingController,
onChanged: (value) {
setState(() {
if (value.length > 0) {
isTextEmpty = true;
} else {
isTextEmpty = false;
}
});
},
decoration: const InputDecoration(
isDense: true,
border: InputBorder.none,
hintText: 'Enter Email Here',),
),
],
),
),
Result before empty TextField:
Result after fill TextField
You need to check the validation process as follows.
TextFormField(
// The validator receives the text that the user has entered.
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
Still a newbie with flutter and was wondering if someone could help me with a problem I'm having. I am trying to create a registeration form with email, password, password confirmation, a county and a zip code. (County and zip code forms are the drop down button form fields) I have successfully coded all else except for the zip code drop down. I would need it to be conditional on the county selection. (In a way that if I select a specific county in cali, it would only display that selected county's zip codes and nothing else). Also if someone would know a quick fix to make the dropdown button form fields empty unless clicked on. My current adaptation on it isn't very functional, since you can just leave the option unanswered, when it's supposed to be mandatory. Thank you in advance :)
Existing code below
(I only have the string for one county zip codes) (Also deleted the irrelevant firebase related code for this post)
class RegisterPage extends StatefulWidget {
const RegisterPage({Key? key}) : super(key: key);
#override
State<RegisterPage> createState() => _RegisterPageState();
}
class _RegisterPageState extends State<RegisterPage> {
// dropdown area
_MyFormStateArea(){
selectedArea = dropdownListArea[0];
}
var selectedArea = '';
final dropdownListArea = <String>['', 'LA', 'San Francisco'...'Santa Barbara'];
// dropdown zipcode
_MyFormStateZip(){
selectedZip = dropdownListZip[0];
}
var selectedZip = '';
final dropdownListZip = <String>['', '90001', '90002', '90003',..., '91609'];
// editing Controller
final emailEditingController = new TextEditingController();
final passwordEditingController = new TextEditingController();
final confirmPasswordEditingController = new TextEditingController();
#override
Widget build(BuildContext context) {
// email field
final emailField = TextFormField(
autofocus: false,
controller: emailEditingController,
keyboardType: TextInputType.emailAddress,
validator: (value) {
if (value!.isEmpty) {
return ("Please enter email.");
}
// reg expression for email validation
if (!RegExp("^[a-zA-Z0-9+_.-]+#[a-zA-Z0-9.-]+.[a-z]")
.hasMatch(value)) {
return ("Please enter a working email.");
}
return null;
},
onSaved: (value) {
emailEditingController.text = value!;
},
textInputAction: TextInputAction.next,
decoration: InputDecoration(
prefixIcon: Icon(Icons.mail_outline_outlined),
contentPadding: EdgeInsets.fromLTRB(20, 15, 20, 15),
hintText: "Email",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)
),
),
);
// password field
final passwordField = TextFormField(
autofocus: false,
controller: passwordEditingController,
obscureText: true,
validator: (value) {
RegExp regex = new RegExp(r'^.{6,}$');
if (value!.isEmpty) {
return ("A password required.");
}
if(!regex.hasMatch(value)) {
return ("Please enter other password (Min. 6 characters)");
}
},
onSaved: (value) {
passwordEditingController.text = value!;
},
textInputAction: TextInputAction.next,
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock_outlined),
contentPadding: EdgeInsets.fromLTRB(20, 15, 20, 15),
hintText: "Password",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)),
));
// confirm password field
final confirmPasswordField = TextFormField(
autofocus: false,
controller: confirmPasswordEditingController,
obscureText: true,
validator: (value)
{
if(confirmPasswordEditingController.text != passwordEditingController.text)
{
return "Passwords don't match";
}
return null;
},
onSaved: (value) {
confirmPasswordEditingController.text = value!;
},
textInputAction: TextInputAction.next,
decoration: InputDecoration(
prefixIcon: Icon(Icons.lock_outlined),
contentPadding: EdgeInsets.fromLTRB(20, 15, 20, 15),
hintText: "Password again",
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)
),
),
);
// area dropdown
final areaField = DropdownButtonFormField(
value: selectedArea,
items: dropdownListArea.map((e) =>
DropdownMenuItem(value: e, child: Text(e),)).toList(),
onChanged: (String? value) {
setState(() {
if (value != null) {
selectedArea = value;
}
});
},
decoration: InputDecoration(
labelText: 'County',
prefixIcon: Icon(Icons.location_city_outlined),
contentPadding: EdgeInsets.fromLTRB(20, 10, 0, 10),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)),
)
);
// zip code field
final zipCodeField = DropdownButtonFormField(
value: selectedZip,
items: dropdownListZip.map((e) =>
DropdownMenuItem(value: e, child: Text(e),)).toList(),
onChanged: (String? value) {
setState(() {
if (value != null) {
selectedZip = value;
}
});
},
decoration: InputDecoration(
labelText: 'Zip Code',
prefixIcon: Icon(Icons.location_on_outlined),
contentPadding: EdgeInsets.fromLTRB(20, 10, 0, 10),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)),
)
);
// sign up button
final signUpButton = Material(
elevation: 5,
borderRadius: BorderRadius.circular(30),
color: Colors.white,
child: MaterialButton(
padding: EdgeInsets.fromLTRB(20, 15, 20, 15),
minWidth: MediaQuery.of(context).size.width,
onPressed: () {
signUp(emailEditingController.text, passwordEditingController.text);
},
child: Text("Sign Up", textAlign: TextAlign.center,
style: TextStyle(fontSize: 20,
color: Colors.lightBlue[900],
fontWeight: FontWeight.bold)
),
),
);
return Scaffold(
backgroundColor: Color(0xFFAED8E6),
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
leading: IconButton(
onPressed: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => FrontPage()));
},
icon: Icon(Icons.arrow_back),
color: Colors.lightBlue[900],
),
),
body: Center(
child: SingleChildScrollView(
child: Container(
color: Color(0xFFAED8E6),
child: Padding(
padding: const EdgeInsets.fromLTRB(36, 20, 36, 30),
child: Form(
key: _formKey,
child:
Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(
height: 170,
child: Image.asset("assets/AR_logoBold.png",
fit: BoxFit.contain
)),
SizedBox(height: 40,),
emailField,
SizedBox(height: 25,),
passwordField,
SizedBox(height: 25,),
confirmPasswordField,
SizedBox(height: 25,),
areaField,
SizedBox(height: 25,),
zipCodeField,
SizedBox(height: 35,),
signUpButton,
SizedBox(height: 15,),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("You already own an account? "),
GestureDetector(onTap: () {
Navigator.push(context, MaterialPageRoute(builder: (context) => LoginPage()));
},
child: Text("Login",
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 15,
color: Colors.lightBlue[900])
),)
],
)
],
)),
),
),
),
),
);
}
}
First you should initalize the selectedArea and selectedZip with the first Entry of your List: = dropdownListZip.first and the first entry should not be an empty String
To get only zips that belong to the choosen area you need to know them. At the moment you have two independent lists.
Better you use a map like:
Map<String, List<String>> zipmap = Map('LA': ['9000','9001','9002], 'San Francisco': ['9003', '9004']);
Than you can only display zips that belong to the area quite easy.
Take a look at: Flutter Documentation for Map
Map<String, List<String>> zipmap = Map('LA': ['9000','9001','9002], 'San Francisco': ['9003', '9004']);
final areaField = DropdownButtonFormField(
value: selectedArea,
items: zipmap.keys.toList().map((e) =>
DropdownMenuItem(value: e, child: Text(e),)).toList(),
onChanged: (String? value) {
setState(() {
if (value != null) {
selectedArea = value;
}
});
},
decoration: InputDecoration(
labelText: 'County',
prefixIcon: Icon(Icons.location_city_outlined),
contentPadding: EdgeInsets.fromLTRB(20, 10, 0, 10),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)),
)
);
// zip code field
final zipCodeField = DropdownButtonFormField(
value: selectedZip,
items: zipmap[selectedArea].map((e) =>
DropdownMenuItem(value: e, child: Text(e),)).toList(),
onChanged: (String? value) {
setState(() {
if (value != null) {
selectedZip = value;
}
});
},
decoration: InputDecoration(
labelText: 'Zip Code',
prefixIcon: Icon(Icons.location_on_outlined),
contentPadding: EdgeInsets.fromLTRB(20, 10, 0, 10),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10)),
)
);
I have a TextFormField. This widget validation is empty show error text. However, the error text fill looks like the picture. How can i solve it.
SizedBox(
height: 50,
child: TextFormField(
validator: (val) {
if (val!.isEmpty) {
return "Boş Geçilemez";
}
if (val.length > 100) {
return "Karakter Sınırı Hatası";
}
return null;
},
obscureText: true,
decoration: InputDecoration(
suffixIcon: const Icon(
Icons.lock_outline_rounded,
color: Color(0xffBDBDBD),
),
hintText: AppLocalizations.getString("sifre"),
contentPadding:
const EdgeInsets.only(top: 14, left: 10, bottom: 10)),
controller: passwordController,
),
),
It would be better if you use same content padding for top and bottom, and try increasing the height of TextFiled like 64.
And use autovalidateMode: AutovalidateMode.onUserInteraction,
SizedBox(
height: 64,
child: TextFormField(
autovalidateMode: AutovalidateMode.onUserInteraction,
validator: (val) {
if (val != null && val.isEmpty) {
return "Boş Geçilemez";
}
if (val!.length > 10) {
return "Karakter Sınırı Hatası";
}
return null;
},
decoration: InputDecoration(
suffixIcon: const Icon(
Icons.lock_outline_rounded,
color: Color(0xffBDBDBD),
),
hintText: "sifre",
contentPadding: const EdgeInsets.only(
top: 14,
left: 10,
bottom: 14,
)),
),
),
I have to make one application which take so many user input , that's why i use so many Textfiled in my code for taking user input.
An Textfiled validation time I simply use List of Global keys and it's work perfactly.
But problem is when user give wrong input then textfiled show an error message that time my Textbox UI can change (UI well change in very appropriately).
Pleas help , what can i do so my error message postion will be changed.
TextFiled code :
//this is inside the statfull Widget
GlobalKey<FormState> _formkeySubNumber = new GlobalKey();
Widget SubNumber_Input_Box() {
return Form(
//for form validation
key: _formkeySubNumber,
child: Container(
margin: EdgeInsets.only(top: 7, left: 6),
height: 38,
width: 300,
decoration: BoxDecoration(
color: HexColor("#D9D9D9"),
borderRadius: BorderRadius.all(Radius.circular(10)),
),
//padding
child: Padding(
padding: EdgeInsets.only(left: 12),
//textfiled
child: TextFormField(
//for validation
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please Enter Subject Number';
}
return null;
},
//for storing user input && value in string format so we convert into the int formate
onChanged: (value) {
subjectNumber = int.tryParse(value)!;
},
//for only number
keyboardType: TextInputType.number,
decoration: const InputDecoration(
hintText: "Enter Minimum-4 to Maximum-8 Subject",
hintStyle: TextStyle(
fontFamily: "RobotoSlab",
fontSize: 14,
),
//for remove underline from input filed
border: InputBorder.none,
),
//for store only integer value
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(RegExp(r'[0-9]')),
],
),
),
),
);
}
And this call inside the one Button Like that
[Container(
margin: EdgeInsets.only(top: 5),
child: ElevatedButton(
child: Text("Go"),
style: ElevatedButton.styleFrom(
primary: HexColor("#EE6C4D"),
//this colour set (opacity is low) when button is disable
onSurface: HexColor("#E0FBFC"),
),
//store user value -->subjectNumber
onPressed: isGoButtonActive
? () {
//for Form Validation
if (_formkeySubNumber.currentState!
.validate()) {
//this show the container after prerssed button
showContainer1();
}
setState(() {
// //this is for store subjectnumber value from user input
// subjectNumber =
// subNumber_Controller.text;
// print("Subject Number Is : $subjectNumber");
});
}
: null,
),
),]
You have created a container around the textfield with specific height. For styling a textfield you can use
TextField(
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10.0),
),
filled: true,
hintStyle: TextStyle(color: Colors.grey),
hintText: "Type in your text",
fillColor: Colors.white),
)
This way the error message appears below the textfield and outside the white area.
You can remove the height from Form> Container. And it will give you flexible height based on error state.
Widget SubNumber_Input_Box() {
return Form(
key: _formkeySubNumber,
child: Container(
width: 300,
color: HexColor("#D9D9D9"),
//padding
child: Padding(
padding: EdgeInsets.only(left: 12),
child: TextFormField(
autovalidateMode: AutovalidateMode.onUserInteraction, // you might like this as well
Also I will suggest to look at OutlineInputBorder
Widget SubNumber_Input_Box() {
return Form(
key: _formkeySubNumber,
child: Padding(
padding: EdgeInsets.only(left: 12),
child: TextFormField(
autovalidateMode: AutovalidateMode.onUserInteraction,
//for validation
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please Enter Subject Number';
}
return null;
},
//for storing user input && value in string format so we convert into the int formate
onChanged: (value) {
subjectNumber = int.tryParse(value)!;
},
//for only number
keyboardType: TextInputType.number,
decoration: const InputDecoration(
hintText: "Enter Minimum-4 to Maximum-8 Subject",
hintStyle: TextStyle(
fontFamily: "RobotoSlab",
fontSize: 14,
),
enabledBorder: OutlineInputBorder(),
errorBorder: OutlineInputBorder(),
focusedBorder: OutlineInputBorder(),
border: OutlineInputBorder(),
),
//for store only integer value
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.allow(RegExp(r'[0-9]')),
],
),
),
);
}
I am still learning Flutter and scratching my head around this.
I have a form with 3 fields (email, password and password_confirmation).
I am using validators and updating a variable that is used as a condition on the button enabled state.
The problem is that the button enabled state is not being updated when I change for instance the email...
If I hot restart, it will update the button state..
How can I force the button to reevaluate it's condition?
What is the Flutter way of doing this?
Thank you
bool _validEmail = false;
bool _validPassword = false;
bool _validPasswordConfirmation = false;
bool _validAccount = false;
SignUpError _serverSignUpErrors = null;
Form signup_form() {
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
//Email field
Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Email",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
),
SizedBox(
height: 10,
),
TextFormField(
controller: _emailFieldController,
obscureText: false,
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Color(0xfff3f3f4),
filled: true),
onChanged: (String value) {
if(_serverSignUpErrors?.email_errors?.isNotEmpty == true)
_serverSignUpErrors.email_errors.clear();
},
autovalidate: true,
validator: (value) {
_validEmail = false;
if (value.isEmpty)
return 'Please enter some text';
//Validate email
String p = r'^(([^<>()[\]\\.,;:\s#\"]+(\.[^<>()[\]\\.,;:\s#\"]+)*)|(\".+\"))#((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regExp = new RegExp(p);
if(!regExp.hasMatch(value))
return 'Invalid email';
if(_serverSignUpErrors?.email_errors?.isNotEmpty == true)
return _serverSignUpErrors.email_errors.join(';');
_validEmail = true;
return null;
}
)
],
),
),
//Password field
Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Password",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
),
SizedBox(
height: 10,
),
TextFormField(
controller: _passwordController,
obscureText: true,
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Color(0xfff3f3f4),
filled: true),
onChanged: (String value) {
if(_serverSignUpErrors?.password_errors?.isNotEmpty == true)
_serverSignUpErrors.password_errors.clear();
this.didChangeDependencies();
},
autovalidate: true,
validator: (value) {
_validPassword = false;
if (value.isEmpty || value.length < 4)
return 'Password must be at least 6 chars wide';
if(_serverSignUpErrors?.password_errors?.isNotEmpty == true)
return _serverSignUpErrors.password_errors.join(';');
_validPassword = true;
return null;
}
)
],
),
),
//Password field
Container(
margin: EdgeInsets.symmetric(vertical: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
"Password confirmation",
style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
),
SizedBox(
height: 10,
),
TextFormField(
controller: _passwordConfirmationController,
obscureText: true,
decoration: InputDecoration(
border: InputBorder.none,
fillColor: Color(0xfff3f3f4),
filled: true),
onChanged: (String value) {
if(_serverSignUpErrors?.password_confirmation_errors?.isNotEmpty == true)
_serverSignUpErrors.password_confirmation_errors.clear();
this.didChangeDependencies();
},
autovalidate: true,
validator: (value) {
_validPasswordConfirmation = false;
if (value.isEmpty)
return 'Please enter password confirmation';
if(value != _passwordController.text)
return 'Password confirmation doesn\'t match password';
if(_serverSignUpErrors?.password_confirmation_errors?.isNotEmpty == true)
return _serverSignUpErrors.password_confirmation_errors.join(';');
_validPasswordConfirmation = true;
return null;
}
)
],
),
),
Center(
child: FlatButton(
onPressed: (_validEmail && _validPassword && _validPasswordConfirmation && _validAccount == false) ? () async => await onRegister() : null,
disabledColor: Colors.blueGrey,
child: Text(
'Register Now',
style: TextStyle(fontSize: 20, color: Colors.white),
),
color: Colors.blue, //specify background color for the button here
colorBrightness: Brightness.dark, //specify the color brightness here, either `Brightness.dark` for darl and `Brightness.light` for light
highlightColor: Colors.red, //color when the button is being actively pressed, quickly fills the button and fades out after
padding: EdgeInsets.symmetric(horizontal: 8.0, vertical: 5.0), // gives padding to the button
),
),
]
)
);
}
It seems you're == false is reversing your intended behavior. Not sure if that's what you want.
onPressed: (_validEmail && _validPassword && _validPasswordConfirmation && _validAccount == false) ? () async => await onRegister() : null,
To solve your error you should wrap anything where you change the state with variables like _validEmail, _validPassword, etc. with setState(() {});. This will rebuild the widget, including the button, when you change the state so it will enable when you want and you'll get the behavior you want.