简体   繁体   中英

When pushing new route in Flutter using flutter_bloc library doesn't work the context as I expected

When I login using the form in the signin_content.dart, I navigate to the home page. And then I can logout because I didn't push to another screen, so it works perfectly.

The problem is when I start the app, I see the first page that is signin_content.dart.

And then, if I push to signup_content.dart and I back to signin_content.dart and try to login, the context in the provider doesn't work.

BlocProvider.of<AuthenticationBloc>(context).add(LoggedIn()),

I already tried this solution but I need to see how to locate the code:

link

I tried to use the the provider as a parent of MaterialApp but how?

I am using this library:

Login Flow

main.dart

// imports ..

    class SimpleBlocDelegate extends BlocDelegate {
  @override
  void onEvent(Bloc bloc, Object event) {
    super.onEvent(bloc, event);
    print(event);
  }

  @override
  void onTransition(Bloc bloc, Transition transition) {
    super.onTransition(bloc, transition);
    print(transition);
  }

  @override
  void onError(Bloc bloc, Object error, StackTrace stacktrace) {
    super.onError(bloc, error, stacktrace);
    print(error);
  }
}

void main() {
  BlocSupervisor.delegate = SimpleBlocDelegate();
  final userRepository = UserRepository();
  runApp(
    BlocProvider<AuthenticationBloc>(
      create: (context) {
        return AuthenticationBloc(userRepository: userRepository)
          ..add(AppStarted());
      },
      child: App(userRepository: userRepository),
    ),
  );
}

app.dart

// imports..

    class App extends StatelessWidget {
      final UserRepository userRepository;

  const App({
    Key key,
    @required this.userRepository,
  })  : assert(userRepository != null),
        super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      theme: fitnessTheme(),
      routes: Routes.appRoutes,
      home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
        builder: (context, state) {
          if (state is AuthenticationLoading) {
            return LoadingIndicator();
          }
          if (state is AuthenticationUnauthenticated) {
            return SigninScreen();
          }
          if (state is AuthenticationProfileInactive) {
            return WelcomeScreen();
          }
          if (state is AuthenticationAuthenticated) {
            return HomeScreen();
          }

          return Splash();
        },
      ),
    );
  }
}

signin_content.dart

//imports..

class SigninContent extends StatefulWidget {
  SigninContent({Key key}) : super(key: key);

  _SigninContentState createState() => _SigninContentState();
}

class _SigninContentState extends State<SigninContent> {
  SigninFormBloc _signinFormBloc;

  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();

  final UserProvider _userProvider = UserProvider();

  @override
  void initState() {
    super.initState();
    _signinFormBloc = BlocProvider.of<SigninFormBloc>(context);
    _emailController.addListener(_onEmailChanged);
    _passwordController.addListener(_onPasswordChanged);
  }

  @override
  Widget build(BuildContext context) {
    final Size size = MediaQuery.of(context).size;

    print(BlocProvider.of<AuthenticationBloc>(context).state);

    return BlocBuilder<SigninFormBloc, SigninFormState>(
      builder: (context, state) {
        if (state.formSubmittedSuccessfully) {
          final authData = {
            'email': _emailController.value.text,
            'password': _passwordController.value.text,
          };

          _userProvider.signin(
            data: authData,
            success: () =>
                BlocProvider.of<AuthenticationBloc>(context).add(LoggedIn()),
            error: (message) {
              Scaffold.of(context).showSnackBar(
                buildSnackbar(
                  message,
                  Colors.red[700],
                  Colors.white,
                  Duration(seconds: 2),
                ),
              );
            },
          );

          // _emailController.clear();
          // _passwordController.clear();
          _signinFormBloc.add(FormReset());
        }

        return Column(
          children: <Widget>[
            Expanded(
              child: SingleChildScrollView(
                child: Container(
                  margin: EdgeInsets.only(top: 16.0),
                  padding: EdgeInsets.only(
                    top: 29.0,
                    left: 14.0,
                    right: 14.0,
                  ),
                  child: Column(
                    children: <Widget>[
                      textInputWidget(
                        controller: _emailController,
                        labelText: "Email",
                        hintText: 'Enter a valid email',
                        autovalidate: state.email.isEmpty ? false : true,
                        validator: (_) {
                          return state.isEmailValid ? null : 'Invalid Email';
                        },
                      ),
                      SizedBox(height: 20.0),
                      textInputWidget(
                        controller: _passwordController,
                        labelText: "Password",
                        hintText: 'Enter a valid password',
                        obscureText: true,
                        autovalidate: state.password.isEmpty ? false : true,
                        validator: (_) {
                          return state.isPasswordValid
                              ? null
                              : 'Invalid Password';
                        },
                      ),
                      SizedBox(height: 20.0),
                      primaryButton(
                        caption: "sign in",
                        context: context,
                        submit: state.isFormValid ? _onSubmitPressed : null,
                      ),
                      SizedBox(height: 10.0),
                      Row(
                        children: <Widget>[
                          Text("Need an account?"),
                          FlatButton(
                            materialTapTargetSize:
                                MaterialTapTargetSize.shrinkWrap,
                            textColor: Color(0xFF32AEE2),
                            child: Text(
                              "Sign up",
                              style: TextStyle(
                                fontSize: 14.0,
                                fontFamily: "SF Pro Text",
                              ),
                            ),
                            onPressed: () => Navigator.pushReplacementNamed(
                              context,
                              SignupScreen.routeName,
                            ),
                          )
                        ],
                      ),
                      Container(
                        child: Row(
                          mainAxisAlignment: MainAxisAlignment.start,
                          children: <Widget>[
                            Text("Forgot your password?"),
                            FlatButton(
                              padding: EdgeInsets.all(0),
                              materialTapTargetSize:
                                  MaterialTapTargetSize.shrinkWrap,
                              textColor: Color(0xFF32AEE2),
                              child: Text(
                                "Reset",
                                style: TextStyle(
                                  fontSize: 14.0,
                                  fontFamily: "SF Pro Text",
                                ),
                              ),
                              onPressed: () => Navigator.pushReplacementNamed(
                                context,
                                PasswordResetScreen.routeName,
                              ),
                            )
                          ],
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ],
        );
      },
    );
  }

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

  void _onEmailChanged() {
    _signinFormBloc.add(EmailChanged(email: _emailController.text));
  }

  void _onPasswordChanged() {
    _signinFormBloc.add(PasswordChanged(password: _passwordController.text));
  }

  void _onSubmitPressed() {
    _signinFormBloc.add(FormSubmitted());
  }
}

signup_content.dart

// imports..

class SignupContent extends StatefulWidget {
  SignupContent({Key key}) : super(key: key);

  _SignupContentState createState() => _SignupContentState();
}

class _SignupContentState extends State<SignupContent> {
  SignupFormBloc _signupFormBloc;


  final TextEditingController _nameController = TextEditingController();
  final TextEditingController _emailController = TextEditingController();
  final TextEditingController _dobController = TextEditingController();
  final TextEditingController _passwordController = TextEditingController();
  final TextEditingController _passwordConfirmationController =
      TextEditingController();

  final UserProvider _userProvider = UserProvider();

  @override
  void initState() {
    super.initState();
    _signupFormBloc = BlocProvider.of<SignupFormBloc>(context);
    _nameController.addListener(_onNameChanged);
    _emailController.addListener(_onEmailChanged);
    _dobController.addListener(_onDobChanged);
    _passwordController.addListener(_onPasswordChanged);
    _passwordConfirmationController.addListener(_onPasswordConfirmationChanged);
  }

  @override
  Widget build(BuildContext context) {
    final Size _size = MediaQuery.of(context).size;
    print(BlocProvider.of<AuthenticationBloc>(context).state);

    return BlocBuilder<SignupFormBloc, SignupFormState>(
      builder: (context, state) {
        if (state.formSubmittedSuccessfully) {
          final userData = {
            'name': _nameController.value.text,
            'email': _emailController.value.text,
            'password': _passwordController.value.text,
          };

          _userProvider.signup(
            userData: userData,
            success: () {
              BlocProvider.of<AuthenticationBloc>(context).add(
                SignedUp(
                  email: userData["email"],
                  password: userData["password"],
                ),
              );
            },
            error: (message) {
              Scaffold.of(context).showSnackBar(
                buildSnackbar(
                  message,
                  Colors.red[700],
                  Colors.white,
                  Duration(seconds: 2),
                ),
              );
            },
          );
          _signupFormBloc.add(FormReset());
        }

        return Column(
          children: <Widget>[
            authHeader(_size),
            Expanded(
              child: SingleChildScrollView(
                child: Container(
                  margin: EdgeInsets.only(top: 16.0),
                  padding: EdgeInsets.only(
                    top: 29.0,
                    left: 14.0,
                    right: 14.0,
                  ),
                  child: Column(
                    children: <Widget>[
                      textInputWidget(
                        controller: _nameController,
                        labelText: "Name",
                        hintText: 'Enter a valid name',
                        autovalidate: state.name.isEmpty ? false : true,
                        validator: (_) {
                          return state.isNameValid
                              ? null
                              : 'At least 6 characters long.';
                        },
                      ),
                      SizedBox(height: 20.0),
                      textInputWidget(
                        controller: _emailController,
                        labelText: "Email",
                        hintText: 'Enter a valid email',
                        autovalidate: state.email.isEmpty ? false : true,
                        validator: (_) {
                          return state.isEmailValid ? null : 'Invalid Email';
                        },
                      ),
                      SizedBox(height: 20.0),
                      textInputWidget(
                        controller: _passwordController,
                        labelText: "Password",
                        hintText: 'Enter a valid password',
                        obscureText: true,
                        autovalidate: state.password.isEmpty ? false : true,
                        validator: (_) {
                          return state.isPasswordValid
                              ? null
                              : 'Invalid Password';
                        },
                      ),
                      SizedBox(height: 20.0),
                      textInputWidget(
                        controller: _passwordConfirmationController,
                        labelText: "Password Confirmation",
                        hintText: 'Enter a valid password',
                        autovalidate:
                            state.passwordConfirmation.isEmpty ? false : true,
                        validator: (_) {
                          return state.isPasswordConfirmationValid
                              ? null
                              : 'Invalid Password';
                        },
                        obscureText: true,
                      ),
                      SizedBox(height: 20.0),
                      primaryButton(
                        caption: "sign up",
                        context: context,
                        submit: state.isFormValid ? _onSubmitPressed : null,
                      ),
                      SizedBox(height: 10.0),
                      Row(
                        children: <Widget>[
                          Text("Already Registered?"),
                          FlatButton(
                            materialTapTargetSize:
                                MaterialTapTargetSize.shrinkWrap,
                            textColor: Color(0xFF32AEE2),
                            child: Text(
                              "Sign in",
                              style: TextStyle(
                                fontSize: 14.0,
                                fontFamily: "SF Pro Text",
                              ),
                            ),
                            onPressed: () => Navigator.pushReplacementNamed(
                              context,
                              SigninScreen.routeName,
                            ),
                          )
                        ],
                      ),
                    ],
                  ),
                ),
              ),
            ),
          ],
        );
      },
    );
  }

  @override
  void dispose() {
    _nameController.dispose();
    _emailController.dispose();
    _passwordController.dispose();
    _passwordConfirmationController.dispose();
    super.dispose();
  }


  void _onNameChanged() {
    _signupFormBloc.add(NameChanged(name: _nameController.text));
  }

  void _onEmailChanged() {
    _signupFormBloc.add(EmailChanged(email: _emailController.text));
  }


  void _onPasswordChanged() {
    _signupFormBloc.add(PasswordChanged(password: _passwordController.text));
  }

  void _onPasswordConfirmationChanged() {
    _signupFormBloc.add(
      PasswordConfirmationChanged(
        password: _passwordController.text,
        passwordConfirmation: _passwordConfirmationController.text,
      ),
    );
  }

  void _onSubmitPressed() {
    _signupFormBloc.add(
      FormSubmitted(),
    );
  }

}

This is because in the SignupContent you are replacement the home of your MaterialApp , so the BlocBuilder is removed and the widget does not change even if the authentication state changes.

To fix it, I recommend that you delete the BlocBuilder and use a BlocListener in each screen instead, in this way you can use the Navigator without any problem.

So change your main to

void main() {
  BlocSupervisor.delegate = SimpleBlocDelegate();

  final userRepository = UserRepository(); 
  final authenticationBloc = AuthenticationBloc(userRepository: userRepository);

  authenticationBloc
      .firstWhere((state) =>
          state is! AuthenticationUninitialized &&
          state is! AuthenticationLoading)
      .then((state) => runApp(App(
            authenticationBloc: authenticationBloc,
            home: state is AuthenticationUnauthenticated
                ? SigninScreen()
                : HomeScreen(),
          )));

  authenticationBloc.add(AppStarted());
}

Also change your App widget

class App extends StatelessWidget {
  const App({
    Key key,
    @required this.home,
    @required this.authenticationBloc,
  }) : super(key: key);

  final Widget home;
  final AuthenticationBloc authenticationBloc;

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: <BlocProvider>[
        BlocProvider<AuthenticationBloc>.value(value: authenticationBloc),
      ],
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        theme: fitnessTheme(),
        routes: Routes.appRoutes,
        home: home,
      ),
    );
  }
}

Then use a BlocListener in your SigninScreen and SignupScreen for navigate, and show loading dialog when the authentication state changes.

class SigninScreen extends StatelessWidget {
  static const String routeName = "signin";

  const SigninScreen({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    ScreenRatio.setScreenRatio(
      MediaQuery.of(context).size.height,
      MediaQuery.of(context).size.width,
    );
    return BlocProvider(
        create: (context) => SigninFormBloc(),
        child: BlocListener<AuthenticationBloc, AuthenticationState>(
          listener: (context, state) {
            if (state is AuthenticationLoading) {
              LoadingDialog.show(context);
            } else if (state is AuthenticationUnauthenticated) {
              LoadingDialog.hide(context);
            } else if (state is AuthenticationAuthenticated) {
              LoadingDialog.hide(context);
              Navigator.of(context).pushNamedAndRemoveUntil(
                HomeScreen.routeName, (route) => false);
            }
          },
          child: Scaffold(
            body: SafeArea(
              child: SigninContent(),
            ),
          ),
        ));
  }
}

class SignupScreen extends StatelessWidget {
  static const String routeName = "signup";

  const SignupScreen({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    ScreenRatio.setScreenRatio(
        MediaQuery.of(context).size.height, MediaQuery.of(context).size.width);
    return BlocProvider(
        create: (context) => SigninFormBloc(),
        child: BlocListener<AuthenticationBloc, AuthenticationState>(
          listener: (context, state) {
            if (state is AuthenticationLoading) {
              LoadingDialog.show(context);
            } else if (state is AuthenticationUnauthenticated) {
              LoadingDialog.hide(context);
            } else if (state is AuthenticationAuthenticated) {
              LoadingDialog.hide(context);
              Navigator.of(context).pushNamedAndRemoveUntil(
                HomeScreen.routeName, (route) => false);
            }
          },
          child: Scaffold(
            body: SafeArea(
              child: SignupContent(),
            ),
          ),
        ));
  }
}

class LoadingDialog extends StatelessWidget {
  static void show(BuildContext context, {Key key}) {
    showDialog<void>(
      context: context,
      barrierDismissible: false,
      builder: (_) => LoadingDialog(key: key),
    );
  }

  static void hide(BuildContext context) {
    Navigator.pop(context);
  }

  LoadingDialog({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return WillPopScope(
      onWillPop: () async => false,
      child: Center(
        child: Card(
          child: Container(
            width: 80,
            height: 80,
            padding: EdgeInsets.all(12.0),
            child: CircularProgressIndicator(),
          ),
        ),
      ),
    );
  }
}

And finally in your HomeScreen use a BlocListener to navigate when the user has signed out

return BlocListener<AuthenticationBloc, AuthenticationState>(
      listener: (context, state) {
        if (state is AuthenticationUnauthenticated) {
          Navigator.of(context).pushNamedAndRemoveUntil(
              SigninScreen.routeName, (route) => false);
        }
      },
      child: Scaffold(
        body: Center(
          child: RaisedButton(
            onPressed: () {
              BlocProvider.of<AuthenticationBloc>(context).add(LoggedOut());
            },
            child: Text("Sign out"),
          ),
        ),
      ),
    );

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM