Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

flutter

dart

bloc

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?

like image 350
DanT29 Avatar asked Sep 25 '19 17:09

DanT29


People also ask

What is repository provider in Flutter?

RepositoryProvider is a Flutter widget which provides a repository to its children via RepositoryProvider.of<T>(context) . It is used as a dependency injection (DI) widget so that a single instance of a repository can be provided to multiple widgets within a subtree.

Where do you put BlocProvider?

I generally prefer to put it in such a way as to wrap the Material App Widget, but many tend to put it deeper into the Widget Tree. In that case, I should pass my repository from widget to widget, so why should I put the BlocProvider very deep? In the code below I put an example of how I organize the code.

When should I use RepositoryProvider?

RepositoryProvider is used to provide a repository to its children. Normally you are going to use it when you need to create an instance of your repository class and then with the BlocProvider, you are going to access that repository with help of context.

What is BlocBuilder in Flutter?

BlocBuilder is a Flutter widget which requires a Bloc and a builder function. BlocBuilder handles building a widget in response to new states . BlocBuilder is very similar to StreamBuilder but has a more simple API to reduce the amount of boilerplate code needed.


1 Answers

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.

like image 133
GiancarloCode Avatar answered Nov 15 '22 08:11

GiancarloCode