StreamBuilder TextField не обновляет свое значение при изменении в другом месте

У меня есть реактивная форма входа в систему по шаблону BLOC. Я пытаюсь программно очистить все значения в нем. В моем блоке моя функция отправки передает пустые строки в мои потоковые приемники:

class Bloc with Validators {
  final _email = BehaviorSubject<String>();
  final _password = BehaviorSubject<String>();

  Stream<String> get email => _email.stream.transform(validateEmail);
  Stream<String> get password => _password.stream.transform(validatePassword);
  Stream<bool> get submitValid => Observable.combineLatest2(email, password, (String e, String p) {
    var valid = (e != null && e.isNotEmpty)
                && (p != null && p.isNotEmpty);
    print('$e && $p = $valid');
    return valid;
  });

  Function(String) get changeEmail => _email.sink.add;
  Function(String) get changePassword => _password.sink.add;

  submit() {
    final validEmail = _email.value;
    final validPassword = _email.value;
    print('final values: $validEmail && $validPassword');
    changeEmail('');
    changePassword('');
  }

  dispose() {
    _email.close();
    _password.close();
  }
}

Когда я нажимаю кнопку отправки, которая вызывает эту функцию submit(), я получаю сообщения об ошибках для обоих текстовых полей, потому что значения электронной почты и пароля изменились за кулисами, но они не обновляются визуально в текстовых полях. Вот мои StreamBuilders для моих текстовых полей и кнопки «Отправить»:

Widget emailField(Bloc bloc) {
    return StreamBuilder(
      stream: bloc.email,
      builder: (context, snapshot) { // re-runs build function every time the stream emits a new value
        return TextField(
          onChanged: bloc.changeEmail,
          autocorrect: false,
          keyboardType: TextInputType.emailAddress,
          decoration: InputDecoration(
            icon: Icon(Icons.email),
            hintText: 'email address ([email protected])',
            labelText: 'Email',
            errorText: snapshot.error
          )
        );
      }
    );
  }

  Widget passwordField(Bloc bloc) {
    return StreamBuilder(
      stream: bloc.password,
      builder: (context, AsyncSnapshot<String> snapshot) {
        return TextField(
          onChanged: bloc.changePassword,
          autocorrect: false,
          obscureText: true,
          decoration: InputDecoration(
            icon: Icon(Icons.security),
            hintText: 'must be greater than 6 characters',
            labelText: 'Password',
            errorText: snapshot.error
          )
        );
      }
    );
  }

  Widget submitButton(Bloc bloc) {
    return StreamBuilder(
      stream: bloc.submitValid,
      builder: (context, snapshot) {
        return RaisedButton(
          child: Text('Logins'),
          color: Colors.blue,
          onPressed: !snapshot.hasData || snapshot.hasError || snapshot.data == false
            ? null
            : bloc.submit
        );
      }
    );
  }'

А вот код, который я использую для своих валидаторов в своем блоке:

class Validators {
  final validateEmail = StreamTransformer<String, String>.fromHandlers(
    handleData: (email, sink) {
      RegExp exp = new RegExp(r"^[a-zA-Z0-9.]+@[a-zA-Z0-9]+\.[a-zA-Z]+");
      var valid = exp.hasMatch(email);
      if (valid) {
        sink.add(email);
      } else {
        sink.add('');
        sink.addError('Invalid email address!');
      }
    }
  );

  final validatePassword = StreamTransformer<String, String>.fromHandlers(
    handleData: (password, sink) {
      var valid = password.length >= 6;
      if (valid) {
        sink.add(password);
      } else {
        sink.add('');
        sink.addError('Password must be at least 6 characters long!');
      }
    }
  );
}

В моих валидаторах я выдаю пустую строку всякий раз, когда возникает ошибка. Это делает так, что геттер submitValid работает, когда пользователь аннулирует что-то, что раньше было действительным.

2
0
1 296
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

В submit() вы, похоже, сбрасываете имя пользователя и пароль

changeEmail('');
changePassword('');

И, как вы прокомментировали, «повторно запускает функцию сборки каждый раз, когда поток выдает новое значение». Он перестраивает пользовательский интерфейс, поскольку значение обновлено до пустого. Может из-за него проблема?

Я не думаю, что это должно вызвать проблему. Он должен перестроить этот элемент пользовательского интерфейса с пустой строкой, но старое значение до того, как я передал пустые строки в приемники, все еще застряло в элементах пользовательского интерфейса. Однако сообщение об ошибке для элемента пользовательского интерфейса обновляется, чтобы отразить, как если бы ввод был пустым.

Corey Cole 11.04.2019 07:10
Ответ принят как подходящий

Я знаю, что это было давно, но это мой способ решить эту проблему.

Во-первых, я создал TextEditingController для своего TextField. Затем я создал два метода на своем BLoC: updateTextOnChanged и updateTextElsewhere. На первом я просто получил значение (потому что мне нужно его использовать позже). На втором добавил сток для обновления контроллера на TextField.

Виджет:

  return StreamBuilder<String>(
      stream: bloc.streamText,
      builder: (context, snapshot) {
        _controller.text = snapshot.data;
        return Expanded(
            child: TextField(
            controller: _controller,
            onChanged: (value) => {bloc.updateTextOnChanged(value)},
          ),
        );
      }
   );

Блок:

  Stream<String> get streamText => _controllerTxt.stream;
  String _myText;

  void updateTextElsewhere(String value) {
    _controllerTxt.sink.add(value);
  }

  void updateTextOnChanged(String value) {
    _myText = value;
  }

Затем вам просто нужно вызывать updateTextElsewhere() всякий раз, когда вам нужно обновить его вне onChanged.

В вашем случае просто добавьте пустую строку, например: updateTextElsewhere("");

Другие вопросы по теме