1

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?

AppWidget.buildUserInput(
     hint: 'Referral code',
     borderRadius: BorderRadius.circular(5),
     prefixIcon: Icon(Icons.local_offer_outlined),
     onSaved: (val) => model.save(
     CreateAccountViewModel.REFERRAL_CODE, val!),
     validate: (val) {
         return null; // The return type 'Null' isn't a 'String', as required by the 
                       //closure's context
         },
      ),

This is buildUser input code


static Widget buildUserInput(
      {String? hint,
      String? labelText,
      TextAlign textAlign = TextAlign.start,
      required Function(String? val) onSaved,
      Function(String val)? onChanged,
      required String Function(String val) validate,
      TextInputType? keyboardType,
      BorderRadius borderRadius = BorderRadius.zero,
      TextEditingController? controller,
      String? initialValue,
      Widget? suffixIcon,
      Widget? prefixIcon,
      String? prefixText,
      bool hasBorder = true,
      Widget? prefix,
      bool filled = false,
      Color? fillColor,
      Color? enabledBorderColor,
      Color? focusedBorderColor,
      TextStyle? style,
      bool obscureText = false}) {
    return Container(
      margin: EdgeInsets.symmetric(vertical: 9),
      child: TextFormField(
        initialValue: initialValue,
        controller: controller,
        onSaved: onSaved,
        onChanged: onChanged,
        validator: validate,
        keyboardType: keyboardType,
        obscureText: obscureText,
        textAlign: textAlign,
        style: style ?? TextStyle(),
        decoration: InputDecoration(
          filled: filled,
          fillColor: fillColor,
          prefixText: prefixText, prefix: prefix,
          prefixStyle: TextStyle(color: AppColors.appGreen),
          suffixIcon: suffixIcon,
          prefixIcon: prefixIcon,
          hintText: hint,
          labelText: labelText,
          labelStyle: userInputStyle(),
          hintStyle: userInputStyle(),
          // filled: true,
          // fillColor: AppColors.textColor2.withOpacity(.05),
          border: hasBorder
              ? borderRadius != null
                  ? OutlineInputBorder(
                      borderRadius: borderRadius,
                      borderSide: BorderSide(
                          style: BorderStyle.solid,
                          color: enabledBorderColor ?? AppColors.appGreen,
                          width: 1))
                  : UnderlineInputBorder(
                      borderRadius: borderRadius,
                      borderSide: BorderSide(
                          style: BorderStyle.solid,
                          color: enabledBorderColor ?? AppColors.appGreen,
                          width: 1))
              : InputBorder.none,
          focusedBorder: hasBorder
              ? borderRadius != null
                  ? OutlineInputBorder(
                      borderRadius: borderRadius,
                      borderSide: BorderSide(
                          style: BorderStyle.solid,
                          color: focusedBorderColor ?? AppColors.appGreen,
                          width: 1))
                  : UnderlineInputBorder(
                      borderRadius: borderRadius,
                      borderSide: BorderSide(
                          style: BorderStyle.solid,
                          color: focusedBorderColor ?? AppColors.appGreen,
                          width: 1))
              : InputBorder.none,
          enabledBorder: hasBorder
              ? borderRadius != null
                  ? OutlineInputBorder(
                      borderRadius: borderRadius,
                      borderSide: BorderSide(
                          style: BorderStyle.solid,
                          color: enabledBorderColor ?? AppColors.appGreen,
                          width: 1))
                  : UnderlineInputBorder(
                      borderRadius: borderRadius,
                      borderSide: BorderSide(
                          style: BorderStyle.solid,
                          color: enabledBorderColor ?? AppColors.appGreen,
                          width: 1))
              : InputBorder.none,
        ),
      ),
    );
  }
}
Evi
  • 41
  • 1
  • 8
  • What is `AppWidget.buildUserInput`? Its `validate` parameter should be fixed to match `TextFormField`'s `validator`. – jamesdlin Jun 26 '21 at 08:30
  • AppWidget.buildUserInput is a custom widget that wraps TextFormField and exposes the same parameters – Evi Jul 09 '21 at 06:25
  • As I mentioned, fix `AppWidget.buildUserInput`. Currently its `validate` parameter does not match the type of `TextFormField`'s `validator`. – jamesdlin Jul 09 '21 at 06:55

4 Answers4

3

This is a question i struggled with so I hope I can help some people out. After I migrated my project to null safety I was getting this error :

Error: The argument type 'String Function(String)' can't be assigned to the parameter type 'String? Function(String?)?'

It means my function should return a nullable String and the parameter should also be nullable.

so I declared my validator function as follows :

String? Function(String? val)? validator;

And below is an example of function I created :

    String? validateFirstName(String? value) {
      if (!value!.isAlphabet() || value.length > 10) {
        return 'Please enter a valid First Name';
      }
    // This code no longer needed 
    // else{
   //   return null;
   // }

    }

And for people who are trying to figure out the whole picture I'll post the full code.

Here is the Custom TextFeild I created :

import './app/components/text_view.dart';
import './app/utils/constants.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

class EditText extends StatelessWidget {
  // final EditTextStyle editTextStyle;
  final String hintText;
  final Color colors;
  final FontWeight fontWeight;
  final double? fontSize;
  final TextAlign textAlign;
  final int maxLines;
  final double lineSpace;
  final double marginLeft,marginRight,marginTop,marginBottom;
  final double paddingLeft,paddingRight,paddingTop,paddingBottom;
  final TextDecoration decoration;
  String? Function(String? val)? validator;
  String? error;
  FocusNode focusNode =  FocusNode();
  TextEditingController? textController;

  EditText({
    this.colors = const Color(0xffA9A9B4),
    this.fontWeight = FontWeight.w600,
    this.fontSize,this.error,
    this.marginTop =0.0,this.marginRight = 0.0,this.marginLeft = 0.0,this.marginBottom =0.0,
    this.paddingTop =0.0,this.paddingRight = 0.0,this.paddingLeft = 0.0,this.paddingBottom =0.0,
    this.textAlign = TextAlign.start,this.validator,
    this.decoration = TextDecoration.none,this.maxLines = 1,this.lineSpace = 0.5, required this.hintText, this.textController
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 54.h,
      margin: EdgeInsets.only(left: marginLeft,right: marginRight,top: marginTop,bottom: marginBottom),
      child: TextFormField(
        textAlign: textAlign,
        maxLines: maxLines,
        focusNode: focusNode,
        controller: textController,
        validator: validator,
        decoration: InputDecoration(
            errorText: error,
            labelStyle: TextStyle(color: Colors.redAccent),
            focusedBorder: OutlineInputBorder(borderSide: BorderSide(width: 1,color: secondaryColor),
              borderRadius: BorderRadius.circular(10.sp),),
            // border: OutlineInputBorder(borderSide: BorderSide(width: 1,color: Colors.red),
            //     borderRadius: BorderRadius.circular(10.sp),
            // ) ,
            enabledBorder: OutlineInputBorder(borderSide: BorderSide(width: 1.5,color: Color(0xffA9A9B4).withOpacity(0.5)),
              borderRadius: BorderRadius.circular(10.sp),
            ),
            label: TextView(text: hintText,colors: focusNode.hasFocus ? secondaryColor : colors,fontSize: 16.sp,textViewStyle: TextViewStyle.THIN,)
        ),
        // overflow: TextOverflow.ellipsis,
        style: FontStyles.montserratSemiBold.copyWith(
            color: colors,
            fontWeight: FontWeight.w600,
            fontSize: fontSize ?? 16.sp,
            decoration: decoration,letterSpacing: lineSpace
        ),
      ),
    );
  }
}

Here is the validator class :

import 'package:regexpattern/regexpattern.dart';

String? validateEmail(String? value) {
  // Pattern pattern =
  //     r"^[a-zA-Z0-9.a-zA-Z0-9.!#$%&'*+-/=?^_`{|}~]+@[a-zA-Z0-9]+\.[a-zA-Z]+";
  // RegExp regex = new RegExp(pattern);
  if (!value!.isEmail()) {
    return 'Please enter a valid Email';
  }
}

String? validateLoginPassword(String? value) {
  if(value!.length < 6){
    return 'Invalid password';
  }
  else {
    return null;
  }
}

String? validatePassword(String? value) {
  if(value!.length < 6){
    return 'Invalid password, length must be more than 6';
  }

}


String? validateFirstName(String? value) {
  if (!value!.isAlphabet() || value.length > 10) {
    return 'Please enter a valid First Name';
  }
}

String? validateLastName(String value) {
  if (!value.isAlphabet() || value.length > 10) {
    return 'Please enter a valid Last Name';
  } else {
    return null;
  }
}

String? validateMobileNumber(String? value) {
  // Pattern pattern =
  //     r'^{0-9}$';
  // RegExp regex = new RegExp(pattern);
  // if(value.length <7 || value.length > 14){
  //   return 'Invalid Number';
  // }
  // else
    if(value!.length <7 || value.length > 14){
        return 'Invalid phone Number';
    }
  }


String? validateOTP(String? value) {
  if (value!.length < 4 ) {
    return 'Please enter a valid OTP';
  }
}
//Please enter a valid Mobile No

Here is how I uses one of these textFeilds

     EditText(hintText:"Name",fontSize: 12.sp,paddingLeft: 8,marginLeft:  10.h,marginRight: 10.h ,
textController: controller.nameController,validator: validateFirstName)
Jeslin Jacob
  • 570
  • 6
  • 8
2

I found the solution, I made the return type of my custom validation function nullable, so that I can return null. Thank you guys.

static Widget buildUserInput(
      {String? hint,
      String? labelText,
      TextAlign textAlign = TextAlign.start,
      required Function(String? val) onSaved,
      Function(String val)? onChanged,
      required String? Function(String? val)? validate,
      TextInputType? keyboardType,
      BorderRadius borderRadius = BorderRadius.zero,
      TextEditingController? controller,
      String? initialValue,
      Widget? suffixIcon,
      ...
Evi
  • 41
  • 1
  • 8
-1

The question is not clear. So if you want to use Flutter Form(). You need a form key = GlobalKey(). And if you want to get a warning when having an invalid You cant return ''. It will have a red warning when your value invalid.

   Form(
      key: GlobalKey<FormState>()
      child: TextFormField(
        validator: (value) {
          if (value invalid) {
            return '';
          }
          return null;
        },
      ),
    )
kietle
  • 153
  • 6
-1

You can follow this example to solve your issue. Suppose this is my TextFormField for Email Validation:-

          TextFormField(
          decoration: InputDecoration(
              labelText: 'EMAIL',
              labelStyle: TextStyle(
                  fontFamily: 'Trueno',
                  fontSize: 12.0,
                  color: Colors.grey.withOpacity(0.5)),
              focusedBorder: UnderlineInputBorder(
                borderSide: BorderSide(color: greenColor),
              )),
          onChanged: (value) {
            this.email = value;
          },
          validator: (value) =>
              value!.isEmpty ? 'Email is required' : validateEmail(value))

And this is my email validation method:-

  String? validateEmail(String value) {
RegExp regex = RegExp(some regex pattern);
if (!regex.hasMatch(value))
  return 'Enter Valid Email';
else
  return null; }

To fix the error while returning null we have to make the return type of validateEmail(String value) method to nullable .i.e String?