FLUTTER MOBX:构建函数返回空值。导致错误的相关小部件是 Observer

FLUTTER MOBX: A build function returned null. The relevant error-causing widget was Observer

我正在尝试使用 MOBX 在电子邮件和密码这两个字段中进行验证,并且我正在 [=29] 中计算两个函数的结果=]compuntig 调用了 formIsValid,但是 mobX 向我返回了这个错误:A build function returned null. 相关的导致错误的小部件是 观察者

我尝试以不同的方式做到这一点,但我做不到,而且我的电子邮件观察值 emailErrorLabelpasswordErrorLabel 不是影响 TextTormField 错误文本。

这是我的代码 ViewModel:

import 'package:covid_app/app/service/firebase/firebase_auth.dart';
import 'package:covid_app/app/service/firebase/firebase_auth_impl.dart';
import 'package:covid_app/app/ui/home/home_page.dart';
import 'package:flutter/material.dart';
import 'package:mobx/mobx.dart';

part 'login_viewmodel.g.dart';

class LoginViewModel = LoginViewModelBase with _$LoginViewModel;

abstract class LoginViewModelBase with Store {
  @observable
  String email = "";

  @observable
  String password = "";

  @observable
  bool error = false;

  @observable
  bool emailErrorLabel = false;

  @observable
  bool passwordErrorLabel = false;

  final _auth = Auth();

  @action
  changeEmail(String newEmail) => email = newEmail;

  @action
  changePassword(String newPassword) => password = newPassword;

  @action
  setHasErrorOnEmail(bool value) => emailErrorLabel = value;

  @action
  setHasErrorOnPassword(bool value) => passwordErrorLabel = value;

  bool emailIsValid() {
    if (email.isNotEmpty && email.contains("@")) {
      return true;
    } else {
      setHasErrorOnEmail(true);
      return false;
    }
  }

  bool passwordIsValid() {
    if (password.isNotEmpty || password.length >= 8) {
      return true;
    } else {
      setHasErrorOnPassword(true);
      return false;
    }
  }

  @computed
  bool get formIsValid {
    return emailIsValid() && passwordIsValid();
  }

  @action
  Future<void> firebaseLogin(dynamic context) async {
    try {
      if (email.isNotEmpty && password.isNotEmpty) {
        var userId;
        await _auth.signIn(email, password).then((value) => userId = value);
        userId.length > 0 ? homeNavigator(context) : error = true;
      } else {
        error = true;
      }
    } catch (Exception) {
      error = true;
      print("Login Error: $Exception");
    }
  }

  void homeNavigator(context) {
    Navigator.push(
        context, MaterialPageRoute(builder: (context) => HomePage()));
  }
}

我的登录页面:

import 'package:covid_app/app/ui/login/login_viewmodel.dart';
import 'package:covid_app/app/widgets/KeyboardHideable.dart';
import 'package:covid_app/core/constants/colors.dart';
import 'package:covid_app/core/constants/dimens.dart';
import 'package:covid_app/core/constants/string.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';
import '../../widgets/button_component.dart';
import 'widgets/text_form_field_component.dart';

class LoginPage extends StatefulWidget {
  @override
  _LoginPageState createState() => _LoginPageState();
}

class _LoginPageState extends State<LoginPage> {
  var vm = LoginViewModel();
  var value = zero;
  var valueTextFields = sixtyEight;
  TextEditingController controllerEmail = TextEditingController();
  TextEditingController controllerPassword = TextEditingController();

  void animatedTest() async {
    Future.delayed(Duration(seconds: 0), () {
      setState(() {
        value = sixtyEight;
        valueTextFields = zero;
      });
    });
  }

  @override
  void initState() {
    super.initState();
    animatedTest();
  }

  @override
  Widget build(BuildContext context) {
    return KeyboardHideable(
      child: Scaffold(
        backgroundColor: darkPrimaryColor,
        body: SingleChildScrollView(
          child: Container(
            height: MediaQuery.of(context).size.height,
            child: SafeArea(
              child: Center(
                child: Padding(
                  padding: const EdgeInsets.all(sixteen),
                  child: Column(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: <Widget>[
                      Expanded(
                        child: Card(
                          elevation: twelve,
                          shape: RoundedRectangleBorder(
                            borderRadius:
                                BorderRadius.all(Radius.circular(twentyFour)),
                          ),
                          child: Padding(
                            padding: const EdgeInsets.all(thirtyTwo),
                            child: Column(
                              mainAxisAlignment: MainAxisAlignment.center,
                              children: <Widget>[
                                Spacer(),
                                Expanded(
                                  flex: 8,
                                  child: AnimatedContainer(
                                      margin: EdgeInsets.only(bottom: value),
                                      duration: Duration(seconds: 1),
                                      child: Image.asset(
                                          "assets/images/logo_covid_app.png")),
                                ),
                                Expanded(
                                  flex: 7,
                                  child: AnimatedContainer(
                                    margin:
                                        EdgeInsets.only(top: valueTextFields),
                                    duration: Duration(seconds: 1),
                                    child: Column(
                                      children: <Widget>[
                                        Expanded(
                                          flex: 2,
                                          child: Observer(
                                            builder: (_) =>
                                                TextFormFieldComponent(
                                                    emailHintText,
                                                    false,
                                                    controllerEmail,
                                                    vm.changeEmail,
                                                    vm.emailErrorLabel,
                                                    emailErrorLabel),
                                          ),
                                        ),
                                        Expanded(
                                          flex: 2,
                                          child: Observer(builder: (_) {
                                            return TextFormFieldComponent(
                                                passwordHintText,
                                                true,
                                                controllerPassword,
                                                vm.changePassword,
                                                vm.emailErrorLabel,
                                                passwordErrorLabel);
                                          }),
                                        ),
                                      ],
                                    ),
                                  ),
                                ),
                                SizedBox(
                                  height: twentyEight,
                                ),
                                Expanded(
                                  flex: 2,
                                  child: Observer(
                                    builder: (_) => ButtonComponent(
                                      title: loginButtonLabel,
                                      fillColor: rosePrimaryColor,
                                      textColor: Colors.white,
                                      loginFun: vm.formIsValid
                                          ? () => vm.firebaseLogin(context)
                                          : null,
                                    ),
                                  ),
                                ),
                                SizedBox(
                                  height: twenty,
                                ),
                                Expanded(
                                  flex: 2,
                                  child: ButtonComponent(
                                      title: registerButtonLabel,
                                      fillColor: darkPrimaryColor,
                                      textColor: Colors.white,
                                      loginFun: () {}),
                                ),
                                Spacer()
                              ],
                            ),
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

我的 TextFormField 组件:

import 'package:covid_app/core/constants/colors.dart';
import 'package:covid_app/core/constants/dimens.dart';
import 'package:flutter/material.dart';

// ignore: must_be_immutable
class TextFormFieldComponent extends StatefulWidget {
  String hintText;
  bool hideText;
  TextEditingController genericControler;
  bool genericValidation;
  String errorMessage;
  Function onChangedGeneric;

  TextFormFieldComponent(this.hintText, this.hideText, this.genericControler,
      this.onChangedGeneric, this.genericValidation, this.errorMessage);

  @override
  _TextFormFieldComponentState createState() => _TextFormFieldComponentState();
}

class _TextFormFieldComponentState extends State<TextFormFieldComponent> {
  @override
  Widget build(BuildContext context) {
    return Theme(
      data:
          ThemeData(cursorColor: rosePrimaryColor, hintColor: darkPrimaryColor),
      child: TextFormField(
        onChanged: widget.onChangedGeneric,
        controller: widget.genericControler,
        obscureText: widget.hideText,
        decoration: InputDecoration(
          hintText: widget.hintText,
          errorText: widget.genericValidation == true ? widget.errorMessage : null,
          border: OutlineInputBorder(
            borderRadius: BorderRadius.all(Radius.circular(twentyFour)),
          ),
          enabledBorder: OutlineInputBorder(
            borderRadius: BorderRadius.all(Radius.circular(twentyFour)),
            borderSide: BorderSide(width: two, color: darkPrimaryColor),
          ),
          focusedBorder: OutlineInputBorder(
            borderRadius: BorderRadius.all(Radius.circular(twentyFour)),
            borderSide: BorderSide(
              width: two,
              color: rosePrimaryColor,
            ),
          ),
        ),
      ),
    );
  }
}

我的按钮组件:

import 'package:covid_app/core/constants/colors.dart';
import 'package:covid_app/core/constants/dimens.dart';
import 'package:flutter/material.dart';

// ignore: must_be_immutable
class ButtonComponent extends StatefulWidget {
  var title;
  var fillColor;
  var textColor;
  Function loginFun;
  ButtonComponent({Key key, this.title, this.fillColor, this.textColor, this.loginFun});

  @override
  _ButtonComponentState createState() => _ButtonComponentState();
}

class _ButtonComponentState extends State<ButtonComponent> {
  @override
  Widget build(BuildContext context) {
    return Container(
      width: hundredSeventyTwo,
      height: fortyFour,
      child: RaisedButton(
        disabledColor: Colors.grey,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(
            twentyFour,
          ),
        ),
        onPressed: widget.loginFun,
        color: widget.fillColor,
        child: Text(
          widget.title,
          style: TextStyle(
            color: widget.textColor,
          ),
        ),
      ),
    );
  }
}

打印错误:https://i.stack.imgur.com/UmQd5.png / https://i.stack.imgur.com/K13rN.png

好吧,错误说明了一切,真的没什么可补充的。

formIsValid 计算中你调用了 2 个函数,这些函数反过来可能会修改 emailErrorLabelpasswordErrorLabel 并且,因为它们都 observable 并且在同一个渲染中使用,这是不允许的。

computed 应该是没有副作用的纯函数,它应该只是从其他 computedobservable 或常量值中导出一些值。