在 StreamBuilder 中使用 SnackBar 的奇特方式是啥?
Posted
技术标签:
【中文标题】在 StreamBuilder 中使用 SnackBar 的奇特方式是啥?【英文标题】:What is the fancy way to use SnackBar in StreamBuilder?在 StreamBuilder 中使用 SnackBar 的奇特方式是什么? 【发布时间】:2019-06-11 07:44:56 【问题描述】:我正在为我的应用程序实现 Bloc 模式,我必须显示 SnackBar,它会在未验证登录时显示错误消息。
但我无法在小部件的构建阶段显示 SnackBar。我寻找了很多解决方案,但我找不到。
使用此功能最有效的方法是什么?
我的代码
import 'package:chat_app/auth/auth_bloc.dart';
import 'package:chat_app/auth/auth_state.dart';
import 'package:chat_app/main_page.dart';
import 'package:flutter/material.dart';
void main() => runApp(App());
class App extends StatelessWidget
@override
Widget build(BuildContext context)
return MaterialApp(
title: 'Chat App',
home: MyApp(),
debugShowCheckedModeBanner: false,
);
class MyApp extends StatefulWidget
@override
_MyAppState createState() => _MyAppState();
class _MyAppState extends State<MyApp>
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
final GlobalKey<ScaffoldState> _scaffoldKey = GlobalKey<ScaffoldState>();
final AuthBloc _bloc = AuthBloc();
final _emailController = TextEditingController();
final _passwordController = TextEditingController();
@override
void dispose()
_emailController.dispose();
_passwordController.dispose();
_bloc.dispose();
super.dispose();
@override
Widget build(BuildContext context)
return Scaffold(
key: _scaffoldKey,
appBar: AppBar(title: Text('Chat Example')),
body: StreamBuilder(
initialData: AuthInitializing(),
stream: _bloc.authStream,
builder: (BuildContext context, AsyncSnapshot<AuthState> snapshot)
AuthState state = snapshot.data;
if(state is AuthUnauthenticated)
_showErrorMessage(state.errorMessage);
if(state is AuthAuthenticated)
_moveNextPage(context);
return Form(
key: _formKey,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 20.0),
child: Column(
children: <Widget>[
TextFormField(
controller: _emailController,
keyboardType: TextInputType.emailAddress,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: '이메일',
),
),
SizedBox(height: 20.0),
TextFormField(
controller: _passwordController,
keyboardType: TextInputType.text,
obscureText: true,
decoration: InputDecoration(
border: OutlineInputBorder(),
labelText: '비밀번호'
),
),
SizedBox(height: 20.0),
RaisedButton(
child: Text('로그인',style: TextStyle(color: Colors.white),),
onPressed: () => _bloc.addLoginData(_emailController.text, _passwordController.text),
color: Theme.of(context).primaryColor,
),
SizedBox(height: 15.0),
state is AuthLoading ? _progressBar() : Container()
],
),
),
);
,
),
);
void _showErrorMessage(String message)
_scaffoldKey.currentState.showSnackBar(SnackBar(
content: Text(message),
));
void _moveNextPage(BuildContext context)
Navigator.pushReplacement(context, MaterialPageRoute(
builder: (_) => MainPage()
));
Widget _progressBar()
return Center(
child: CircularProgressIndicator(),
);
堆栈跟踪
I/flutter (30505): ══╡ 小部件库发现异常 ╞═════════════════════════════════════════════════ ══════════ I/颤振 (30505):以下断言被抛出构建 StreamBuilder(脏,状态:I/flutter(30505): _StreamBuilderBaseState>#bd8b2):I/flutter (30505):在构建期间调用 setState() 或 markNeedsBuild()。 I/flutter (30505):此 Scaffold 小部件无法标记为需要 build 因为框架已经在 I/flutter (30505) 中: 构建小部件的过程。可以将小部件标记为需要 在构建阶段 I/flutter (30505) 期间构建:仅当其中一个 祖宗目前正在建设中。这个例外是允许的,因为 framework I/flutter (30505):在子组件之前构建父组件, 这意味着将始终构建一个肮脏的后代。我/颤动 (30505):否则,框架可能不会在期间访问此小部件 这个构建阶段。 I/flutter (30505): setState() 或 markNeedsBuild() 被称为:I/flutter (30505): 脚手架-[LabeledGlobalKey#5bdc5](状态: ScaffoldState#61be4(tickers: tracking 2 I/flutter (30505): tickers))
【问题讨论】:
【参考方案1】:首先,您必须确保始终返回一个小部件
然后您可以将SnackBar
安排在帧的结尾
if(state is AuthUnauthenticated)
WidgetsBinding.instance.addPostFrameCallback((_) => _showErrorMessage(state.errorMessage));
return Container();
您还应该检查data
是否为空或snapshot
是否有数据。
【讨论】:
很好的答案!谢谢【参考方案2】:另一种 bloc 模式是从 BuildContext
检索 Bloc
而不是将其保存在状态中。在这种情况下,您可以使用BlocListener
而不是StreamBuilder
。
class _SnackbarListener extends StatelessWidget
@override
Widget build(BuildContext context)
return BlocListener< AuthBloc, AuthState>(
listener: (context, state)
if(state is AuthUnauthenticated)
_showErrorMessage(state.errorMessage));
,
child: Container(),
);
【讨论】:
以上是关于在 StreamBuilder 中使用 SnackBar 的奇特方式是啥?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Angular Material 7 中使用 CSS 操作 mat-snack-bar 模板的外观
在 StreamBuilder 中使用 AnimatedList
来自一个 StreamBuilder 的 Flutter 快照显示在另一个 StreamBuilder 中
在 StreamBuilder 中使用 SnackBar 的奇特方式是啥?