BLOC losing context when navigating to a new page
Asked Answered
A

1

7

I'm using the BLOC pattern to authenticate a user in my app. I have a main BlocProvider that wraps my app. And a BlocBuilder to build according to the authentication state.

If the user is unauthenticated i have onboarding / intro screens that will navigate to the login screen.

The login screen is wrapped in another BlocProvider that contains a button that will do the login, and add a logged in event when the login is successful.

Problem is when i navigate from the onboarding screens i loose the main authenticationBloc context. What do i need to to to have access to the authentication bloc after i pushed a new screen.

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  Bloc.observer = SimpleBlocObserver();

  runApp(
    MyApp(),
  );
}

class AuthenticationWrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: BlocProvider<AuthenticationBloc>(
        create: (context) => AuthenticationBloc()..add(AppStarted()),
        child: MyApp(),
      ),
    );
  }
}
class MyApp extends StatelessWidget {

  @override
  Widget build(BuildContext context) {
    return BlocListener<AuthenticationBloc, AuthenticationState>(
      listener: (context, state) {
        if (state is Authenticated) {
          _appUserProfileRepository = AppUserProfileRepository();
        }
      },
      child: BlocBuilder<AuthenticationBloc, AuthenticationState>(
        builder: (context, state) {

          _authCredentialHelper = state.authCredentialHelper;

          if (state is Uninitialized) {
            return SplashScreen();
          }

          if (state is Unauthenticated) {
            return OnboardingScreens(authCredentialHelper: _authCredentialHelper);
          }

          if (state is InvalidRegistration) {
            return RegisterProfileScreen(authCredentialHelper: _authCredentialHelper);
          }

          if (state is Authenticated) {
              xxx
          }

          return Scaffold(body: Center(child: LoadingIndicator()));
        },
      ),
    );
  }
}

This is the onboarding screen where i loose the authenticationbloc context as soon as i navigate

class OnboardingScreens extends StatelessWidget {
  final AuthCredentialHelper authCredentialHelper;

  OnboardingScreens({this.authCredentialHelper});

  _pages(BuildContext context) {
    return [
      xxx
    ];
  }

  _getStartedClicked(BuildContext context) {
    Navigator.push(context, MaterialPageRoute(builder: (context) {
      return LoginScreen(authCredentialHelper: authCredentialHelper);
    }));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: IntroductionScreen(
          pages: _pages(context),
          onDone: () => _getStartedClicked(context),
          showSkipButton: true,
          done: xxx
        ),
      ),
    );
  }
}

When adding a breakpoint at 1. the context is fine with a valid value for BlocProvider.of(context)

Stepping to 2. gives me an error: BlocProvider.of() called with a context that does not contain a Cubit of type AuthenticationBloc.

  _getStartedClicked(BuildContext context) {
    1----->Navigator.push(context, MaterialPageRoute(builder: (context) {
    2----->return LoginScreen(authCredentialHelper: authCredentialHelper);
    }));
  }

This is the LoginScreen code

class LoginScreen extends StatelessWidget {
  final AuthCredentialHelper authCredentialHelper;

  LoginScreen({this.authCredentialHelper});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          icon: Icon(Icons.arrow_back, color: darkBlue),
          onPressed: () => Navigator.of(context).pop(),
        ),
        backgroundColor: Colors.transparent,
        elevation: 0.0,
      ),
      body: SafeArea(
        child: Center(
          child: BlocProvider<LoginBloc>(
            create: (context) => LoginBloc(authCredentialHelper: authCredentialHelper),
            child: LoginForm(authCredentialHelper: authCredentialHelper),
          ),
        ),
      ),
    );
  }
}

Getting this error:

The following assertion was thrown building _InheritedProviderScope<LoginBloc>(value: Instance of 'LoginBloc'):
BlocProvider.of() called with a context that does not contain a Cubit of type AuthenticationBloc.

No ancestor could be found starting from the context that was passed to BlocProvider.of<AuthenticationBloc>().

This can happen if the context you used comes from a widget above the BlocProvider.
Asternal answered 24/8, 2020 at 11:50 Comment(2)
You can either manually pass the authbloc instance to the new route and re- provide it with another BlocProvider, or just don't bother with Navigator routes and have another Bloc layer to handle which page is displayed.Oak
This is because when you call navigator.push(context, ... (context){ ...}), the second context is different from the first.Oak
P
13

Change this :

   Navigator.push(context, MaterialPageRoute(builder: (context) {
      return LoginScreen(authCredentialHelper: authCredentialHelper);
   }));

to

   Navigator.push(
     context,
     MaterialPageRoute(builder: (contextLoginScreen) {
        return BlocProvider.value(
            value: context.bloc<AuthenticationBloc>(),
            child: LoginScreen(authCredentialHelper: authCredentialHelper));
     }),
   );
Paraffin answered 24/8, 2020 at 12:8 Comment(2)
PS. Going to the login screen directly from the authentication screen works fine. Ie. Adding return LoginScreen(authCredentialHelper: authCredentialHelper); when the state is UnauthenticatedAsternal
I moved the Authentication bloc up one level and amended the original question. I'm still getting an error though. The following assertion was thrown building _InheritedProviderScope<LoginBloc>(value: Instance of 'LoginBloc'): BlocProvider.of() called with a context that does not contain a Cubit of type AuthenticationBloc. No ancestor could be found starting from the context that was passed to BlocProvider.of<AuthenticationBloc>().Asternal

© 2022 - 2024 — McMap. All rights reserved.