32

I have a FloatingActionButton inside a widget tree which has a BlocProvider from flutter_bloc. Something like this:

BlocProvider(
  builder: (context) {
    SomeBloc someBloc = SomeBloc();
    someBloc.dispatch(SomeEvent());

    return someBloc;
  },
  child: Scaffold(
    body: ...
    floatingActionButton: FloatingActionButton(
      onPressed: _openFilterSchedule,
      child: Icon(Icons.filter_list),
    ),
  )
);

Which opens a modal bottom sheet:

void _openFilterSchedule() {
    showModalBottomSheet<void>(
      context: context,
      builder: (BuildContext context) {
        return TheBottomSheet();
      },
    );
  }

I am trying to access SomeBloc using BlocProvider.of<SomeBloc>(context) inside TheBottomSheet but I get the following error:

BlocProvider.of() called with a context that does not contain a Bloc of type SomeBloc.

I have tried to use the solution described in https://stackoverflow.com/a/56533611/2457045 but only works for BottomSheet and not ModalBottomSheet.


Note: This is not restricted to BlocProvider or flutter_bloc. Any Provider from the provider package has the same behaviour.

How can I access BlocProvider.of<SomeBloc>(context) inside the showModalBottomSheet?

In case it's not possible to do that, how to adapt https://stackoverflow.com/a/56533611/2457045 solution to Modal Bottom Sheet?

Henrique Arthur
  • 988
  • 1
  • 9
  • 17

10 Answers10

58

InheritedWidgets, and therefore Providers, are scoped to the widget tree. They cannot be accessed outside of that tree.

The thing is, using showDialog and similar functions, the dialog is located in a different widget tree – which may not have access to the desired provider.

It is therefore necessary to add the desired providers in that new widget tree:

void myShowDialog() {
  final myModel = Provider.of<MyModel>(context, listen: false);
  showDialog(
    context: context,
    builder: (_) {
      return Provider.value(value: myModel, child: SomeDialog());
    },
  );
}
Rémi Rousselet
  • 256,336
  • 79
  • 519
  • 432
11

Provider in showModalBottomSheet (Bottom-Sheet)

void myBottomSheet() {
  final myModel = Provider.of<MyModel>(context, listen: false);
  showModalBottomShee(
    context: context,
    builder: (_) {
      return ListenableProvider.value(
        value: myModel,
        child: Text(myModel.txtValue),
      );
    },
  );
}
Mobina
  • 6,369
  • 2
  • 25
  • 41
Avinash Kahar
  • 129
  • 1
  • 5
7

You need move Provider to top layer(MaterialApp)

According to picture, Dialog widget is under MaterialApp, so this is why you using wrong context

enter image description here

CH Wing
  • 1,062
  • 1
  • 12
  • 21
1

wrap your whole child widget inside the consumer.

void myShowDialog() {
  showDialog(
    context: context,
    builder: Consumer<MyModel>(
        builder: (context, value, builder) {
        retuen widget(); 
      }
  );
}
0

You should split Scaffold widget and its children, to another StatefulWidget

From single Widget

class MainScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      builder: (context) {
        SomeBloc someBloc = SomeBloc();
        someBloc.dispatch(SomeEvent());
        return someBloc;
      },
      child: Scaffold(
        body: ...
        floatingActionButton: FloatingActionButton(
          onPressed: _openFilterSchedule,
          child: Icon(Icons.filter_list),
        ),
      )
    );
  }
}

Splitted into these two widget

class MainScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      builder: (context) {
        SomeBloc someBloc = SomeBloc();
        someBloc.dispatch(SomeEvent());
        return someBloc;
      },
      child: Screen(),
    );
  }
}

and ..

class Screen extends StatelessWidget {

  void _openFilterSchedule() {
    showModalBottomSheet<void>(
      context: context,
      builder: (BuildContext context) {
        return TheBottomSheet();
      },
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: ...
      floatingActionButton: FloatingActionButton(
        onPressed: _openFilterSchedule,
        child: Icon(Icons.filter_list),
      ),
    );
  }
}
ejabu
  • 2,998
  • 23
  • 31
  • 1
    I had to change `Screen` to `Stateful` in order to use showModalBottomSheet, but it does not work. Same error is throwed inside `TheBottomSheet`. – Henrique Arthur Aug 18 '19 at 20:19
0

I found a solution, Just return your showModalBottomSheet with a StatefulBuilder and use the context of your modalsheet builder to pass to your provider. a snippet of my code below:

Future<Widget> showModal(int qty, Product product) async {
    return await showModalBottomSheet(
        isScrollControlled: true,
        backgroundColor: Colors.transparent,
        context: context,
        builder: (BuildContext ctx) {
          return StatefulBuilder(builder: (ctx, state) {
             return Container(
                  child: RaisedButton(
                          onPressed: () {
                             Product prod = Product(product.id, 
                                         product.sku, product.name, qty);
                             Provider.of<CartProvider>(ctx, listen: 
                                        false).addCart(prod);}),);
    }
  }
);
}
Dharman
  • 30,962
  • 25
  • 85
  • 135
Jayrek
  • 53
  • 2
  • 9
0

Not finding a clear explanation of adding multiple provided values, I thought I'd share here for reference.

   await showMobileModals(
    isDismissible: false,
    context: context,
    child: MultiProvider(
      providers: [
        Provider.value(
          value: provided_one,
        ),
        Provider.value(
          value: provided_two,
        ),
        Provider.value(
          value: provided_three,
        ),
      ],
      child: Container(),
    ),
  );
jbryanh
  • 1,193
  • 7
  • 17
0

I used ChangeNotifierProvider from provider and used ChangeNotifierProvider.value in the bottomsheet.

NOTE:
It's important to make sure to use the provider's builder context:
builder: (context, _) {}

ChangeNotifierProvider(
  create: (context) => MyProvider(),
  builder: (context, _) {
    return Scaffold(
      body: ...
      floatingActionButton: FloatingActionButton(
        onPressed: () => _openFilterSchedule(context),
        child: Icon(Icons.filter_list),
      ),
    ),
  },
);

The bottom sheet:

void _openFilterSchedule(BuildCOntext context) {
  showModalBottomSheet<void>(
    context: context,
    builder: (_) {
      return ChangeNotifierProvider<MyProvider>.value(
        value: context.watch<MyProvider>(),
        builder: (context, _) {
          final List<String> sampleDataToWatch =
              context.watch<MyProvider>().myData;

          if (sampleDataToWatch.isEmpty) {
            return const Center(child: CircularProgressIndicator());
          } else {
            return Column(
              children: sampleDataToWatch.map((name) => Text(name)).toList(),
            );
          }
        },
      );
    },
  );
}

Just a sample provider and the future changes:

class MyProvider extends ChangeNotifier {
  List<String> myData = [];

  MyProvider() {
    loadNames();
  }

  void loadNames() {
    Future.delayed(
      const Duration(seconds: 3),
      () {
        myData.addAll(['John', 'Maria', 'Lewis', 'Danny']);
        notifyListeners();
      },
    );
  }
}
Giraldi
  • 16,451
  • 6
  • 33
  • 52
-1

TLDR: Make sure your import statement's casings match your project's folder casings.

I came across one other quirk while debugging this same error. I had several providers that were all working, including in showModalBottomSheets, however one was not working. After combing through the entire widget tree, without finding any discrepancies, I found that I had capitalized the first letter of a folder on one of the import statements of my problem-child notifier. I think this confused the compiler and caused it to throw the Could not find the correct Provider above this widget error.

After ensuring the import statement casing matched the folder name, my provider problems were resolved. Hopefully this will save someone a headache.

Iron John Bonney
  • 2,451
  • 1
  • 17
  • 14
-2

Faced the same issue while dealing with showModelBottomSheet, since it happens to work in a different (context)widget tree I had to level up my state to that of the app so that I could access my provider using the context.

Code snippet

Amal Sunil
  • 133
  • 2