Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the fancy way to use SnackBar in StreamBuilder?

I'm implementing Bloc pattern for my application and I have to show SnackBar which shows error message when login is unauthenticated.

But I cannot show SnackBar during building phase of widget. I looked for lots of solutions, but I couldn't found.

What is the most efficient way to use this function?

My code

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): ══╡ EXCEPTION CAUGHT BY WIDGETS LIBRARY ╞═══════════════════════════════════════════════════════════ I/flutter (30505): The following assertion was thrown building StreamBuilder(dirty, state: I/flutter (30505): _StreamBuilderBaseState>#bd8b2): I/flutter (30505): setState() or markNeedsBuild() called during build. I/flutter (30505): This Scaffold widget cannot be marked as needing to build because the framework is already in the I/flutter (30505): process of building widgets. A widget can be marked as needing to be built during the build phase I/flutter (30505): only if one of its ancestors is currently building. This exception is allowed because the framework I/flutter (30505): builds parent widgets before children, which means a dirty descendant will always be built. I/flutter (30505): Otherwise, the framework might not visit this widget during this build phase. I/flutter (30505): The widget on which setState() or markNeedsBuild() was called was: I/flutter (30505):
Scaffold-[LabeledGlobalKey#5bdc5](state: ScaffoldState#61be4(tickers: tracking 2 I/flutter (30505): tickers))

like image 286
baeharam Avatar asked Jan 17 '19 06:01

baeharam


People also ask

How do you show SnackBar in Flutter Web?

Building, displaying, and customizing the SnackBar To get started building, displaying, and styling your SnackBar, first complete the following steps: Launch Android Studio or another IDE of your choice. Start a new Flutter project. Select Flutter Application and name the project something like “snackbardemo”

When should I use StreamBuilder?

If you need to build a widget dependent on the result of a Stream, you can utilize the StreamBuilder widget. You can make a Stream and pass it as the stream contention. Then, at that point, you need to pass an AsyncWidgetBuilder work that is utilized to construct a widget dependent on the snapshots of the Stream.

How do I create a StreamBuilder?

To use StreamBuilder , you need to call the constructor below. Basically, you need to create a Stream and pass it as the stream argument. Then, you have to pass an AsyncWidgetBuilder which can be used to build the widget based on the snapshots of the Stream .


1 Answers

First, you must ensure that you are always returning a widget

and then you can schedule the SnackBar for the end of the frame

if(state is AuthUnauthenticated){
  WidgetsBinding.instance.addPostFrameCallback((_) => _showErrorMessage(state.errorMessage));
  return Container();
}

You should also check if the data is null o the snapshot has data.

like image 176
chemamolins Avatar answered Oct 25 '22 14:10

chemamolins