【问题标题】:Flutter formKey currentstate is null when pass to another widget传递给另一个小部件时,Flutter formKey currentstate为null
【发布时间】:2021-10-29 20:30:33
【问题描述】:

我正在尝试创建一个带有 disabled 属性的 Textbutton 小部件,如下所示:

class AppTextButton extends StatelessWidget {
  final String title;
  final void Function(BuildContext context) onPress;
  final EdgeInsetsGeometry margin;
  final EdgeInsetsGeometry padding;
  final double borderRadius;
  final Color backgroundColor;
  final Image? leadingIcon;
  final Image? trailingIcon;
  final TextStyle? textStyle;
  final bool disabled;

  AppTextButton(this.title, this.onPress,
      {this.margin = const EdgeInsets.all(0),
      this.padding = const EdgeInsets.all(12),
      this.borderRadius = 0,
      this.leadingIcon,
      this.trailingIcon,
      this.textStyle,
      this.disabled = false,
      this.backgroundColor = const Color(0xFFFFFFFF)});

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: margin,
      child: TextButton(
        style: ButtonStyle(
            shape: MaterialStateProperty.all<RoundedRectangleBorder>(
                RoundedRectangleBorder(
                    borderRadius: BorderRadius.circular(borderRadius))),
            backgroundColor: MaterialStateProperty.all(backgroundColor)),
        child: Row(
          children: [
            if (this.leadingIcon != null) ...[this.leadingIcon!],
            Expanded(
              child: Padding(
                padding: padding,
                child:
                    Text(title, textAlign: TextAlign.center, style: textStyle),
              ),
            ),
            if (this.trailingIcon != null) ...[this.trailingIcon!]
          ],
        ),
        onPressed: () => !disabled ? onPress(context) : null,
      ),
    );
  }
}

在我的屏幕上,我声明我的 formKey 和我的表单如下:

class LoginScreen extends AppBaseScreen {
  LoginScreen({Key? key}) : super(key: key);

  final _formKey = GlobalKey<FormState>();

@override
  Widget build(BuildContext context) {
              Form(
                  key: _formKey,
                  child: Obx(
                    () => AppTextInput(
                      "Please input passcode",
                      _passwordController,
                      borderRadius: 8,
                      fillColor: Color(0xFFF6F4F5),
                      keyboardType: TextInputType.number,
                      errorMessage: _c.errorLoginConfirm.value,
                      isObscure: true,
                      onChange: _onInputChange,
                      maxLength: 6,
                      margin: EdgeInsets.only(top: 12, left: 20, right: 20),
                      validator: (text) {
                        if (text != null && text.length > 0) {
                          if (text.length < 6) {
                            return "Passcode must have at least 6 digits";
                          }
                        }
                      },
                    ),
                  )),

我将在屏幕底部有一个按钮,我在禁用字段中传递!_formKey.currentState!.validate()

AppTextButton("Login", _onLogin,
                  margin: EdgeInsets.fromLTRB(24, 24, 24, 8),
                  backgroundColor: Color(0xFFFF353C),
                  disabled: !_formKey.currentState!.validate(),
                  textStyle: TextStyle(color: Colors.white),
                  borderRadius: 8),

但是,formKey.currentState 为 null,每次打开屏幕时都会抛出以下错误。 Null check operator used on a null value

我在这里做错了什么?提前谢谢!

【问题讨论】:

  • 您是否尝试过使用有状态小部件?由于您的 UI 需要根据表单验证状态进行更新,因此它不是无状态小部件。
  • 嗨@PeterKoltai 我使用来自https://pub.dev/packages/get的单独控制器和绑定

标签: flutter dart-null-safety flutter-form-builder


【解决方案1】:

传递前需要保存表单状态,

final FormState formState = _formKey.currentState;
formState.save();


onPressed: () {
                FocusScope.of(context).requestFocus(FocusNode());
                final FormState formState = _formKey.currentState;
                if (formState.validate()) {
                  formState.save();
                  onPress(context);
                }
              },

【讨论】:

  • 嗨 Sachin,我应该把上面两行放在哪里?目前,我将它们放在构建函数中。但是,当我尝试打印_formKey.currentState 时,结果为空。
【解决方案2】:

我认为问题是因为所有的小部件是同时创建的,所以_formKey.currentStateAppTextButton调用时仍然为空。

您需要创建一个单独的控制器来控制按钮的状态并将其添加到验证器中,如下所示:

validator: (text) {

                     if (text != null && text.length > 0) {
                        if (text.length < 6) {
                           buttonDisableController = true;
                           return "Passcode must have at least 6 digits";
                        }
                     }
                     buttonDisableController = false;
                     return null;
                  },

【讨论】:

    【解决方案3】:

    在您的情况下,您应该知道小部件的构建过程(假设您有Botton 小部件和Input 小部件):

    1. BottonInput 正在构建初始状态。这两种状态都还没有准备好阅读和使用
    2. BottonInput 已构建。状态已准备就绪。
    3. 用户与Input 交互。如果值通过验证器,Input 必须调用 Button 重建其状态
    4. Botton重建。

    对于这个过程,你应该改变你的代码:

    1. 获取和修改Button内部Input的状态
    2. 通知Button重建

    有很多方法可以处理小部件之间的state management。我只是把AppTextButton改成Statefultwidget就可以实现了。

    ...
    final _buttonKey = GlobalKey<_AppTextButtonState>();
    ...
      AppTextButton(key: _buttonKey)
    ...
    
    
    class AppTextButton extends StatefulWidget {
      final bool initDisable;
    
      AppTextButton({
        this.initDisable = false,
        Key? key,
      }) : super(key: key);
    
      @override
      _AppTextButtonState createState() => _AppTextButtonState();
    }
    
    class _AppTextButtonState extends State<AppTextButton> {
      var disable;
    
      @override
      void initState() {
        disable = widget.initDisable;
        super.initState();
      }
    
      @override
      Widget build(BuildContext context) {
        return TextButton(child: Text('Button'), onPressed: disable ? null : () {});
      }
    
      void enableButton() {
        setState(() {
          disable = false;
        });
      }
    
      void disableButton() {
        setState(() {
          disable = true;
        });
      }
    }
    
    
    class LoginScreen extends StatelessWidget {
      LoginScreen({Key? key}) : super(key: key);
    
      final _formKey = GlobalKey<FormState>();
    
      @override
      Widget build(BuildContext context) {
        return Form(
          key: _formKey,
          child: TextFormField(
            autovalidateMode: AutovalidateMode.onUserInteraction,
            validator: (text) {
              if (text != null && text.length > 0) {
                if (text.length < 6) {
                  return "Passcode must have at least 6 digits";
                }
              }
            },
            onChanged: (v) {
              if (_formKey.currentState?.validate() ?? false) {
                _buttonKey.currentState?.enableButton();
              } else {
                _buttonKey.currentState?.disableButton();
              }
            },
          ),
        );
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-02-21
      • 1970-01-01
      • 1970-01-01
      • 2021-07-02
      • 2021-08-06
      • 1970-01-01
      • 1970-01-01
      • 2020-08-09
      相关资源
      最近更新 更多