I am having some problems with my flutter app. I am trying to add an overlay like this in the photo below:
And it works just fine, I am able to open it on long press and close it on tap everywhere else on the screen. The problem is that those two buttons - delete and edit - should call a bloc method that then do all the logic, but I do not have a bloc provider above the OverlayEntry. This is the error:
Error: Could not find the correct Provider<BrowseBloc> above this _OverlayEntryWidget Widget
This happens because you used a `BuildContext` that does not include the provider
of your choice. There are a few common scenarios:
- You added a new provider in your `main.dart` and performed a hot-reload.
To fix, perform a hot-restart.
- The provider you are trying to read is in a different route.
Providers are "scoped". So if you insert of provider inside a route, then
other routes will not be able to access that provider.
- You used a `BuildContext` that is an ancestor of the provider you are trying to read.
Make sure that _OverlayEntryWidget is under your MultiProvider/Provider<BrowseBloc>.
This usually happens when you are creating a provider and trying to read it immediately.
For example, instead of:
```
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// Will throw a ProviderNotFoundError, because `context` is associated
// to the widget that is the parent of `Provider<Example>`
child: Text(context.watch<Example>().toString()),
);
}
```
consider using `builder` like so:
```
Widget build(BuildContext context) {
return Provider<Example>(
create: (_) => Example(),
// we use `builder` to obtain a new `BuildContext` that has access to the provider
builder: (context, child) {
// No longer throws
return Text(context.watch<Example>().toString());
}
);
}
```
If none of these solutions work, consider asking for help on StackOverflow:
https://stackoverflow.com/questions/tagged/flutter
I've already encountered this error but this time I'm in a bit of trouble because I'm working with an overlay and not a widget.
This is my code:
late OverlayEntry _popupDialog;
class ExpenseCard extends StatelessWidget {
const ExpenseCard({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return BlocConsumer<AppBloc, AppState>(
listener: (context, state) {},
buildWhen: (previous, current) => previous.theme != current.theme,
builder: (context, state) {
return Column(
children: [
GestureDetector(
onLongPress: () {
_popupDialog = _createOverlay(expense);
Overlay.of(context)?.insert(_popupDialog);
},
child: Card(
...some widgets
),
),
const Divider(height: 0),
],
);
},
);
}
}
OverlayEntry _createOverlay(Expenses e) {
return OverlayEntry(
builder: (context) => GestureDetector(
onTap: () => _popupDialog.remove(),
child: AnimatedDialog(
child: _createPopupContent(context, e),
),
),
);
}
Widget _createPopupContent(BuildContext context, Expenses e) {
return GestureDetector(
onTap: () {},
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Container(
width: MediaQuery.of(context).size.width * 0.9,
decoration: BoxDecoration(
color: LocalCache.getActiveTheme() == ThemeMode.dark ? darkColorScheme.surface : lightColorScheme.surface,
borderRadius: const BorderRadius.all(Radius.circular(16)),
),
padding: const EdgeInsets.all(16.0),
child: Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
...some other widgets
],
),
),
SizedBox(
width: 256,
child: Card(
child: Column(
children: [
InkWell(
onTap: () {
_popupDialog.remove();
// This is where the error is been thrown
context.read<BrowseBloc>().add(SetTransactionToEdit(e));
showBottomModalSheet(
context,
dateExpense: e.dateExpense,
total: e.total,
transactionToEdit: e,
);
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
child: Row(
children: [Text(AppLocalizations.of(context).edit), const Spacer(), const Icon(Icons.edit)],
),
),
),
const Divider(height: 0),
InkWell(
onTap: () {
_popupDialog.remove();
// This is where the error is been thrown
context.read<BrowseBloc>().add(DeleteExpense(e.id!, e.isExpense));
},
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 16),
child: Row(
children: [Text(AppLocalizations.of(context).delete), const Spacer(), const Icon(Unicons.delete)],
),
),
),
],
),
),
),
],
),
);
}
How can I add the bloc provider above my OverlayEntry? Is this the best course of action?
Thank you to everyone that can help!
CodePudding user response:
Wrap your widget that you use in OverlayEntry
in BlocProvider.value
constructor and pass the needed bloc as an argument to it, like so
OverlayEntry _createOverlay(Expenses e, ExampleBloc exampleBloc) {
return OverlayEntry(
builder: (context) => GestureDetector(
onTap: () => _popupDialog.remove(),
child: BlocProvider<ExampleBloc>.value(
value: exampleBloc,
child: AnimatedDialog(
child: _createPopupContent(context, e),
),
),
),
);
}
CodePudding user response:
I have found a solution starting from the answer of Olga P, but changing one thing. I use the BlocProvider.value but I am passing as an argument to the method the context and not the bloc itself. This is the code:
OverlayEntry _createOverlay(Expenses e, BuildContext context) {
return OverlayEntry(
builder: (_) => GestureDetector(
onTap: () => _popupDialog.remove(),
child: BlocProvider<BrowseBloc>.value(
value: BlocProvider.of(context),
child: AnimatedDialog(
child: _createPopupContent(context, e),
),
),
),
);
}
With this change the two methods - edit and delete - work perfectly. Thanks to everyone who replied, I learned something today too!
CodePudding user response:
The problem is that you are using a function and not a widget. So you can either modify _createOverlay to be stateless or stateful widget, or you can pass the bloc as an argument to the function.
In the latter case this would be _createOverlay(expense, context.read<AppBloc>())