I am trying to get form validation working, however, every time the form validates to true and I cannot work out why. Below is my code and every time I click "Save" the form validation passes even when the text box is empty.
There is a single validator for testing purposes checking if the contents of the text box is empty.
Any help would be greatly appreciated.
import 'package:flutter/material.dart';
class SignInScreen extends StatefulWidget {
#override
_SignInScreenState createState() => _SignInScreenState();
}
class _SignInScreenState extends State<SignInScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
// Form field state
String phoneNumber;
void validateAndSave() {
final FormState form = _formKey.currentState;
if (form.validate()) {
print('Form is valid');
} else {
print('Form is invalid');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.symmetric(
horizontal: 20.0,
vertical: 10.0,
),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
SizedBox(height: 10.0),
TextFormField(
validator: (value) {
value.isEmpty ? 'Enter a mobile phone number' : null;
},
keyboardType: TextInputType.phone,
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(vertical: 15.0),
fillColor: Colors.white,
filled: true,
hintText: 'Mobile phone number',
prefixIcon: Icon(
Icons.phone_iphone,
size: 30.0,
),
),
onChanged: (value) {
setState(() => phoneNumber = value);
}),
SizedBox(height: 40.0),
FlatButton(
onPressed: () {
validateAndSave();
},
child: Text('Save'),
),
],
),
),
),
);
}
}
Solution is simple just add return keyword in the validator function.
import 'package:flutter/material.dart';
class SignInScreen extends StatefulWidget {
#override
_SignInScreenState createState() => _SignInScreenState();
}
class _SignInScreenState extends State<SignInScreen> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
// Form field state
String phoneNumber;
void validateAndSave() {
final FormState form = _formKey.currentState;
if (form.validate()) {
print('Form is valid');
} else {
print('Form is invalid');
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
padding: EdgeInsets.symmetric(
horizontal: 20.0,
vertical: 10.0,
),
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
SizedBox(height: 10.0),
TextFormField(
validator: (value) {
//Return a error string
return value.isEmpty ? 'Enter a mobile phone number' : null;
},
keyboardType: TextInputType.phone,
decoration: InputDecoration(
contentPadding: EdgeInsets.symmetric(vertical: 15.0),
fillColor: Colors.white,
filled: true,
hintText: 'Mobile phone number',
prefixIcon: Icon(
Icons.phone_iphone,
size: 30.0,
),
),
onChanged: (value) {
setState(() => phoneNumber = value);
}),
SizedBox(height: 40.0),
FlatButton(
onPressed: () {
validateAndSave();
},
child: Text('Save'),
),
],
),
),
),
);
}
}
Related
I'm trying to change my second TextField's visibility in my other Auth class but I'm using mobx. I tried this version. But I can't solve my problem anyway.
If I declare my otpvisibility variable in SignInView class, I need to use setstate when i change the value of this variable in the Auth class. But i can't use setstate because I am using mobx. On the other hang if I declare otpvisibility variable in my mobx class, changes won't effect.
class SignInView extends StatelessWidget {
final BuildContext context;
SignInView({required this.context, Key? key}) : super(key: key);
TextEditingController phonecontroller = TextEditingController();
TextEditingController otpcontroller = TextEditingController();
Auth auth = Auth();
SignInViewModel svm = SignInViewModel();
#override
Widget build(BuildContext context) {
return Scaffold(
body: buildbody,
);
}
Widget get buildbody {
double screenWidth = MediaQuery.of(context).size.width;
return Observer(builder: (_) {
return SizedBox(
width: screenWidth,
child: Padding(
padding: const EdgeInsets.all(8.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
"Welcome to Whatsapp Clone, Let's begin!",
style: TextStyle(color: Colors.green, fontSize: 20),
),
const SizedBox(height: 30),
Container(
padding: const EdgeInsets.all(8),
height: 80,
child: TextField(
controller: phonecontroller,
decoration: const InputDecoration(
border: OutlineInputBorder(borderSide: BorderSide()),
),
keyboardType: TextInputType.phone,
textInputAction: TextInputAction.done,
onSubmitted: (String value) {
if (phonecontroller.text != '') {
if (svm.otpVisibility) {
svm.otpvisiblty(context);
auth.verifyotp(context, otpcontroller.text);
} else {
print('otpvisible false');
auth.loginWithPhone(
context: context, phone: phonecontroller.text);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content:
Text('Please enter your phone number')));
}
},
)),
Visibility(
visible: svm.otpVisibility,
child: Container(
padding: const EdgeInsets.all(8),
height: 80,
child: TextField(
controller: otpcontroller,
decoration: const InputDecoration(
border: OutlineInputBorder(
borderSide: BorderSide(),
),
),
keyboardType: TextInputType.number,
maxLength: 6,
),
),
),
TextButton(
onPressed: () {
print(phonecontroller.text);
if (phonecontroller.text != '') {
if (svm.otpVisibility) {
svm.otpvisiblty(context);
auth.verifyotp(context, otpcontroller.text);
} else {
print('otpvisible false');
auth.loginWithPhone(
context: context, phone: phonecontroller.text);
}
} else {
ScaffoldMessenger.of(context).showSnackBar(const SnackBar(
content: Text('Please enter your phone number')));
}
},
child: const Text(
'Verify',
style: TextStyle(color: Colors.green),
),
)
],
),
),
);
});
}
}
I'm making an app using Flutter which calculates motor vehicle tax.
It calculates it perfectly fine when I enter the cost of vehicle.
But I want to add a validation to it. When I don't enter any cost of vehicle and keeps it empty and then click the calculate button, I want it show - please enter the cost.
How do I add this validation as this is not a form.
Here is the code of that part:
TextField(
controller: costController,
decoration: const InputDecoration(labelText: "Cost of Vehicle"),
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
),
const SizedBox(
height: 20,
),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Theme.of(context).primaryColor,
),
onPressed: () {
setState(() {
toPrint = calc(
dropDownValue!,
int.parse(costController.text),
).toString();
});
},
child: const Text("Calculate")),
const SizedBox(
height: 20,
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: Colors.lightGreenAccent[100],
border: const Border(
bottom: BorderSide(color: Colors.grey),
)),
child: Text("Tax : $toPrint "),
),
Wrap the column with a Form widget add avalidator to the textfield
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
#override
Widget build(BuildContext context) {
const appTitle = 'Form Validation Demo';
return MaterialApp(
title: appTitle,
home: Scaffold(
appBar: AppBar(
title: const Text(appTitle),
),
body: const MyCustomForm(),
),
);
}
}
// Create a Form widget.
class MyCustomForm extends StatefulWidget {
const MyCustomForm({super.key});
#override
MyCustomFormState createState() {
return MyCustomFormState();
}
}
// Create a corresponding State class.
// This class holds data related to the form.
class MyCustomFormState extends State<MyCustomForm> {
// Create a global key that uniquely identifies the Form widget
// and allows validation of the form.
//
// Note: This is a GlobalKey<FormState>,
// not a GlobalKey<MyCustomFormState>.
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
// Build a Form widget using the _formKey created above.
return Form(
key: _formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextField(
controller: costController,
decoration: const InputDecoration(labelText: "Cost of Vehicle"),
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
// The validator receives the text that the user has entered.
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter some text';
}
return null;
},
),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Theme.of(context).primaryColor,
),
onPressed: () {
if (_formKey.currentState!.validate()) {
setState(() {
toPrint = calc(
dropDownValue!, int.parse(costController.text),
).toString();
});
}
},
child: const Text("Calculate")),
const SizedBox(
height: 20,
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: Colors.lightGreenAccent[100],
border: const Border(
bottom: BorderSide(color: Colors.grey),
)),
child: Text("Tax : $toPrint "),
),
],
),
);
}
}
Use Form Widget and Convert TextField to TextFormField like that.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
class FormWidget extends StatefulWidget {
const FormWidget({Key? key}) : super(key: key);
#override
State<FormWidget> createState() => _FormWidgetState();
}
class _FormWidgetState extends State<FormWidget> {
final TextEditingController costController = TextEditingController();
final _formKey = GlobalKey<FormState>();
#override
Widget build(BuildContext context) {
return Scaffold(
key: _formKey,
body: Column(
children: [
Form(
child: TextFormField(
validator: (value) {
if (value.isEmpty) {
return "Please enter the cost.";
}
return null;
},
controller: costController,
decoration: const InputDecoration(labelText: "Cost of Vehicle"),
keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
FilteringTextInputFormatter.digitsOnly
],
),
),
const SizedBox(
height: 20,
),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: Theme.of(context).primaryColor,
),
onPressed: () {
if(_formKey.currentState.validate()){
//do your setState stuff
setState(() {
});
}
},
child: const Text("Calculate")),
const SizedBox(
height: 20,
),
Container(
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
decoration: BoxDecoration(
color: Colors.lightGreenAccent[100],
border: const Border(
bottom: BorderSide(color: Colors.grey),
)),
child: Text("Tax : "),
),
],
),
);
}
}
import 'package:flutter/material.dart';
import 'package:sumanthk07/utilities/routes.dart';
class LoginPage extends StatefulWidget {
const LoginPage({Key? key}) : super(key: key);
#override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final _formkey = GlobalKey<FormState>();
// ignore: avoid_types_as_parameter_names, non_constant_identifier_names
moveToHome(BuildContext) async{
Navigator.pushNamed(context, MyRoutes.homeRoute);
}
#override
Widget build(BuildContext context) {
return Material(
color: Colors.white,
child: SingleChildScrollView(
child: Form(
key: _formkey,
child: Column(
children: [
Image.asset("assets/images/login.png", fit: BoxFit.cover),
const SizedBox(
height: 20.0,
),
const Text(
'Welcome',
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(
height: 20.0,
),
Padding(
padding: const EdgeInsets.symmetric(
vertical: 16.0, horizontal: 32.0),
child: Column(
children: [
TextFormField(
decoration: const InputDecoration(
hintText: "Enter User name", labelText: "Username "),
initialValue: "",
validator: (String? value) {
if (value !=null && value.isEmpty ) {
return "User name cannot be empty";
}
return null;
},
onChanged: (value) {
setState(() {});
},
),
TextFormField(
obscureText: true,
decoration: const InputDecoration(
hintText: "Enter password", labelText: "Password "),
initialValue: "",
validator: (String? value) {
if (value !=null && value.isEmpty ) {
return "Password name cannot be empty";
}
return null;
},
),
const SizedBox(
height: 20.0,
),
InkWell(
onTap: () => moveToHome(context),
child: AnimatedContainer(
duration: const Duration(seconds: 1),
height: 40,
width: 80,
alignment: Alignment.center,
child: const Text("Login",
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.bold,
fontSize: 18,
)),
decoration: BoxDecoration(
color: Colors.red,
// ignore: unnecessary_const
borderRadius: BorderRadius.circular(20)),
),
)
// ElevatedButton(
// child: const Text("Login"),
// style: TextButton.styleFrom(),
// onPressed: () {
// // ignore: unused_local_variable
// var myRoutes = MyRoutes;
// Navigator.pushNamed(context, MyRoutes.homeRoute);
// },
// )
],
),
)
],
),
),
),
);
}
BorderRadius newMethod() => BorderRadius.circular(20);
}
Hi All, I'm a beginner to flutter and I'm trying to add validator to widget but I'm not getting the validation when I run the application.
I searched and tried the ways to do it but I didn't get the desired outcome.
Can you guys look into my code and suggest the right way.
no errors found but validation is not working.
First assign TextEditingController to your both fields.
final TextEditingController _controllerUserName = TextEditingController();
final TextEditingController _controllerPassword = TextEditingController();
And also assign autovalidateMode to your text field so you can validate at user input like this. It's not necessary it's optional but you can add it to validate your field on input field changes. Although you can validate your form at submission time.
TextFormField(
decoration: const InputDecoration(
hintText: "Enter User name", labelText: "Username "),
initialValue: "",
validator: (String? value) {
if (value !=null && value.isEmpty ) {
return "User name cannot be empty";
}
return null;
},
onChanged: (value) {
setState(() {});
},
autovalidate : AutovalidateMode.onUserInteraction,
controller:_controllerUserName
),
And also you have not validate your form at submission time. try this
moveToHome(BuildContext) async{
if (_formkey.currentState.validate()) {
Navigator.pushNamed(context, MyRoutes.homeRoute);
}
}
am trying to create an app whereby at the initial step, users log in and after keying a username and password, the list of users should be reflected in Firebase Authentication screen. I am able to log in with username but the list of users logged in is not reflected...
Please help!
This is my login page code:
import 'package:flutter/material.dart';
import 'package:firebase_auth/firebase_auth.dart';
class Login extends StatefulWidget {
#override
_LoginState createState() => _LoginState();
}
class _LoginState extends State<Login> {
final FirebaseAuth _auth = FirebaseAuth.instance;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
String _email, _password;
checkAuthentification() async {
_auth.authStateChanges().listen((user) {
if (user != null) {
print(user);
Navigator.pushReplacementNamed(context, "/");
}
});
}
#override
void initState() {
super.initState();
this.checkAuthentification();
}
login() async {
if (_formKey.currentState.validate()) {
_formKey.currentState.save();
try {
await _auth.signInWithEmailAndPassword(
email: _email, password: _password);
} catch (e) {
showError(e.message);
print(e);
}
}
}
showError(String errormessage) {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: Text('ERROR'),
content: Text(errormessage),
actions: <Widget>[
FlatButton(
onPressed: () {
Navigator.of(context).pop();
},
child: Text('OK'))
],
);
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: SingleChildScrollView(
child: Container(
child: Column(
children: <Widget>[
Container(
child: Form(
key: _formKey,
child: Column(
children: <Widget>[
SizedBox(height: 300),
Container(
child: TextFormField(
validator: (input) {
if (input.isEmpty) return 'Enter Email';
},
decoration: InputDecoration(
labelText: 'Email',
prefixIcon: Icon(Icons.email)),
onSaved: (input) => _email = input),
),
Container(
child: TextFormField(
validator: (input) {
if (input.length < 6)
return 'Provide Minimum 6 Characters';
},
decoration: InputDecoration(
labelText: 'Password',
prefixIcon: Icon(Icons.lock),
),
obscureText: true,
onSaved: (input) => _password = input),
),
SizedBox(height: 20),
RaisedButton(
padding: EdgeInsets.fromLTRB(70, 10, 70, 10),
onPressed: login,
child: Text('LOGIN',
style: TextStyle(
color: Colors.white,
fontSize: 20.0,
fontWeight: FontWeight.bold)),
color: Colors.orange,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20.0),
),
)
],
),
),
),
],
),
),
));
}
}
In my project their are two Screens.
First Screen contains a floating Action Button, onclick navigates to second screen having form and on submitting the form. A Map is created with Key and value as the fields in the form. And map is added to the list.
Now using the Navigator. pop function the list is passed to the first screen and On printing the list on the console, error message comes saying,
Error: The argument type 'List' can't be assigned to the parameter type 'String' in Flutter
Code:
First Screen:
class donateNow extends StatefulWidget{
#override
_donateNowState createState() => _donateNowState();
}
class _donateNowState extends State<donateNow>{
List medicineCart = [];
#override
Widget build(BuildContext context){
return Scaffold(
appBar: AppBar(
title: Text('Donate Medicine'),
),
body: Container(),
floatingActionButton: FloatingActionButton.extended(
icon:Icon(Icons.add),
label: Text('Add Medicine'),
backgroundColor: kPrimaryColor,
foregroundColor: Colors.white,
elevation: 10.0,
splashColor: kPrimaryLightColor,
onPressed: (){
_navigateAndDisplayMedicine(context);
}
),
);
}
_navigateAndDisplayMedicine(BuildContext context) async{
List<dynamic> newList = await Navigator.push(context,
MaterialPageRoute(builder: (context) => medicineDetails()),
);
print('Medicine:'+ newList.toString());
}
}
Second Screen:
class medicineDetails extends StatefulWidget {
#override
State<StatefulWidget> createState() => medicineDetailsState();
}
class medicineDetailsState extends State<medicineDetails> {
final _formKey = GlobalKey<FormState>();
List medicineCart = [];
TextEditingController medName = new TextEditingController();
TextEditingController qty = new TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Add Medicine"),
),
body: Padding(
padding: EdgeInsets.only(top: 15.0, left: 10.0, right: 10.0),
child: Form(
key: _formKey,
child: ListView(
children: [
TextFormField(
obscureText: false,
style: Theme.of(context).textTheme.title,
onChanged: (value) {
debugPrint('Something change');
},
controller: medName,
validator: (value){
if(value.isEmpty)
return kMedicineNameNullError;
},
decoration: InputDecoration(
labelText: 'Medicine Name with Strength',
labelStyle: Theme.of(context).textTheme.title,
hintText: 'Enter Medicine Name',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(5.0))),
),
Padding(
padding: EdgeInsets.only(top: 15.0, left: 10.0, bottom: 15.0),
child: TextFormField(
keyboardType: TextInputType.phone,
controller: qty,
validator: (value){
int val = int.parse(value);
if(value.isEmpty)
return kQuantityNullError;
else if(val<4){
return kMinimumQuantityError;
}
},
style: Theme.of(context).textTheme.title,
decoration: InputDecoration(
labelText: 'Quantity',
labelStyle: Theme.of(context).textTheme.title,
hintText: 'Enter the quantity',
border:
OutlineInputBorder(borderRadius: BorderRadius.circular(5.0))),
),
),
Padding(
padding: EdgeInsets.only(top: 15.0, bottom: 15.0),
child: Row(
children: <Widget>[
// SaveButton(),
Expanded(
child: RaisedButton(
color: Color(0xFF574FC4),
textColor: Theme.of(context).primaryColorLight,
child: Text(
'Save',
textScaleFactor: 2.0,
),
onPressed: () {
if(_formKey.currentState.validate()){
_formKey.currentState.save();
KeyboardUtil.hideKeyboard(context);
var map = {
'Medicine Name': medName.text,
'Date': expDate.text,
'Quantity Type': qtyType,
'Quantity': qty.text,
};
medicineCart.add(map);
Navigator.pop(context,medicineCart);
debugPrint('Save Clicked');
}
})),
Expanded(
child: RaisedButton(
color: Color(0xFF574FC4),
textColor: Theme.of(context).primaryColorLight,
child: Text(
'Delete',
textScaleFactor: 2.0,
),
onPressed: () {
setState(() {
debugPrint('Delete Clicked');
//print(medicineCart);
});
}))
],
),
)
],
),
),
),
);
}
}
The issue here is that you're trying to assign a List<dynamic> to a String variable - which seems to be done when you're trying to pass medicineCart on Navigator.pop(). A quick workaround here is to encode your List to String then decode it back again using jsonEncode() and jsonDecode().