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);
请看下面的代码。
main.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
只支持一个订阅者,所以如果你想让StreamController
的Stream
可以被多次收听,这需要是一个广播流,一个其中一种方法是使用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 未按预期工作的主要内容,如果未能解决你的问题,请参考以下文章