'How to access Provided (Provider.of()) value inside showModalBottomSheet?
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?
Solution 1:[1]
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());
},
);
}
Solution 2:[2]
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),
);
},
);
}
Solution 3:[3]
You need move Provider to top layer(MaterialApp)
According to picture, Dialog widget is under MaterialApp, so this is why you using wrong context
Solution 4:[4]
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),
),
);
}
}
Solution 5:[5]
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);}),);
}
}
);
}
Solution 6:[6]
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.
Solution 7:[7]
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(),
),
);
Solution 8:[8]
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.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Rémi Rousselet |
Solution 2 | Mobina |
Solution 3 | CH Wing |
Solution 4 | ejabu |
Solution 5 | Dharman |
Solution 6 | Iron John Bonney |
Solution 7 | jbryanh |
Solution 8 | Amal Sunil |