在StreamBuilder中使用SnackBar的理想方式是什么?

时间:2019-01-17 06:40:36

标签: dart flutter snackbar

我正在为我的应用程序实现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(),
    );
  }
}

StackTrace

  

I / flutter(30505):W小部件库引起的异常提示   ╞═════════════════════════════════════════════════ ══════════我/扑   (30505):以下断言被抛出   StreamBuilder(脏,状态:I / flutter(30505):   _StreamBuilderBaseState>#bd8b2):I / flutter(30505):在构建期间调用setState()或markNeedsBuild()。   I / flutter(30505):此脚手架小部件无法标记为需要   构建,因为该框架已经在I / flutter中(30505):   构建小部件的过程。可以将小部件标记为需要   在构建阶段I / flutter(30505)中构建:仅当其中一个   祖先正在建造中。允许此例外,因为   框架I / flutter(30505):在子级之前构建父级小部件,   这意味着将永远建造一个肮脏的后代。我/扑   (30505):否则,框架可能在访问期间无法访问此小部件   这个构建阶段。 I / flutter(30505):在其上setState()或   被称为markNeedsBuild()的是:I / flutter(30505):
  支架-[LabeledGlobalKey#5bdc5](状态:   ScaffoldState#61be4(股票代码:跟踪2个I / flutter(30505):股票代码)

2 个答案:

答案 0 :(得分:5)

首先,您必须确保始终返回小部件

然后您可以将struct MateMessage newMate(char (*cr)[3]){ ...... } 安排在帧的结尾

SnackBar

您还应该检查if(state is AuthUnauthenticated){ WidgetsBinding.instance.addPostFrameCallback((_) => _showErrorMessage(state.errorMessage)); return Container(); } 是否为空,或者data是否有数据。

答案 1 :(得分:0)

另一种集团模式是从Bloc检索BuildContext而不是将其保存为状态。在这种情况下,您可以使用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(),
    );
  }
}