【问题标题】:StreamBuilder TextField does not update its value when changed elsewhereStreamBuilder TextField 在其他地方更改时不会更新其值
【发布时间】:2019-09-01 10:47:08
【问题描述】:

我有一个遵循 BLOC 模式的反应式登录表单。我正在尝试以编程方式清除其中的所有值。在我的 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() 函数的提交按钮时,我收到两个文本字段的错误消息,因为电子邮件和密码的值在后台发生了变化,但它们在文本字段。这是我的 TextFields 和提交按钮的 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 (you@example.com)',
            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
        );
      }
    );
  }'

这是我在 Bloc 中用于验证器的代码:

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 getter 在用户使曾经有效的内容无效时起作用。

【问题讨论】:

    标签: dart flutter bloc rxdart


    【解决方案1】:

    在 submit() 中,你似乎想重置用户名和密码

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

    正如您评论的那样,“每次流发出新值时都重新运行构建函数”。它重新构建 UI,因为值更新为空。也许它会导致问题?

    【讨论】:

    • 我认为这不会导致问题。它应该用空字符串重建那个 UI 元素,但是在我将空字符串传递给接收器之前的旧值仍然卡在 UI 元素中。但是,UI 元素的错误消息会更新以反映输入为空。
    【解决方案2】:

    我知道这已经很久了,但这是我解决它的方法。

    首先,我为我的TextField 创建了一个TextEditingController。然后我在 BLoC 上创建了两种方法:updateTextOnChangedupdateTextElsewhere。在第一个我刚刚检索到的值(因为我需要它稍后使用)。在第二个中,我添加了一个接收器来更新 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;
      }
    

    然后,您只需在需要在 onChanged 之外更新它时调用updateTextElsewhere()

    在您的情况下,只需添加一个空字符串,例如:updateTextElsewhere("");

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-24
      • 1970-01-01
      • 2021-06-13
      • 2020-03-30
      • 1970-01-01
      • 2020-01-15
      • 2020-04-21
      相关资源
      最近更新 更多