Какой модный способ использовать SnackBar в StreamBuilder? - PullRequest
0 голосов
/ 17 января 2019

Я реализую шаблон 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 / флаттер (30505): ИСКЛЮЧЕНИЕ, ЗАПРЕЩЕННОЕ БИБЛИОТЕКОЙ ВИДЖЕТОВ ╞═════════════════════════════════════════════════ ══════════ I / флаттер (30505): следующее утверждение было брошено здание StreamBuilder (грязный, состояние: I / флаттер (30505): _StreamBuilderBaseState> # bd8b2): I / flutter (30505): setState () или markNeedsBuild () вызывается во время сборки. I / flutter (30505): этот виджет Scaffold нельзя пометить как необходимый построить, потому что фреймворк уже находится в I / флаттер (30505): Процесс построения виджетов. Виджет может быть помечен как необходимый построен на этапе сборки I / флаттер (30505): только если один из его предки сейчас строят. Это исключение разрешено, потому что Framework I / flutter (30505): создает родительские виджеты перед дочерними, что означает, что грязный потомок всегда будет построен. I / трепетание (30505): в противном случае платформа может не посещать этот виджет во время этот этап сборки. I / flutter (30505): виджет, для которого setState () или Был вызван markNeedsBuild (): I / flutter (30505):
Scaffold- [LabeledGlobalKey # 5bdc5] (состояние: ScaffoldState # 61be4 (тикеры: отслеживание 2 I / флаттер (30505): тикеры))

1 Ответ

0 голосов
/ 17 января 2019

Во-первых, вы должны убедиться, что вы всегда возвращаете виджет

и затем вы можете запланировать SnackBar для конца кадра

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

Вы также должны проверить, является ли data нулевым или snapshot имеет данные.

...