Bloc: showdialog prevents updating of screen

Created on 1 May 2020  路  5Comments  路  Source: felangel/bloc

Good day flutter bloc team, i have been playing with flutter bloc for the past couple of days, its really smoothed my development process, Great work.

But... i just have a small issue.
i am unable to show a widget after showDialog() method is fired in the bloc listener. i will pop the navigation stack when the state changes though, but in this case i will like to show a different widget once the navigation stack has been popped, like a "direct navigation". Here is my code sample to just to illustrate what i am talking about.

import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:trucks/blocs/bloc.dart';
import 'package:trucks/core/bootstrap.dart';

class LoginForm extends StatefulWidget {
  @override
  _LoginFormState createState() => _LoginFormState();
}

class _LoginFormState extends State<LoginForm> {
  final _formKey = GlobalKey<FormState>();
  final TextEditingController _authKeyController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();
  final FocusNode _authKeyNode = FocusNode();
  final FocusNode _passwordNode = FocusNode();
  bool _passwordVisible = false;
  String _authKeyError;
  String _passwordError;
  LoginBloc _bloc;

  @override
  void dispose() {
    super.dispose();
    _authKeyController.dispose();
    _passwordController.dispose();
  }

  @override
  void initState() {
    super.initState();
    _bloc = BlocProvider.of<LoginBloc>(context);
    _authKeyController.addListener(_onAuthKeyFieldChange);
    _passwordController.addListener(_onPasswordFieldChange);
  }

  @override
  Widget build(BuildContext context) {
    return BlocConsumer<LoginBloc, LoginState>(
      listener: (BuildContext context, LoginState state) {
        if (state is SuccessLoginState) {
          // pop the loading state to hide the show dialog method
          Navigator.pop(context);
        }

        if (state is LoadingLoginState) {
          showDialog(
            context: context,
            barrierDismissible: false,
            child: Spinner(
              title: "Logging in, Please Wait ...",
            ),
          );
        }
      },
      builder: (BuildContext context, LoginState state) {
        if (state is SuccessLoginState) {
            return HomePage();
        }

        return Form(
          key: _formKey,
          child: ListView(
            children: <Widget>[
              Heading(
                title: "Welcome Back",
                subTitle: "Please log in to your account to continue",
              ),
              FormLabel("Email Addess / Phone Number"),
              Padding(
                padding: EdgeInsets.symmetric(
                  horizontal: 20,
                ),
                child: TextFormField(
                  controller: _authKeyController,
                  autofocus: true,
                  autovalidate: true,
                  validator: (value) {
                    if (state is FormValidationLoginState &&
                        state.authKeyFieldError != null) {
                      return state.authKeyFieldError;
                    }

                    return null;
                  },
                  focusNode: _authKeyNode,
                  onFieldSubmitted: (_) {
                    _authKeyNode.unfocus();
                    FocusScope.of(context).requestFocus(_passwordNode);
                  },
                  textInputAction: TextInputAction.next,
                  keyboardType: TextInputType.emailAddress,
                  decoration: InputDecoration(
                    errorText: _authKeyError ?? null,
                    hintText: "Enter your email address or phone number",
                  ),
                ),
              ),
              FormLabel("Password"),
              Padding(
                padding: EdgeInsets.symmetric(
                  horizontal: 20,
                ),
                child: TextFormField(
                  controller: _passwordController,
                  maxLength: 20,
                  validator: (value) {
                    if (state is FormValidationLoginState &&
                        state.passwordFieldError != null) {
                      return state.passwordFieldError;
                    }

                    return null;
                  },
                  focusNode: _passwordNode,
                  onFieldSubmitted: (_) {
                    _passwordNode.unfocus();
                  },
                  textInputAction: TextInputAction.done,
                  obscureText: !_passwordVisible,
                  maxLengthEnforced: true,
                  autovalidate: true,
                  decoration: InputDecoration(
                    errorText: _passwordError ?? null,
                    suffixIcon: Padding(
                      child: IconButton(
                        tooltip: !_passwordVisible
                            ? "Show Password"
                            : "Hide Password",
                        icon: Icon(!_passwordVisible
                            ? Icons.visibility
                            : Icons.visibility_off),
                        onPressed: () => setState(
                            () => _passwordVisible = !_passwordVisible),
                      ),
                      padding: EdgeInsets.all(10),
                    ),
                    hintText: "Enter your password",
                  ),
                ),
              ),
              Padding(
                child: Button(
                  isDisabled: _isSubmitButtonDisabled(state),
                  onPressed: _onSubmitForm,
                  title: "Log In",
                ),
                padding: EdgeInsets.symmetric(horizontal: 20, vertical: 20),
              ),
              Button(
                buttonType: ButtonType.PrimaryLink,
                onPressed: () => Routes.sailor(SendResetCode.routeID),
                title: "Forgot Password",
              ),
              Button(
                buttonType: ButtonType.PrimaryLink,
                onPressed: () => Routes.sailor.pop(),
                title: "Don't Have An Account ?  Sign Up",
              ),
            ],
          ),
        );
      },
    );
  }

  bool _isSubmitButtonDisabled(LoginState state) =>
      !_isFormValid(state) && _isPopulated;

  bool get _isPopulated =>
      _authKeyController.text.isNotEmpty || _passwordController.text.isNotEmpty;

  bool _isFormValid(LoginState state) =>
      state is FormValidationLoginState &&
      state.passwordFieldError == null &&
      state.authKeyFieldError == null;

  void _onAuthKeyFieldChange() {
    _authKeyError =
        null; // clear the error state set once the user starts correcting errors
    _bloc.add(
      ValidateAuthKeyFieldLoginEvent(
        authKey: _authKeyController.text,
      ),
    );
  }

  void _onPasswordFieldChange() {
    _passwordError =
        null; // clear the error state set once the user starts correcting errors
    _bloc.add(
      ValidatePasswordFieldLoginEvent(
        password: _passwordController.text,
      ),
    );
  }

  void _onSubmitForm() {
    _bloc.add(ButtonPressedLoginEvent(
        password: _passwordController.text, authKey: _authKeyController.text));
  }
}

the expected response here is that once i yield the success state, the showdialog() should be hidden using Navigator.pop(context), and then the home page widget should show, but what happens here is that the navigation stack is popped but the homepage never shows. i can see from my transitions that the success state is yielded, Please what am i doing wrong here?

question

Most helpful comment

Good day, thanks for your response, i have been able to fix the issue, i had to use the navigator key to be able to solve the issue, thanks

All 5 comments

Hi @peterson-umoke 馃憢
Thanks for opening an issue!

Can you explain why you expect the home page widget to show? The only code I see being executed in response to a success state is the Navigator.of(context).pop.

Also, it would be awesome, if you could share the link to a sample app which reproduces the issue so I can debug it locally. Thanks 馃憤

Hi @peterson-umoke 馃憢
Thanks for opening an issue!

Can you explain why you expect the home page widget to show? The only code I see being executed in response to a success state is the Navigator.of(context).pop.

Also, it would be awesome, if you could share the link to a sample app which reproduces the issue so I can debug it locally. Thanks 馃憤

hi, thanks for responding, if you look closely you will see the me checking for a success state in the builder, let me edit the code so it can be clearer

@peterson-umoke ah I see. So the dialog is being popped but the builder isn't rebuilding? Are you able to share a link to the app?

@peterson-umoke ah I see. So the dialog is being popped but the builder isn't rebuilding? Are you able to share a link to the app?

the code base is huge, let me see if i can slim it down.

Good day, thanks for your response, i have been able to fix the issue, i had to use the navigator key to be able to solve the issue, thanks

Was this page helpful?
0 / 5 - 0 ratings

Related issues

shawnchan2014 picture shawnchan2014  路  3Comments

timtraversy picture timtraversy  路  3Comments

rsnider19 picture rsnider19  路  3Comments

MahdiPishguy picture MahdiPishguy  路  3Comments

zjjt picture zjjt  路  3Comments