Flutter 值对象工厂构造函数在 Bloc 发出时执行两次

标签 flutter dart bloc

[更新]:我在这里创建了一个复制存储库:https://gitlab.com/dantec204/flutter-app-issue-reproduction

我正在学习 Flutter/Dart,到目前为止我真的很喜欢使用它,但今天我遇到了一个问题,我似乎无法理解它。

我开始使用 Bloc 进行用户身份验证。每当用户输入他的电子邮件地址时,都应该发出 EmailChanged 事件。到目前为止一切顺利,一切仍然正常。

但是对于电子邮件地址,我有这个值对象类:

class EmailAddress extends ValueObject<String> {
  @override
  final Either<Failure<String>, String> value;

  factory EmailAddress(String input) {
    return EmailAddress._(
      validateEmailAddress(input),
    );
  }

  const EmailAddress._(this.value);
}

Either<Failure<String>, String> validateEmailAddress(String input) {
  const emailRegex = r"""^[a-zA-Z0-9.!#$%&'*\/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?)*$""";

  if (RegExp(emailRegex).hasMatch(input)) {
    return right(input);
  } else {
    return left(Failure(input));
  }
}

问题是,无论何时,每当 bloc 事件被发出时,这个工厂构造函数都会被执行两次第一次它收到正确的输入值,但第二次输入值只是一个空白字符串。因此我的状态是不正确的。

这就是该 Bloc 的样子:

@injectable
class LoginBloc extends Bloc<LoginEvent, LoginState> {
  final IAuthFacade _authFacade;

  LoginBloc(this._authFacade): super(LoginState()) {
    on<LoginEmailChanged>((event, emit) async {
      emit(
        state.copyWith(emailAddress: EmailAddress(event.emailAddress))
      );
    });
  }
}

Bloc 国家:

class LoginState {
  final EmailAddress emailAddress;
  final Password password;
  final bool isSubmitting;

  LoginState({
    emailAddress,
    password,
    this.isSubmitting = false
  }) : emailAddress = EmailAddress(""),
       password = Password("");

  LoginState copyWith({
    EmailAddress? emailAddress,
    Password? password,
    bool? isSubmitting
  }) {
    return LoginState(
      emailAddress: emailAddress ?? this.emailAddress,
      password: password ?? this.password,
      isSubmitting: isSubmitting ?? this.isSubmitting,
    );
  }
}

这是 TextFormField 的 onChanged 事件:

onChanged: (value) =>
     context.read<LoginBloc>().add(LoginEmailChanged(emailAddress: value)),
abstract class LoginEvent {}

class LoginEmailChanged extends LoginEvent {
  final String emailAddress;

  LoginEmailChanged({ required this.emailAddress });
}

我真的希望有人能帮助我,因为我几乎一整天都在互联网上查找并思考这个问题......

最佳答案

感谢您提供项目,我可以使用它来查看您的问题。

我已经发现问题所在。您正在日志中查找文本 EmailAddress, input: 的工厂构造函数,并且当用户通过键入(例如)中的新字符来更改其电子邮件地址时,您会看到它在日志中打印两次文本字段。

您看到该消息两次打印到屏幕上的原因是:

首先您要发出此事件:

LoginBloc(this._authFacade): super(LoginState()) {
  on<LoginEmailChanged>((event, emit) async {
    emit(
      state.copyWith(emailAddress: EmailAddress(event.emailAddress))
    );
  });

它依次调用您所在州的 copyWith() 函数,如下所示:

LoginState copyWith({
  EmailAddress? emailAddress,
  Password? password,
  bool? isSubmitting,
  Option<Either<AuthFailure, Unit>>? authFailureOrSuccess
}) {
  return LoginState(
    emailAddress: emailAddress ?? this.emailAddress,
    password: password ?? this.password,
    isSubmitting: isSubmitting ?? this.isSubmitting,
    authFailureOrSuccess: authFailureOrSuccess ?? this.authFailureOrSuccess
  );
}

这是调用 LoginState() 构造函数,它本身创建 EmailAddress 的另一个副本,如下所示:

LoginState({
  emailAddress,
  password,
  this.isSubmitting = false,
  authFailureOrSuccess
}) : emailAddress = EmailAddress(""),
      password = Password(""),
      authFailureOrSuccess = none();

因此,即使您认为您正在调用 LoginState 并且应该只使用传入的 EmailAddress? 值,默认构造函数确实会创建 的另一个实例EmailAddress,然后使用您提供给它的电子邮件地址。

解决方案是实际修复您的 LoginState 构造函数,如下所示:

class LoginState {
  final EmailAddress emailAddress;
  final Password password;
  final bool isSubmitting;
  final Option<Either<AuthFailure, Unit>> authFailureOrSuccess;

  LoginState({
    required this.emailAddress,
    required this.password,
    required this.isSubmitting,
    required this.authFailureOrSuccess,
  });
  ...

然后您必须使用我们在您的代码库中的这行代码来解决问题:

LoginBloc(this._authFacade): super(LoginState())

由于 LoginState 不再具有默认状态,因此您需要像这样定义一个状态:

@injectable
class LoginBloc extends Bloc<LoginEvent, LoginState> {
  final IAuthFacade _authFacade;

  LoginBloc(this._authFacade)
      : super(LoginState(
          emailAddress: EmailAddress(''),
          password: Password(''),
          isSubmitting: false,
          authFailureOrSuccess: none(),
        )) {
    on<LoginEmailChanged>((event, emit) async {
      emit(state.copyWith(emailAddress: EmailAddress(event.emailAddress)));
    });
   ...

然后你就可以继续了!

关于Flutter 值对象工厂构造函数在 Bloc 发出时执行两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/71146711/

相关文章:

dart - Flutter:读取文件,解析为 JSON,然后转换为对象以使用点表示法

android - 在模拟器中启动 AVD 时显示此 "Unable to locate adb"

flutter - 为什么在Flutter Sink中添加数据不起作用?

flutter - 使用不包含 Bloc 类型的上下文调用的 BlocProvider.of()

flutter - 我需要在 Flutter 中重现一个 Floating Action Button,它可以转换成一个横跨整个屏幕的新表面

flutter - 异常:-RenderBox was not laid out despite removing the expanded widgets

flutter - onWillPop-无法将Future <bool> Function(BuildContext)分配给参数类型Future <bool> Function()

dart - 为什么这个 bloc getter 在 null 上被调用?

flutter - 抽象类不能被实例化!在身份验证 block 中

android - 在 Release模式下检查互联网连接不起作用