我正在尝试获取一个身份验证表单,以使用三元语句有条件地在Flutter StreamBuilder小部件中重定向。
当重定向条件返回true时,我将显示红色屏幕和以下日志:
I/flutter ( 3787): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════
I/flutter ( 3787): The following assertion was thrown building StreamBuilder<Map<dynamic, dynamic>>(dirty, state:
I/flutter ( 3787): _StreamBuilderBaseState<Map<dynamic, dynamic>, AsyncSnapshot<Map<dynamic, dynamic>>>#66400):
I/flutter ( 3787): setState() or markNeedsBuild() called during build.
I/flutter ( 3787): This Overlay widget cannot be marked as needing to build because the framework is already in the
I/flutter ( 3787): process of building widgets. A widget can be marked as needing to be built during the build phase
I/flutter ( 3787): only if one of its ancestors is currently building. This exception is allowed because the framework
I/flutter ( 3787): builds parent widgets before children, which means a dirty descendant will always be built.
I/flutter ( 3787): Otherwise, the framework might not visit this widget during this build phase.
I/flutter ( 3787): The widget on which setState() or markNeedsBuild() was called was:
I/flutter ( 3787): Overlay-[LabeledGlobalKey<OverlayState>#4f97a](state: OverlayState#5df28(tickers: tracking 2
I/flutter ( 3787): tickers, entries: [OverlayEntry#09e48(opaque: false; maintainState: false),
I/flutter ( 3787): OverlayEntry#61a61(opaque: false; maintainState: true), OverlayEntry#79842(opaque: false;
I/flutter ( 3787): maintainState: false), OverlayEntry#11ff2(opaque: false; maintainState: true)]))
I/flutter ( 3787): The widget which was currently being built when the offending call was made was:
I/flutter ( 3787): StreamBuilder<Map<dynamic, dynamic>>(dirty, state: _StreamBuilderBaseState<Map<dynamic, dynamic>,
I/flutter ( 3787): AsyncSnapshot<Map<dynamic, dynamic>>>#66400)
违规小部件:
import 'package:flutter/material.dart';
import '../blocs/auth_bloc.dart';
class LoginPage extends StatelessWidget {
final authBloc = AuthBloc();
Map<String, bool> initialData = {'loginSuccess': false, 'isLoading': false};
@override
Widget build(BuildContext context) {
return StreamBuilder(
stream: authBloc.redirect,
initialData: initialData,
builder: (context, snapshot) {
return Scaffold(body: _render(context, snapshot));
});
}
Widget _render(BuildContext context, AsyncSnapshot snapshot) {
return !snapshot.data['loginSuccess'] && snapshot.data['isLoading']
? _circularSpinner()
: snapshot.data['loginSuccess'] && !snapshot.data['isLoading']
? _redirect(context)
: _buildPage();
}
_redirect(BuildContext context) {
return Navigator.pushReplacementNamed(context, '/dashboard');
}
Widget _buildPage() {
return Container(
margin: EdgeInsets.all(20.0),
child: Center(
child: SingleChildScrollView(
child: Column(
children: <Widget>[
_emailField(authBloc),
_padding(),
_passwordField(authBloc),
_padding(),
_submitButton(authBloc)
],
),
),
),
);
}
Widget _circularSpinner() {
return Center(
child: CircularProgressIndicator(),
);
}
Widget _emailField(AuthBloc authBloc) {
return StreamBuilder(
stream: authBloc.email,
builder: (BuildContext context, snapshot) {
return TextField(
onChanged: authBloc.emailChanged,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
hintText: 'you@example.com',
labelText: 'Email Address',
errorText: snapshot.error,
border: OutlineInputBorder(),
),
);
},
);
}
Widget _passwordField(AuthBloc authBloc) {
return StreamBuilder(
stream: authBloc.password,
builder: (BuildContext context, snapshot) {
return TextField(
onChanged: authBloc.passwordChanged,
obscureText: true,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
hintText: '8 characters or more with at least 1 number',
labelText: 'Password',
errorText: snapshot.error,
border: OutlineInputBorder(),
),
);
},
);
}
Widget _padding() {
return Padding(
padding: EdgeInsets.only(top: 20.0),
);
}
Widget _submitButton(AuthBloc authBloc) {
return StreamBuilder(
stream: authBloc.submitValid,
builder: (context, snapshot) {
return RaisedButton(
child: Text('Login'),
color: Colors.blue,
onPressed: snapshot.hasError ? null : authBloc.submitForm,
);
});
}
}
我已经在Google上进行了搜索,但找不到与Navigator相关的任何内容。
我希望该小部件重定向到“仪表板”小部件。相反,我得到红色错误屏幕。
答案 0 :(得分:1)
我今天遇到了同样的问题。我尝试了@ user8467470的解决方案,但StreamBuilder已注册到我的Stream中。所以我最终得到了这个解决方案:
@override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<Response<ExchangeRate>>(
stream: _bloc.exchangeDataStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
switch (snapshot.data.status) {
case Status.LOADING:
return LoadingWidget(loadingMessage: snapshot.data.message);
case Status.COMPLETED:
WidgetsBinding.instance.addPostFrameCallback((_) {
Navigator.pushReplacementNamed(context, '/home');
});
break;
case Status.ERROR:
return Container(
child: Text(snapshot.data.message),
);
}
}
return Container();
}));
}
这样,我的集团中只有一个流。
答案 1 :(得分:0)
我找到了一种解决方法,但这并不漂亮。我以为在其他任何人遇到相同问题的情况下也会在这里分享它,但是我当然希望其他更有经验的Flutter开发人员可以提供更好的解决方案和理由。
似乎出于任何原因,StreamBuilder都不喜欢您,由于其构建流程而无法使用构建器功能。结果,我将导航移到了StreamBuilder之外,但移到了build方法中,并添加了一个像这样的侦听器:
@override
Widget build(BuildContext context) {
// Manually listening for redirect conidtions here
// once a response is received from the server
authBloc.loginSuccess.listen((data) {
if (data) {
Navigator.pushReplacementNamed(context, '/dashboard');
}
});
return StreamBuilder(
stream: authBloc.isLoading,
initialData: false,
builder: (context, snapshot) {
print(snapshot.data);
return Scaffold(
body: snapshot.data ? _circularSpinner() : _buildPage(),
);
});
}
它现在似乎可以工作,但是我会继续关注最佳实践。