- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
我的应用程序中有一个带有 TextField 表单验证器的注册页面,带有一个按钮。如果未满足业务规则,文本字段将显示表单验证错误消息,并且一旦满足所有条件,“下一步”按钮将可点击。这目前在我的应用程序中运行良好,但我发现一旦我离开页面并返回到它,验证错误消息就会停止显示并且按钮也会停止工作。查看我的 IDE (android studio) 中的控制台日志,我收到的唯一相关错误消息是
[VERBOSE-2:ui_dart_state.cc(148)] Unhandled Exception: Bad state:
Stream is already closed
#0 _SinkTransformerStreamSubscription._add
(dart:async/stream_transformers.dart:66:7)
#1 _EventSinkWrapper.add
(dart:async/stream_transformers.dart:15:11)
我不太确定这是什么意思,在页面重新加载后流是否关闭而不是重新打开?如果没有,有没有办法解决这个问题或者我缺少什么? This is我正在经历什么
流生成器代码:
Widget emailField(authBloc) {
return StreamBuilder(
stream: authBloc.emailStream,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
return TextField(
onChanged: authBloc.updateEmail,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
border: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.deepOrange
)
),
hintText: 'Enter Email',
labelText: 'Email Address',
errorText: snapshot.error
),
);
},
);
}
Widget passwordField( authBloc) {
return StreamBuilder(
stream: authBloc.passwordStream,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
return TextField(
onChanged: authBloc.updatePassword,
obscureText: true,
decoration: InputDecoration(
hintText: 'Enter password',
labelText: 'Password',
errorText: snapshot.error,
),
);
},
);
}
Widget checkPasswordField( authBloc) {
return StreamBuilder(
stream: authBloc.validatePasswordStream,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
return TextField(
onChanged: authBloc.updateValidatePassword,
obscureText: true,
decoration: InputDecoration(
hintText: 'Re-enter password',
labelText: 'Confirm Password',
errorText: snapshot.error,
),
);
},
);
}
Widget nextBtn(authBloc) {
return StreamBuilder(
stream: authBloc.submitValid,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
return RaisedButton(
child: Text('Next'),
color: Colors.deepOrange,
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(7.0))
),
onPressed: snapshot.hasData
? () => Navigator.pushNamed(context, '/register')
: null,
);
}
);
}
流:
/// REGISTER VARIABLES
static final _emailController = BehaviorSubject<
String>(); //RxDart's implementation of StreamController. Broadcast stream by default
static final _passwordController = BehaviorSubject<String>();
static final _validatePasswordController = BehaviorSubject<
String>(); // Will check that the password entered the 2nd time is correct
/// REGISTER STREAM & METHODS
//Retrieve data from the stream
Stream<String> get emailStream => _emailController.stream
.transform(performEmailValidation); //Return the transformed stream
Stream<String> get passwordStream =>
_passwordController.stream.transform(performPasswordValidation);
Stream<String> get validatePasswordStream =>
_validatePasswordController.stream.transform(performIsPasswordSame);
//Merging email, password and validate password
Stream<bool> get submitValid => Observable.combineLatest3(
emailStream, passwordStream, validatePasswordStream, (e, p1, p2) => true);
//Add data to the stream
Function(String) get updateEmail => _emailController.sink.add;
Function(String) get updatePassword => _passwordController.sink.add;
Function(String) get updateValidatePassword =>
_validatePasswordController.sink.add;
// performing user input validations
final performEmailValidation = StreamTransformer<String, String>.fromHandlers(
handleData: (email, sink) async {
String emailValidationRule =
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regExp = new RegExp(emailValidationRule);
if (await doesNameAlreadyExist("email", _emailController.value) == true)
sink.addError("That email already exists");
else if (regExp.hasMatch(email)) {
sink.add(email);
} else {
sink.addError(StringConstant.emailErrorMessage);
}
});
final performPasswordValidation =
StreamTransformer<String, String>.fromHandlers(
handleData: (_passwordController, sink) {
if (_passwordController.length >= 6) {
sink.add(_passwordController);
} else {
sink.addError(StringConstant.passwordErrorMessage);
}
});
final performIsPasswordSame = StreamTransformer<String, String>.fromHandlers(
handleData: (password, sink) {
if (password != _passwordController.value)
sink.addError(StringConstant.invalidPasswordMessage);
else
sink.add(password);
});
整个屏幕代码:
class SignUp extends StatefulWidget {
@override
_SignUpState createState() => _SignUpState();
}
class _SignUpState extends State<SignUp> {
AuthBloc _authBloc;
@override
void didChangeDependencies() {
super.didChangeDependencies();
_authBloc = AuthBlocProvider.of(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.center,
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
children: <Widget>[
SizedBox(height: 80,),
Text("Register", style: Style.appTextStyle),
SizedBox(height: 100,),
emailField(_authBloc),
SizedBox(height: 30),
passwordField(_authBloc),
SizedBox(height: 30),
checkPasswordField(_authBloc),
SizedBox(height: 30),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
cancelBtn(),
nextBtn(_authBloc),
],
)
// checkPasswordField(authBloc),
],
),
)
),
);
}
Widget emailField(authBloc) {
return StreamBuilder(
stream: authBloc.emailStream,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
return TextField(
onChanged: authBloc.updateEmail,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
border: UnderlineInputBorder(
borderSide: BorderSide(
color: Colors.deepOrange
)
),
hintText: 'Enter Email',
labelText: 'Email Address',
errorText: snapshot.error
),
);
},
);
}
Widget passwordField( authBloc) {
return StreamBuilder(
stream: authBloc.passwordStream,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
return TextField(
onChanged: authBloc.updatePassword,
obscureText: true,
decoration: InputDecoration(
hintText: 'Enter password',
labelText: 'Password',
errorText: snapshot.error,
),
);
},
);
}
Widget checkPasswordField( authBloc) {
return StreamBuilder(
stream: authBloc.validatePasswordStream,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
return TextField(
onChanged: authBloc.updateValidatePassword,
obscureText: true,
decoration: InputDecoration(
hintText: 'Re-enter password',
labelText: 'Confirm Password',
errorText: snapshot.error,
),
);
},
);
}
Widget nextBtn(authBloc) {
return StreamBuilder(
stream: authBloc.submitValid,
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
return RaisedButton(
child: Text('Next'),
color: Colors.deepOrange,
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(7.0))
),
onPressed: snapshot.hasData
? () => Navigator.pushNamed(context, '/register')
: null,
);
}
);
}
Widget cancelBtn(){
return RaisedButton(
child: Text('Cancel'),
color: Colors.white30,
shape: BeveledRectangleBorder(
borderRadius: BorderRadius.all(Radius.circular(7.0))
),
onPressed: () => Navigator.pop(context),
);
}
@override
void dispose() {
super.dispose();
_authBloc.dispose();
}
Bloc 代码:
/// REGISTER VARIABLES
static final _emailController = BehaviorSubject<
String>(); //RxDart's implementation of StreamController. Broadcast stream by default
static final _passwordController = BehaviorSubject<String>();
static final _validatePasswordController = BehaviorSubject<
String>(); // Will check that the password entered the 2nd time is correct
/// REGISTER STREAM & METHODS
//Retrieve data from the stream
Stream<String> get emailStream => _emailController.stream
.transform(performEmailValidation); //Return the transformed stream
Stream<String> get passwordStream =>
_passwordController.stream.transform(performPasswordValidation);
Stream<String> get validatePasswordStream =>
_validatePasswordController.stream.transform(performIsPasswordSame);
//Merging email, password and validate password
Stream<bool> get submitValid => Observable.combineLatest3(
emailStream, passwordStream, validatePasswordStream, (e, p1, p2) => true);
//Add data to the stream
Function(String) get updateEmail => _emailController.sink.add;
Function(String) get updatePassword => _passwordController.sink.add;
Function(String) get updateValidatePassword =>
_validatePasswordController.sink.add;
// performing user input validations
final performEmailValidation = StreamTransformer<String, String>.fromHandlers(
handleData: (email, sink) async {
String emailValidationRule =
r'^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$';
RegExp regExp = new RegExp(emailValidationRule);
if (await doesNameAlreadyExist("email", _emailController.value) == true)
sink.addError("That email already exists");
else if (regExp.hasMatch(email)) {
sink.add(email);
} else {
sink.addError(StringConstant.emailErrorMessage);
}
});
final performPasswordValidation =
StreamTransformer<String, String>.fromHandlers(
handleData: (_passwordController, sink) {
if (_passwordController.length >= 6) {
sink.add(_passwordController);
} else {
sink.addError(StringConstant.passwordErrorMessage);
}
});
final performIsPasswordSame = StreamTransformer<String, String>.fromHandlers(
handleData: (password, sink) {
if (password != _passwordController.value)
sink.addError(StringConstant.invalidPasswordMessage);
else
sink.add(password);
});
dispose() {
_emailController.close();
_passwordController.close();
_validatePasswordController.close();
}
最佳答案
好吧,查看完整的源代码和您展示的 GIF,我可以看出是什么导致了这个问题。您的错误是在 dispose()
SingUp 小部件类方法中调用了 dispose()
BLoC 实例方法。
为什么是错误的?
在您的特定情况下,当您处于 SingUp 屏幕并转到下一个路由/屏幕时,将调用来自 SingUp 的 dispose
方法,此时您的 BLoC 实例的流将被关闭.但是下一个允许用户返回到 SingUp 屏幕,当这种情况发生时,SingUp 实例获得与之前使用的相同的 BLoC 实例,但是这个 BLoC 实例的流已经关闭。
我怎样才能以简单的方式解决这个问题?
在 SingUp 类中:
@override
void dispose() {
super.dispose();
/// DON'T CALL BLoC dispose here
/// _authBloc.dispose();
}
不要在这里放置你的 BloC,因为用户可以随时返回到这个屏幕。由于您正在使用 InheritedWidget 获取 BLoC 实例,这使您可以在不同的地方访问相同的 BLoC 实例,我建议您在用户结束所有调用时调用 yourBloc.dispose()
过程。
关于页面重新加载后 Flutter StreamBuilder 不工作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55132741/
我正在尝试计算未读消息的数量。在第一个 streambuilder 中,我需要获取所有与第一个查询匹配的文档 ID。 然后,在该文档 ID 中,我可以访问该文档中的子集合并执行另一个查询。然后我需要访
我正在构建一个应用程序,该应用程序大量使用 streambuilder 来从应用程序中的多个页面的 Firestore 获取实时数据。 由于没有流关闭/流订阅取消方法,假设当框架处理小部件时,stre
在注册过程中,我正在构建的应用程序中为每个“用户”文档创建了一个子集合,最多包含100个文档。 我正在尝试用StreamBuilder显示这些子集合文档。 我有一个无法解决的奇怪错误。用户首次查看时,
我的 StreamBuilder 在 View 中: Widget build(BuildContext context) { print("rebuilding..."); // as of
目前,我有一个 StreamBuilder 从 Firebase 为群消息应用程序提取数据。我有一个 snapshot.hasData 检查以确保在显示消息之前有数据。问题在于,当 setState
我正在使用 http库以下载图像。 final client = http.Client(); final _response = await client.send(http.Request('GE
StreamBuilder每当它获得新事件时都会重建。这会导致导航( Navigator.push )出现问题,因为如果在导航时接收到新事件,则此触发器重建。因为在构建小部件树时尝试导航,这将抛出 e
考虑以下代码: StreamBuilder _createDataStream(){ return StreamBuilder( stream: Firestore.i
我正在尝试弄清楚如何将以下数据放入数据表中,但我真的很挣扎,因为它不断重复标题 import 'dart:async'; import 'dart:convert'; import 'package:
我遇到了一个问题,我的流生成器只触发一次。 我正在尝试根据用户选择的主题将我的 bottomNavigationBar 配置为不同的颜色。 为此,我有一个页面,用户可以在其中决定是使用浅色主题还是
以下代码按预期构建和运行——当用户键入内容时,会显示一条错误消息,直到该字符串通过电子邮件验证格式。 Widget emailField(){ return StreamBuilder( stre
我创建了这段代码,我想要发生的是当我按下按钮时我希望饼图用新值重新渲染(应该是旧值但食物值增加 1)我正在使用 pie_chart: 0.8.0 包中的饼图。Deposit 只不过是一个 pojo(S
我有一个页面,我在其中使用 Bloc 从上面的搜索栏中获取文本,以根据输入的文本过滤下面的列表。 当我第一次进入页面时,Bloc 和流都按预期工作,但是,当我弹出它并返回页面时,我可以看到数据正在通过
我的 ApplicationBloc 是小部件树的根。在 bloc 的构造函数中,我正在收听来自存储库的流,其中包含从 JSON 解码的模型并将它们转发到另一个流,该流由 StreamBuilder
我有一个名为 RootContainer 的小部件,它接收一个 Widget child 并将其包装在 StreamBuilder 中: class RootContainer extends Sta
我正在尝试使用 StreamBuilder 从我的 api 中获取数据,这就是我所做的 loadProduct(String categoryId, int limit, int offset) a
我只是在测试来自 GPS 的一些流。我可以直接插入 gps 流,但我想暂时将其分开。所以我可以将 StreamBuilder 与我自己创建的流一起使用。 这一切似乎都在工作,但 Streambuild
当我在 Flutter 中使用 BLOC 时,例如: class StreamText extends StatelessWidget { StreamText( this.stream,
我写了一些简化的代码,我希望 firestore 数据的条件能够更新父小部件。 我收到错误:在构建期间调用了 setState() 或 markNeedsBuild()。 如何从 StreamBuil
我在有状态小部件中有一个 streamBuilder,它从服务器异步获取数据。此外,我有一个收集这些数据的列表。 StreamBuilder( stream: myStream.stream,
我是一名优秀的程序员,十分优秀!