具有空安全性的 Flutter 表单验证

TextFormField validate 参数采用一个函数,如果字段内容有效则返回 null,如果内容无效则返回字符串。我的flutter项目中有 null 安全性,我无法从我的 validate 函数中返回 null。如何编写一个启用空安全的有效验证函数?

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
         },
      ),

这是 buildUser 输入代码


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,
        ),
      ),
    );
  }
}
stack overflow Flutter Form validation with null safety
原文答案
author avatar

接受的答案

我找到了解决方案,我将自定义验证函数的返回类型设置为可为空,这样我就可以返回 null。感谢你们。

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,
      ...

答案:

作者头像

问题不清楚。所以如果你想使用 Flutter Form()。您需要一个表单键 = GlobalKey()。如果你想在无效时得到警告,你不能返回''。当您的值无效时,它将有一个红色警告。

   Form(
      key: GlobalKey<FormState>()
      child: TextFormField(
        validator: (value) {
          if (value invalid) {
            return '';
          }
          return null;
        },
      ),
    )
作者头像

您可以按照此示例解决您的问题。假设这是我用于电子邮件验证的 TextFormField:-

          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))

这是我的电子邮件验证方法:-

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

要在返回 null 时修复错误,我们必须将 validateEmail(String value) 方法的返回类型设置为可为空。即 String?

作者头像

这是我挣扎的问题,所以我希望我能帮助一些人。将项目迁移到无效安全后,我遇到了这个错误:

错误:参数类型“字符串函数(字符串)”不能分配给参数类型'字符串?功能(字符串?)?

这意味着我的函数应返回一个无效的字符串,并且该参数也应无效。

因此,我声明了我的验证函数如下:

String? Function(String? val)? validator;

下面是我创建的函数的一个示例:

    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;
   // }

    }

对于试图弄清楚整个图片的人,我将发布完整的代码。

这是我创建的自定义文章:

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

这是验证器类:

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

这是我使用这些文本fefilds之一的方式

     EditText(hintText:"Name",fontSize: 12.sp,paddingLeft: 8,marginLeft:  10.h,marginRight: 10.h ,
textController: controller.nameController,validator: validateFirstName)