所以我正在制作一个带有LayoutBuilder 的页面,如所述here
在 LayoutBuilder 中,我放置了一个带有 TextField 的 StreamBuilder,该 TextField 由 bloc 类 SignupFormBloc 提供支持。流是一个BehaviorSubject
当有人在输入中输入内容时,它会触发 onChanged 函数,该函数是我的流的接收器。因此,我将值添加到流中,然后将值传递到 StreamTransformer 中以验证该值,然后让 StreamBuilder 再次构建 TextField 并显示错误消息(如果值无效)。
这是问题的开始。
当我点击 TextField 并输入内容时,它会启动一个无限循环,如下所示:
- StreamBuilder 看到流中的新值
- StreamBuilder 尝试重建 TextField
- 这是如何触发 LayoutBuilder 构建器函数的一些信息
- LayoutBuilder 构建器函数再次构建 StreamBuilder
- StreamBuilder 在流中找到一个值(因为 BehaviorSubject)
- 从第一个被欺负的人开始,无限循环
提示:如果我将 BehaviorSubject 更改为 PublishSubject,一切正常
提示 2: 如果我完全删除 StreamBuilder 并只留下一个空白的 TextField,您可以看到在每个条目中运行 LayoutBuilder 构建器函数。这是正常行为吗?
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:rxdart/rxdart.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
SignupFormBloc _signupFormBloc;
@override
void initState() {
super.initState();
_signupFormBloc = SignupFormBloc();
}
@override
Widget build(BuildContext context) {
print('Build Run!!!!!');
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: LayoutBuilder(
builder: (BuildContext context, BoxConstraints viewportConstraints) {
print('Layout Builder!!!');
return SingleChildScrollView(
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: viewportConstraints.maxHeight,
),
child: IntrinsicHeight(
child: StreamBuilder<String>(
stream: _signupFormBloc.emailStream,
builder: (context, AsyncSnapshot<String> snapshot) {
return TextField(
onChanged: _signupFormBloc.onEmailChange,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
hintText: 'Email',
contentPadding: const EdgeInsets.symmetric(horizontal: 15, vertical: 18),
filled: true,
fillColor: Colors.white,
errorText: snapshot.error,
border: new OutlineInputBorder(
borderSide: BorderSide.none
),
),
);
}
),
),
),
);
},
)
);
}
@override
void dispose() {
_signupFormBloc?.dispose();
super.dispose();
}
}
class SignupFormBloc {
///
/// StreamControllers
///
BehaviorSubject<String> _emailController = BehaviorSubject<String>();
///
/// Stream with Validators
///
Observable<String> get emailStream => _emailController.stream.transform(StreamTransformer<String,String>.fromHandlers(handleData: (email, sink){
final RegExp emailExp = new RegExp(r"^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$");
if (!emailExp.hasMatch(email) || email.isEmpty){
print('has error');
sink.addError('Email format is invalid');
} else {
sink.add(email);
}
}));
///
/// Sinks
///
Function(String) get onEmailChange => _emailController.sink.add;
void dispose() {
_emailController.close();
}
}
最佳答案
发生这种情况是因为流的滥用。
罪魁祸首是这一行:
Observable<String> get emailStream => _emailController.stream.transform(...);
此行的问题在于它每次都会创建一个新流。
这意味着 bloc.emailStream == bloc.emailStream
实际上是错误的。
当与StreamBuilder
结合使用时,意味着每次请求StreamBuilder
重建时,后者都会从头开始重新启动监听过程。
您应该在 BLoC 的构造函数主体中一次创建流,而不是 getter:
class MyBloc {
StreamController _someController;
Stream foo;
MyBloc() {
foo = _someController.stream.transform(...);
}
}
关于当 LayoutBuilder 中的 StreamBuilder 时 flutter 无限循环,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56075306/