Проверка электронной почты не обновляется при изменении текстового поля электронной почты - PullRequest
1 голос
/ 23 февраля 2020

Итак, вот соответствующая часть моего виджета формы.

// email
        section = Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            // heading section
            Container(
              width: _formSectionHeadingSize.width,
              height: _formSectionHeadingSize.height,
              child: Center(
                child: Text(
                  "Your Email Address",
                  style: Theme.of(context).textTheme.headline,
                ),
              ),
            ),
            // description
            Container(
              width: _formSectionDescriptionSize.width,
              height: _formSectionDescriptionSize.height,
              padding: const EdgeInsets.all(_sectionPadding),
              child: Text(
                "Your email address will be used to send you purchase receipts, updates, and other information.",
                style: Theme.of(context).textTheme.body1,
              ),
            ),
            // body
            Container(
              width: _formSectionBodySize.width,
              height: _formSectionBodySize.height,
              padding: const EdgeInsets.all(_sectionPadding),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  TextFormField(
                    controller: _email,
                    focusNode: _emailFocus,
                    textInputAction: TextInputAction.done,
                    keyboardType: TextInputType.emailAddress,
                    onChanged: (value) {
                      BlocProvider.of<RegistrationFormBloc>(context)
                          .add(EmailChanged(email: value));
                    },
                    onFieldSubmitted: (value) {
                      FocusScope.of(context).unfocus();
                      BlocProvider.of<RegistrationFormBloc>(context)
                          .add(EmailChanged(email: value));
                    },
                    decoration: InputDecoration(
                      labelText: "Email Address",
                      labelStyle: Theme.of(context).textTheme.body1,
                      hintText: "example@example.com",
                      errorText: currentState.emailError,
                    ),
                  ),
                ],
              ),
            ),

            // navigation
            Container(
              width: _bottomNavigationSize.width,
              height: _bottomNavigationSize.height,
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  FlatButton(
                    child: Text(
                      "Back",
                      style: Theme.of(context).textTheme.body1,
                    ),
                    onPressed: this._showPreviousSection,
                  ),
                  FlatButton(
                    child: Text(
                      "Next",
                      style: Theme.of(context).textTheme.body1,
                    ),
                    onPressed: currentState.emailFieldsAreValid()
                        ? this._showNextSection
                        : null,
                  ),
                ],
              ),
            ),
          ],
        );

Каждый раз, когда меняется поле электронной почты, я добавляю событие EmailChanged в мой RegistrationFormBlo c для проверки значения. Вот Blo c Logi c.

@override
  Stream<RegistrationState> mapEventToState(RegistrationEvent event) async* {
    // The first name was updated.
    if (event is FirstNameChanged) {
      yield await this._onFirstNameChanged(event);
    }

    // Last name was changed
    if (event is LastNameChanged) {
      yield await this._onLastNameChanged(event);
    }

    // email was changed
    if (event is EmailChanged) {
      yield await this._onEmailChanged(event);
    }

    // Password was changed
    if (event is PasswordChanged) {
      yield await this._onPasswordChanged(event);
    }

    // The terms of service accept status has been changed
    if (event is TermsAcceptanceChanged) {
      yield await this._onTermsAccptanceChanged(event);
    }

    if (event is PrivacyAcceptanceChanged) {
      yield await this._onPrivacyAcceptanceChanged(event);
    }

    // The form is submitted
    if (event is FormSubmitted) {
      yield RegistrationProcessing();
      yield await this._onFormSubmitted(event);
    }

    // date of birth has been changed.
    if (event is DateOfBirthChanged) {
      yield await this._onDateOfBirthChanged(event);
    }

    // form started
    if (event is FormStarted) {
      yield await this._onFormStarted(event);
    }

    // form canceled by the user
    if (event is FormCanceled) {
      yield await this._onFormCanceled(event);
      yield RegistrationUninitialized();
    } 
  }

Вот lo goc для функции _onEmailChanged().

Future<RegistrationState> _onEmailChanged(EmailChanged event) async {
    return RegistrationInProgress(
      firstName: state.firstName,
      firstNameError: state.firstNameError,
      lastName: state.lastName,
      lastNameError: state.lastNameError,
      email: event.email,
      emailError: await this._validateEmail(event.email),
      password: state.password,
      passwordError: state.passwordError,
      dob: state.dob,
      dobError: state.dobError,
      acceptedTerms: state.acceptedTerms,
      acceptedPrivacy: state.acceptedPrivacy,
    );
  }

Соответствующая вещь здесь поле emailError. Вот код для функции validateEmail().

Future<String> _validateEmail(String email) async {
    // make sure the email is not empty.
    if (QuiverString.isBlank(email)) {
      return "This field is required";
    }

    // make sure the email is of a valid form
    if (!this._emailIsValidForm(email)) {
      return "Email is invalid.";
    }

    // make sure the email is not already taken by another account.
    if ( !await this._emailIsAvailable(email)) {
      return "email already in use";
    }

    return null;
  }

Строка, которая, по-видимому, вызывает ошибку, - это функция emailIsValidForm() (поскольку возвращена ошибка ответа). Вот код для этой функции. Я использую пакет EmailValidator для проверки электронной почты.

bool _emailIsValidForm(String email) {
    return EmailValidator.validate(email, ALLOW_TOP_LEVEL_DOMAINS, ALLOW_INTERNATIONAL);
  }

Проблема, с которой я сталкиваюсь, заключается в том, что даже если я введу действительный адрес электронной почты, в поле ошибки будет указано «Email Invalid».

Любой совет, что является причиной этого?

Вот мои классы состояний и событий, кстати.

abstract class RegistrationState extends Equatable {
  final String firstName;
  final String firstNameError;
  final String lastName;
  final String lastNameError;
  final String email;
  final String emailError;
  final String password;
  final String passwordError;
  final DateTime dob;
  final String dobError;
  final bool acceptedTerms;
  final bool acceptedPrivacy;
  final String registrationError;

  @override
  List<Object> get props => [
        firstName,
        firstNameError,
        lastName,
        lastNameError,
        email,
        emailError,
        password,
        passwordError,
        dob,
        dobError,
        acceptedTerms,
        acceptedPrivacy,
        registrationError
      ];

  const RegistrationState({
    @required this.firstName,
    @required this.firstNameError,
    @required this.lastName,
    @required this.lastNameError,
    @required this.email,
    @required this.emailError,
    @required this.password,
    @required this.passwordError,
    @required this.dob,
    @required this.dobError,
    @required this.acceptedTerms,
    @required this.acceptedPrivacy,
    this.registrationError,
  });

  // make sure the name fields are valid.
  bool nameFieldsAreValid() {
    return (firstNameError == null) && (lastNameError == null) && (firstName != null) && (lastName != null);
  }

  // checks if the email field is valid.
  bool emailFieldsAreValid() {
    return (emailError == null) && (email != null);
  }

  // make sure the password fields are valid.
  bool passwordFieldsAreVaid() {
    return (passwordError == null) && (password != null);
  }

  // chekcs to see if the date of birth is valid.
  bool dobFiedsAreValid() {
    return ((dob != null) && (dobError == null));
  }

  // validates that all the terms have been accepted.
  bool allTermsAccepted() {
    return (acceptedTerms && acceptedPrivacy);
  }
}

/// Uninitialized State
///
/// The Registration has not been invoked.

class RegistrationUninitialized extends RegistrationState {
  RegistrationUninitialized()
      : super(
            firstName: null,
            firstNameError: null,
            lastName: null,
            lastNameError: null,
            email: null,
            emailError: null,
            password: null,
            passwordError: null,
            dob: null,
            dobError: null,
            acceptedTerms: false,
            acceptedPrivacy: false,
            registrationError: null);
}

/// Started State
///
/// The Registration process has been initiated and is in progress.

class RegistrationStarted extends RegistrationState {
  RegistrationStarted()
      : super(
            firstName: null,
            firstNameError: null,
            lastName: null,
            lastNameError: null,
            email: null,
            emailError: null,
            password: null,
            passwordError: null,
            dob: null,
            dobError: null,
            acceptedTerms: false,
            acceptedPrivacy: false,
            registrationError: null);
}

/// InProgress State
///
/// The Registration application is in progress.

class RegistrationInProgress extends RegistrationState {
  RegistrationInProgress(
      {@required firstName,
      @required firstNameError,
      String lastName,
      String lastNameError,
      @required email,
      @required String emailError,
      @required String password,
      @required String passwordError,
      @required DateTime dob,
      @required String dobError,
      @required bool acceptedTerms,
      @required bool acceptedPrivacy})
      : super(
            firstName: firstName,
            firstNameError: firstNameError,
            lastName: lastName,
            lastNameError: lastNameError,
            email: email,
            emailError: emailError,
            password: password,
            passwordError: passwordError,
            dob: dob,
            dobError: dobError,
            acceptedTerms: acceptedTerms,
            acceptedPrivacy: acceptedPrivacy,
            registrationError: null);
}

/// Completed State
///
/// The Registration has been completed successfully.

class RegistrationCompleted extends RegistrationState {
  RegistrationCompleted(
      {@required firstName,
      @required firstNameError,
      String lastName,
      String lastNameError,
      @required email,
      @required String emailError,
      @required String password,
      @required String passwordError,
      @required DateTime dob,
      @required String dobError,
      @required bool acceptedTerms,
      @required bool acceptedPrivacy})
      : super(
            firstName: firstName,
            firstNameError: firstNameError,
            lastName: lastName,
            lastNameError: lastNameError,
            email: email,
            emailError: emailError,
            password: password,
            passwordError: passwordError,
            dob: dob,
            dobError: dobError,
            acceptedTerms: acceptedTerms,
            acceptedPrivacy: acceptedPrivacy,
            registrationError: null);
}

/// Rejected State
///
/// The registration has been rejected

class RegistrationRejected extends RegistrationState {
  RegistrationRejected(
      {@required firstName,
      @required firstNameError,
      String lastName,
      String lastNameError,
      @required email,
      @required String emailError,
      @required String password,
      @required String passwordError,
      @required DateTime dob,
      @required String dobError,
      @required bool acceptedTerms,
      @required bool acceptedPrivacy,
      @required String registrationError})
      : super(
            firstName: firstName,
            firstNameError: firstNameError,
            lastName: lastName,
            lastNameError: lastNameError,
            email: email,
            emailError: emailError,
            password: password,
            passwordError: passwordError,
            dob: dob,
            dobError: dobError,
            acceptedTerms: acceptedTerms,
            acceptedPrivacy: acceptedPrivacy,
            registrationError: registrationError);
}

/// Canceled State
///
/// The registration was canceled by the user.

class RegistrationCanceled extends RegistrationState {
  RegistrationCanceled(
      {@required firstName,
      @required String lastName,
      @required email,
      @required DateTime dob,})
      : super(
            firstName: firstName,
            firstNameError: null,
            lastName: lastName,
            lastNameError: null,
            email: email,
            emailError: null,
            password: null,
            passwordError: null,
            dob: dob,
            dobError: null,
            acceptedTerms: true,
            acceptedPrivacy: true,
            registrationError: null);
}

/// Processing State
///
/// The registration is being processed by the server.

class RegistrationProcessing extends RegistrationState {}

А вот мой код событий.

abstract class RegistrationEvent extends Equatable {

  const RegistrationEvent();

  @override
  List<Object> get props => [];
}

class FirstNameChanged extends RegistrationEvent {
  final String firstName;

  const FirstNameChanged({@required this.firstName});

  @override
  List<Object> get props => [firstName];

  @override
  String toString() {
    return "FirstNameChanged Event: {\n$firstName\n}";
  }
}

class LastNameChanged extends RegistrationEvent {
  final String lastName;

  const LastNameChanged({@required this.lastName});

  @override
  List<Object> get props => [lastName];

  @override
  String toString() {
    return "LastNameChanged Event: {\n$lastName\n}";
  }
}

class EmailChanged extends RegistrationEvent {
  final String email;

  const EmailChanged({@required this.email});

  @override
  List<Object> get props => [email];

  @override
  String toString() {
    return "EmailChanged Event: {\n$email\n}";
  }
}

class PasswordChanged extends RegistrationEvent {
  final String password;

  const PasswordChanged({@required this.password});

  @override
  List<Object> get props => [password];

  @override
  String toString() {
    return "PasswordChanged Event: {\n${password.replaceRange(0, password.length - 1, "*")}\n}";
  }
}

class TermsAcceptanceChanged extends RegistrationEvent {
  final bool acceptTerms;

  const TermsAcceptanceChanged({@required this.acceptTerms});

  @override
  List<Object> get props => [acceptTerms];

  @override
  String toString() {
    String msg = acceptTerms ? "Accepted" : "Denied";
    return "TermsAcceptanceChanged Event: {\n$msg\n}";
  }
}

class PrivacyAcceptanceChanged extends RegistrationEvent {
  final bool acceptPrivacy;

  const PrivacyAcceptanceChanged({@required this.acceptPrivacy});

  @override
  List<Object> get props => [acceptPrivacy];

   @override
  String toString() {
    String msg = acceptPrivacy ? "Accepted" : "Denied";
    return "PrivacyAcceptanceChanged Event: {\n$msg\n}";
  }
}

class FormSubmitted extends RegistrationEvent {
  @override
  String toString() {
    return "FormSubmitted Event: {}";
  }
}

class FormStarted extends RegistrationEvent {
  @override
  String toString() {
    return "FormStarted Event: {}";
  }
}

class FormCanceled extends RegistrationEvent {
  @override
  String toString() {
    return "FormCanceled Event: {}";
  }
}

class DateOfBirthChanged extends RegistrationEvent {
  final DateTime dob;

  const DateOfBirthChanged({@required this.dob});

  @override
  List<Object> get props => [dob];

  @override
  String toString() {
    DateFormat format = DateFormat.yMd();
    return "DateOfBirthChanged Event: {\n${format.format(dob)}\n}";
  }
}

class FormProcessing extends RegistrationEvent {
  @override
  String toString() {
    return "FormProcessing Event: {}";
  }
}

Вот _onFirstNameChange()

Future<RegistrationState> _onFirstNameChanged(FirstNameChanged event) async {
    return RegistrationInProgress(
        firstName: event.firstName,
        firstNameError: this._validateFirstName(event.firstName),
        lastName: state.lastName,
        lastNameError: state.lastNameError,
        email: state.email,
        emailError: state.emailError,
        password: state.password,
        passwordError: state.passwordError,
        dob: state.dob,
        dobError: state.dobError,
        acceptedTerms: state.acceptedTerms,
        acceptedPrivacy: state.acceptedPrivacy);
  }

И метод _validateFirstName().

String _validateFirstName(String fName) {
    return QuiverString.isBlank(fName) ? "This field is required." : null;
  }

Можно также включить мой метод сборки.

@override
  Widget build(BuildContext context) {
    return BlocBuilder<RegistrationFormBloc, RegistrationState>(
      builder: (BuildContext context, RegistrationState state) {
        return Scaffold(
          body: GestureDetector(
            onTap: () => FocusScope.of(context).unfocus(),
            child: SingleChildScrollView(
              child: Form(
                key: _formKey,
                child: Column(
                  mainAxisAlignment: MainAxisAlignment.start,
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: <Widget>[
                    // the form itself.
                    _showFormSection(state),
                    // the exit button
                    Center(
                      child: FlatButton(
                        child: Text("Cancel"),
                        onPressed: () {
                          BlocProvider.of<RegistrationFormBloc>(context)
                              .add(FormCanceled());
                          Navigator.pop(context);
                        },
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ),
        );
      },
    );
  }

_showFormSection () отображает соответствующий раздел формы для пользователя (раздел имени, раздел электронной почты и т. д. c ...), чтобы придать ему эффект «Многостраничная форма». Функция просто возвращает соответствующий виджет в соответствии с текущим индексом.

Widget _showFormSection(RegistrationState currentState) {
    Widget section;

    switch (_pageIndex) {
      case 0:
        section = NameWidget();
        break;
      case 1:
        // email
        section = Column(
          mainAxisAlignment: MainAxisAlignment.start,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: <Widget>[
            // heading section
            Container(
              width: _formSectionHeadingSize.width,
              height: _formSectionHeadingSize.height,
              child: Center(
                child: Text(
                  "Your Email Address",
                  style: Theme.of(context).textTheme.headline,
                ),
              ),
            ),
            // description
            Container(
              width: _formSectionDescriptionSize.width,
              height: _formSectionDescriptionSize.height,
              padding: const EdgeInsets.all(_sectionPadding),
              child: Text(
                "Your email address will be used to send you purchase receipts, updates, and other information.",
                style: Theme.of(context).textTheme.body1,
              ),
            ),
            // body
            Container(
              width: _formSectionBodySize.width,
              height: _formSectionBodySize.height,
              padding: const EdgeInsets.all(_sectionPadding),
              child: Column(
                mainAxisAlignment: MainAxisAlignment.start,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  TextFormField(
                    controller: _email,
                    focusNode: _emailFocus,
                    textInputAction: TextInputAction.done,
                    keyboardType: TextInputType.emailAddress,
                    onChanged: (value) {
                      BlocProvider.of<RegistrationFormBloc>(context)
                          .add(EmailChanged(email: value));
                    },
                    onFieldSubmitted: (value) {
                      FocusScope.of(context).unfocus();
                      BlocProvider.of<RegistrationFormBloc>(context)
                          .add(EmailChanged(email: value));
                    },
                    decoration: InputDecoration(
                      labelText: "Email Address",
                      labelStyle: Theme.of(context).textTheme.body1,
                      hintText: "example@example.com",
                      errorText: currentState.emailError,
                    ),
                  ),
                ],
              ),
            ),

            // navigation
            Container(
              width: _bottomNavigationSize.width,
              height: _bottomNavigationSize.height,
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                crossAxisAlignment: CrossAxisAlignment.start,
                children: <Widget>[
                  FlatButton(
                    child: Text(
                      "Back",
                      style: Theme.of(context).textTheme.body1,
                    ),
                    onPressed: this._showPreviousSection,
                  ),
                  FlatButton(
                    child: Text(
                      "Next",
                      style: Theme.of(context).textTheme.body1,
                    ),
                    onPressed: currentState.emailFieldsAreValid()
                        ? this._showNextSection
                        : null,
                  ),
                ],
              ),
            ),
          ],
        );
        break;
      case 2:
        // password
        section = PasswordWidget();
        break;
      case 3:
        // date of birth
        section = DobWidget();
        break;
      case 4:
        // terms section
        section = TermsWidget();
        break;
    }

    return section;
  }

  // show the next section.
  void _showNextSection() {
    if (_pageIndex < 4) {
      setState(() {
        _pageIndex++;
      });
    }
  }

  // show the previous section
  void _showPreviousSection() {
    if (_pageIndex > 0) {
      setState(() {
        _pageIndex--;
      });
    }
  }

1 Ответ

0 голосов
/ 24 февраля 2020

В неизмененном случае передайте значение value.trim () событию. Иногда после ввода адреса электронной почты во вводимом тексте остается пробел, чтобы удалить его при проверке, используйте функцию обрезки, чтобы он удалил этот пробел.

...