Rx.combineLatest2 没有按预期工作
Rx.combineLatest2 not working as expected
我是 flutter 的新手。我尝试在 flutter 中创建一些示例应用程序以创建自己的应用程序。我在 flutter with bloc pattern 中找到了一个不错的应用。请看以下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.");
}
});
}
请指教
Combinelatest2
很好,问题是您在每次重建中都创建了一个新的 bloc。
因此在 StatefulWidget
的 initState method and dispose the bloc in the 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"),
))
],
),
),
),
);
}
}
我是 flutter 的新手。我尝试在 flutter 中创建一些示例应用程序以创建自己的应用程序。我在 flutter with bloc pattern 中找到了一个不错的应用。请看以下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.");
}
});
}
请指教
Combinelatest2
很好,问题是您在每次重建中都创建了一个新的 bloc。
因此在 StatefulWidget
的 initState method and dispose the bloc in the 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"),
))
],
),
),
),
);
}
}