Search code examples
flutterdartauthenticationdart-null-safety

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(),
  ),
);

Solution

  • 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(),
        ),
      );
    

    enter image description here