Rx.combineLatest2 未按预期工作

Posted

技术标签:

【中文标题】Rx.combineLatest2 未按预期工作【英文标题】:Rx.combineLatest2 not working as expected 【发布时间】:2020-06-19 20:13:16 【问题描述】:

我是使用颤振的新手。我正在尝试在颤振中创建一些示例应用程序以创建自己的应用程序。我在 Flutter with bloc 模式中找到了一个不错的应用程序。请参阅以下link

这只是一个登录页面。表单中有一个带有验证的电子邮件和密码文本框。一个电子邮件验证器和另一个密码长度验证器。表单中有一个提交按钮,最初它被禁用,如果电子邮件和密码成功验证,则启用提交按钮。它使用带有 rxdart 包的 bloc 模式架构。

我在提交按钮验证时遇到问题,输入电子邮件和密码字段后,按钮字段未启用。

按钮验证码

Stream<bool> get submitCheck => 
  Rx.combineLatest2(email, password, (e, p) => true);

请看下面的代码。

ma​​in.dart

import 'package:bloc_login/pagetwo.dart';
import 'package:flutter/material.dart';
import 'package:bloc_login/bloc.dart';
import 'package:flutter/rendering.dart';

 void main() => runApp(MyApp());

class MyApp extends StatelessWidget 
 @override
  Widget build(BuildContext context) 
return MaterialApp(
  home: MyHomePage(title: 'Flutter Demo Home Page'),
  title: 'Flutter Demo',
  theme: ThemeData(
    primarySwatch: Colors.teal,
  ),
  debugShowCheckedModeBanner: false,
);



class MyHomePage extends StatefulWidget 
MyHomePage(Key key, this.title) : super(key: key);
final String title;
 @override
_MyHomePageState createState() => _MyHomePageState();


class _MyHomePageState extends State<MyHomePage> 
 changethePage(BuildContext context) 
Navigator.of(context)
    .push(MaterialPageRoute(builder: (context) => PageTwo()));
  

 @override
   Widget build(BuildContext context) 
final bloc = Bloc();

return Scaffold(
  appBar: AppBar(
    title: Text("Bloc pattern"),
  ),
  body: SingleChildScrollView(
    child: Container(
      height: MediaQuery.of(context).size.height,
      padding: EdgeInsets.all(16),
      child: Column(
        mainAxisSize: MainAxisSize.max,
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          StreamBuilder<String>(
            stream: bloc.email,
            builder: (context, snapshot) => TextField(
              onChanged: bloc.emailChanged,
              keyboardType: TextInputType.emailAddress,
              decoration: InputDecoration(
                  border: OutlineInputBorder(),
                  hintText: "Enter Email",
                  labelText: "Email",
                  errorText: snapshot.error),
            ),
          ),
          SizedBox(
            height: 20,
          ),
          StreamBuilder<String>(
            stream: bloc.password,
            builder: (context, snapshot) => TextField(
              onChanged: bloc.passwordChanged,
              keyboardType: TextInputType.text,
              obscureText: true,
              decoration: InputDecoration(
                  border: OutlineInputBorder(),
                  hintText: "Enter password",
                  labelText: "Password",
                  errorText: snapshot.error),
            ),
          ),
          SizedBox(
            height: 20,
          ),
          StreamBuilder<bool>(
              stream: bloc.submitCheck,
              builder: (context, snapshot) => RaisedButton(
                    color: Colors.tealAccent,
                    onPressed: (snapshot.data != null)
                        ? () => changethePage(context)
                        : null,
                    child: Text("Submit"),
                  ))
        ],
      ),
    ),
  ),
);


bloc.dart

import 'dart:async';
import 'package:bloc_login/validator.dart';
import 'package:rxdart/rxdart.dart';

class Bloc extends Object with Validators implements BaseBloc 
final _emailController = StreamController<String>();
final _passwordController = StreamController<String>();

Function(String) get emailChanged => _emailController.sink.add;
Function(String) get passwordChanged => _passwordController.sink.add;

Stream<String> get email => _emailController.stream.transform(emailValidator);
Stream<String> get password =>
  _passwordController.stream.transform(passwordValidator);

Stream<bool> get submitCheck =>
  Rx.combineLatest2(email, password, (e, p) => true);

@override
void dispose() 
_emailController.close();
_passwordController.close();



 abstract class BaseBloc 
   void dispose();

validator.dart

import 'dart:async';

mixin Validators 
 var emailValidator =
  StreamTransformer<String, String>.fromHandlers(handleData: (email, sink) 
if (email.contains("@")) 
  sink.add(email);
 else 
  sink.addError("Email is not valid.");

);

var passwordValidator = StreamTransformer<String, String>.fromHandlers(
  handleData: (password, sink) 
if (password.length > 4) 
  sink.add(password);
 else 
  sink.addError("Password length should be greater than 4.");

);

请指教。

【问题讨论】:

【参考方案1】:

Combinelatest2 很好,问题是您在每次重建时都创建了一个新块。

所以在 initState 方法中创建 bloc 并在 StatefulWidget 的 dispose 方法中处置 bloc。

而且你的 StreamControllers 有一个 Stream 只支持一个订阅者,所以如果你想让StreamControllerStream 可以被多次收听,这需要是一个广播流,一个其中一种方法是使用StreamController .broadcast () 构造函数。

P.D.如果您正在使用 bloc 模式创建表单,您可以查看flutter_form_bloc,它可以为您节省大量代码。


自从 dart 2.1 you don't need to extend an object to use a mixin

class Bloc with Validators implements BaseBloc 
  final _emailController = StreamController<String>.broadcast();
  final _passwordController = StreamController<String>.broadcast();
  //...

class MyHomePage extends StatefulWidget 
  MyHomePage(Key key, this.title) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();


class _MyHomePageState extends State<MyHomePage> 
  Bloc _bloc;

  @override
  void initState() 
    super.initState();
    _bloc = Bloc();
  

  @override
  void dispose() 
    _bloc.dispose();
    super.dispose();
  

  changethePage(BuildContext context) 
    Navigator.of(context)
        .push(MaterialPageRoute(builder: (context) => PageTwo()));
  

  @override
  Widget build(BuildContext context) 
    return Scaffold(
      appBar: AppBar(
        title: Text("Bloc pattern"),
      ),
      body: SingleChildScrollView(
        child: Container(
          height: MediaQuery.of(context).size.height,
          padding: EdgeInsets.all(16),
          child: Column(
            mainAxisSize: MainAxisSize.max,
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              StreamBuilder<String>(
                stream: _bloc.email,
                builder: (context, snapshot) => TextField(
                  onChanged: _bloc.emailChanged,
                  keyboardType: TextInputType.emailAddress,
                  decoration: InputDecoration(
                      border: OutlineInputBorder(),
                      hintText: "Enter Email",
                      labelText: "Email",
                      errorText: snapshot.error),
                ),
              ),
              SizedBox(
                height: 20,
              ),
              StreamBuilder<String>(
                stream: _bloc.password,
                builder: (context, snapshot) => TextField(
                  onChanged: _bloc.passwordChanged,
                  keyboardType: TextInputType.text,
                  obscureText: true,
                  decoration: InputDecoration(
                      border: OutlineInputBorder(),
                      hintText: "Enter password",
                      labelText: "Password",
                      errorText: snapshot.error),
                ),
              ),
              SizedBox(
                height: 20,
              ),
              StreamBuilder<bool>(
                  stream: _bloc.submitCheck,
                  builder: (context, snapshot) => RaisedButton(
                        color: Colors.tealAccent,
                        onPressed: (snapshot.data != null)
                            ? () => changethePage(context)
                            : null,
                        child: Text("Submit"),
                      ))
            ],
          ),
        ),
      ),
    );
  


【讨论】:

非常感谢您的宝贵回答。但它不起作用。它显示一个错误,例如“状态不佳:流已被收听。” @RageshS 将 StreamController 更改为 StreamController.broadcast()

以上是关于Rx.combineLatest2 未按预期工作的主要内容,如果未能解决你的问题,请参考以下文章

C中的无符号字符未按预期工作

使用广播应用地图转换时,pyspark Udf 未按预期工作?

带有一个 goto 标签的 C 代码未按预期工作

C中的电源功能未按预期工作

Apache 虚拟主机未按预期工作

devenv.exe /upgrade 未按预期工作