Repository provider in the flutter_bloc library doesn't provide repository with when pushing new route
Asked Answered
L

1

7

I am using the flutter_bloc library to architect my app. In addition to the BlocProvider I am using the Repository Provider, since I will be using a specific repository extensively throughout my app. But I am having an issue with regards to context . Below is snippets of my code:

main.dart

void main() async {
  .......
  appRepository _appRepository = AppRepository();
  SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp])
      .then((_) {
    runApp(
      BlocProvider(
        builder: (context) =>
            AuthenticationBloc(appRepository: _appRepository)..dispatch(AppStarted()),
        child: App(appRepository: _appRepository,),
      ),
    );
  });
}

class App extends StatelessWidget {

............
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
        builder: (BuildContext context, AuthenticationState state) {
       .....
          if (state is AuthenticationUnauthenticated) {
            return SafeArea(
              top: false,
              bottom: false,
              child: RepositoryProvider(
                builder: (context) => _appRepository,
                child: LoginPage(firebaseMessaging: _firebaseMessaging),
              ),
            );
          }
      ......

        },
      ),
    );
  }
}

Register button found in login form:

register_button.dart

class RegisterButton extends StatelessWidget {
  final FirebaseMessaging _firebaseMessaging;

  RegisterButton({
    Key key,
    @required FirebaseMessaging firebaseMessaging,
  })  : assert(firebaseMessaging != null),
        _firebaseMessaging = firebaseMessaging,
        super(key: key);

  @override
  Widget build(BuildContext context) {
    return Row(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text("Don't have an account?", style: TextStyle(color: Colors.black)),
        SizedBox(width: 4.0),
        GestureDetector(
          child: Text("Register here!",
              style: TextStyle(
                  color: Color(0xFF585B8D), fontWeight: FontWeight.w500)),
          onTap: () {
            Navigator.of(context).push(
              MaterialPageRoute(builder: (context) {
                return RegisterPage(
                  firebaseMessaging: _firebaseMessaging,
                );
              }),
            );
          },
        )
      ],
    );
  }

register_page.dart

class RegisterPage extends StatelessWidget {
  final FirebaseMessaging _firebaseMessaging;

  RegisterPage({
    Key key,
    @required FirebaseMessaging firebaseMessaging,
  })  : assert(firebaseMessaging != null),
        _firebaseMessaging = firebaseMessaging,
        super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocProvider(
        builder: (context) => RegisterBloc(
          appRepository: RepositoryProvider.of<AppRepository>(context),
          firebaseMessaging: _firebaseMessaging,
        ),
        child: RegisterForm(),
      ),
    );
  }
}

Question:

I'm getting an error when I click on the register button on my login form that says the following:

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

This can happen if:
1. The context you used comes from a widget above the RepositoryProvider.
2. You used MultiRepositoryProvider and didn't explicity provide the RepositoryProvider types.

Good: RepositoryProvider<AppRepository>(builder: (context) => AppRepository())
Bad: RepositoryProvider(builder: (context) => AppRepository()).

The context used was: BlocProvider<RegisterBloc>(dirty, state: _DelegateWidgetState#a87b2(lifecycle state: created))

Why am I getting this error? This problem seems to be fixed if I put the repository provider as the child of the blocprovider and app as the child repository provider in the main function and then deleting the invidual repository providers in App(). I'm guessing the issue is from pushing the material page route from the button. I don't think I understand how context or provider exactly works in Flutter. I thought the provider would look up the widget tree for the repository/bloc, does pushing a route some how break this continuity?

Labannah answered 25/9, 2019 at 17:25 Comment(0)
F
15

When you use Navigator.of(context).push or Navigator.of(context).pushNamed the widget pushed is not a child of the widget that call Navigator.of(context).push or Navigator.of(context).pushNamed, this widget is a child of the closest instance of Navigator that encloses the given context, in your case the Navigator is created by the MaterialApp, so if you want to provide the Repository or Bloc to different routes, the Provider must be a parent of the Navigator, in your case must be a parent of MaterialApp.

Frump answered 26/9, 2019 at 18:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.