flutter form validator with null safety
i got a problem while trying to make a form validator on my login screen it just doesn t work when i tape login and the text field is already null. here s my code if u have any help, i m down to : TextFormField validate parameter takes a function that returns null if the content of the field is valid, or a string if the content is invalid. I have null safety in my flutter project and I can't return null from my validate function. How can I write a working validate function with null safety on?
Login code screen :
import 'package:flutter/material.dart';
import 'package:flutter_udemy/shared/components/components.dart';
class LoginScreen extends StatefulWidget {
LoginScreen({Key? key}) : super(key: key);
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
var emailController = TextEditingController();
var passwordController = TextEditingController();
var formKey = GlobalKey<FormState>();
bool isPassword = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Center(
child: SingleChildScrollView(
child: Form(
key: formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Login',
style: TextStyle(
fontSize: 40.0,
fontWeight: FontWeight.bold,
),
),
SizedBox(
height: 40.0,
),
defaultFormField(
controller: emailController,
label: 'Email',
prefix: Icons.email,
type: TextInputType.emailAddress,
validate: (String value) {
if (value.isEmpty) {
return 'email must not be empty';
}
return null;
},
),
SizedBox(
height: 15.0,
),
defaultFormField(
controller: passwordController,
label: 'Password',
prefix: Icons.lock,
suffix:
isPassword ? Icons.visibility : Icons.visibility_off,
isPassword: isPassword,
suffixPressed: () {
setState(() {
isPassword = !isPassword;
});
},
type: TextInputType.visiblePassword,
validate: (String value) {
if (value.isEmpty) {
return 'password is too short';
}
return null;
},
),
SizedBox(
height: 20.0,
),
defaultButton(
text: 'login',
function: () {
if (formKey.currentState!.validate()) {
print(emailController.text);
print(passwordController.text);
}
},
),
SizedBox(
height: 20.0,
),
defaultButton(
text: 'ReGIster',
function: () {
print(emailController.text);
print(passwordController.text);
},
),
SizedBox(
height: 10.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Don\'t have an account?',
),
TextButton(
onPressed: () {},
child: Text(
'Register Now',
),
),
],
),
],
),
),
),
),
),
);
}
}
componenents code screen where i got the button widget and text field widget:
import 'package:flutter/material.dart';
Widget defaultButton({
double width = double.infinity,
Color background = Colors.blue,
bool isUpperCase = true,
double radius = 10.0,
required Function function,
required String text,
}) =>
Container(
width: width,
height: 50.0,
child: MaterialButton(
onPressed: () {
function();
},
child: Text(
isUpperCase ? text.toUpperCase() : text,
style: TextStyle(
color: Colors.white,
),
),
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
radius,
),
color: background,
),
);
Widget defaultFormField({
required TextEditingController controller,
required TextInputType type,
Function? onSubmit,
Function? onChange,
bool isPassword = false,
required Function validate,
required String label,
required IconData prefix,
IconData? suffix,
Function? suffixPressed,
}) =>
TextFormField(
controller: controller,
keyboardType: type,
obscureText: isPassword,
onFieldSubmitted: (s) {
onSubmit!(s);
},
onChanged: (s) {
onChange!(s);
},
validator: (s) {
validate(s);
},
decoration: InputDecoration(
labelText: label,
prefixIcon: Icon(
prefix,
),
suffixIcon: suffix != null
? IconButton(
onPressed: () {
suffixPressed!();
},
icon: Icon(
suffix,
),
)
: null,
border: OutlineInputBorder(),
),
);
This code works
Main changes done for null safety
required String? Function(String?)? validate
validate: (String? value) {
if (value!.isEmpty)
{
return 'email must not be empty';
}
return null;
},
Full code below
class LoginScreen extends StatefulWidget {
LoginScreen({Key? key}) : super(key: key);
@override
_LoginScreenState createState() => _LoginScreenState();
}
class _LoginScreenState extends State<LoginScreen> {
var emailController = TextEditingController();
var passwordController = TextEditingController();
final formKey = GlobalKey<FormState>();
bool isPassword = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: Padding(
padding: const EdgeInsets.all(20.0),
child: Center(
child: SingleChildScrollView(
child: Form(
key: formKey,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Login',
style: TextStyle(
fontSize: 40.0,
fontWeight: FontWeight.bold,
),
),
SizedBox(
height: 40.0,
),
defaultFormField(
controller: emailController,
label: 'Email',
prefix: Icons.email,
type: TextInputType.emailAddress,
validate: (String? value) {
if (value!.isEmpty) {
return 'email must not be empty';
}
return null;
},
),
SizedBox(
height: 15.0,
),
defaultFormField(
controller: passwordController,
label: 'Password',
prefix: Icons.lock,
suffix:
isPassword ? Icons.visibility : Icons.visibility_off,
isPassword: isPassword,
suffixPressed: () {
setState(() {
isPassword = !isPassword;
});
},
type: TextInputType.visiblePassword,
validate: (String? value) {
if (value!.isEmpty) {
return 'password is too short';
}
return null;
},
),
SizedBox(
height: 20.0,
),
defaultButton(
text: 'login',
function: () {
if (formKey.currentState!.validate()) {
print(emailController.text);
print(passwordController.text);
}
},
),
SizedBox(
height: 20.0,
),
defaultButton(
text: 'ReGIster',
function: () {
print(emailController.text);
print(passwordController.text);
},
),
SizedBox(
height: 10.0,
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
'Don\'t have an account?',
),
TextButton(
onPressed: () {},
child: Text(
'Register Now',
),
),
],
),
],
),
),
),
),
),
);
}
Widget defaultButton({
double width = double.infinity,
Color background = Colors.blue,
bool isUpperCase = true,
double radius = 10.0,
required Function function,
required String text,
}) =>
Container(
width: width,
height: 50.0,
child: MaterialButton(
onPressed: () {
function();
},
child: Text(
isUpperCase ? text.toUpperCase() : text,
style: TextStyle(
color: Colors.white,
),
),
),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(
radius,
),
color: background,
),
);
}
Widget defaultFormField({
required TextEditingController controller,
required TextInputType type,
Function? onSubmit,
Function? onChange,
bool isPassword = false,
required String? Function(String?)? validate,
required String label,
required IconData prefix,
IconData? suffix,
Function? suffixPressed,
}) =>
TextFormField(
controller: controller,
keyboardType: type,
obscureText: isPassword,
onFieldSubmitted: (s) {
onSubmit!(s);
},
onChanged: (s) {
onChange!(s);
},
validator: validate,
decoration: InputDecoration(
labelText: label,
prefixIcon: Icon(
prefix,
),
suffixIcon: suffix != null
? IconButton(
onPressed: () {
suffixPressed!();
},
icon: Icon(
suffix,
),
)
: null,
border: OutlineInputBorder(),
),
);