Using BLoC in a dialog: Could not find the correct Provider

2,282

I finally figured it out thanks to the so entry:

Flutter: bloc, how to show an alert dialog

The answer here is to use a stack to have the dialog and the page coexist and the dialog show when the listener has the correct state. The answer used a StatelessWidget to manage, in the case of the answer, SnackBar. To be able to add events to the bloc, I used a StatefulWidget.

@override
Widget build(BuildContext context) {
  return Scaffold(
    appBar: AppBar(
    title: Text("Business Region Manager"),
    ),
    body: MultiBlocProvider(
      providers: [
        BlocProvider(
          create: (context) => EntityModifyBloc(
              repository: xbrRepository), //widget.repository),
        ),
        BlocProvider(
          create: (context) => EntityBloc(
              repository: xbrRepository, //widget.repository,
              modifyBloc: BlocProvider.of<EntityModifyBloc>(context)),
        ),
        BlocProvider(
          create: (context) => BusinessRegionDialogBloc(
              repository: xbrRepository),
        ),
      ],
      child: Stack(children: [
        BusinessRegionDialogManager(widget.business.id),
        Center(child: BusinessRegionPage(widget.business)),
      ]),
    ));
}

class BusinessRegionDialogManager extends StatefulWidget {
  final int businessId;

  BusinessRegionDialogManager(this.businessId);

  State<BusinessRegionDialogManager> createState() =>
    _BusinessRegionDialogManagerState();
}

class _BusinessRegionDialogManagerState extends State {

// Callback functions from the dialog so can have access to
// BusinessRegionDialogBloc
void xnPositive(List<Region> addedRegions) {
    BlocProvider.of<BusinessRegionDialogBloc>(context)
      .add(AddBusinessRegions(addedRegions, widget.businessId));
}

void xnNegative(){
}


@override
Widget build(BuildContext context) {
  return BlocListener<BusinessRegionDialogBloc, BusinessRegionDialogState>(
    listener: (context, state) {
      if (state is RegionDialogLoaded) {
        showDialog(
          context: context,
          barrierDismissible: false,
          builder: (BuildContext context) {
            return BusinessRegionDialog(
              onPositive: xnPositive,
              onNegative: xnNegative,
              regions: state.regions,
            );
          })
      }
    },
    child: Container(),
  );
}
Share:
2,282
Nefarious
Author by

Nefarious

Updated on December 25, 2022

Comments

  • Nefarious
    Nefarious over 1 year

    I am attempting to use a bloc to manage the content of a dialog. I am fairly new to flutter.

    The page is defined as:

    @override
    Widget build(BuildContext context) {
      return Scaffold(
        appBar: AppBar(
          title: Text("Business Region Manager"),
        ),
        body: MultiBlocProvider(
          providers: [
            BlocProvider(
              create: (context) =>
                  EntityModifyBloc(repository: widget.repository),
            ),
            BlocProvider(
              create: (context) => EntityBloc(
                  repository: widget.repository,
                  modifyBloc: BlocProvider.of<EntityModifyBloc>(context)),
            ),
            BlocProvider(
              create: (context) => BusinessRegionBloc(repository: widget.repository),
            ),
          ],
          child: Center(
            child: BusinessRegionPage(widget.business),
          ),
        ));
    }
    

    Inside BusinessRegionPage I make use of EntityBloc and EntityModifyBloc and, I at least think I have that correct. The page has a callback that when fired calls a method:

    void onAddBusinessRegion(){
      showDialog(context: context,
        barrierDismissible: false,
        builder: (BuildContext context){
          return BusinessRegionDialog(
            positiveText: "Ok",
            onPositive: onPositive,
            negativeText: 'Cancel',
            onNegative: onNegative,
            businessId: widget.business.id,
          );
        });
    }
    

    BusinessRegionDialog is a stateful widget and the state implementation build is:

    @override
    Widget build(BuildContext context) {
      return BlocBuilder<BusinessRegionBloc, BusinessRegionState>(builder: (context, state) {
        if (state is BusinessRegionEmpty) {
          BlocProvider.of<BusinessRegionBloc>(context).add(GetBusinessRegions(widget.businessId));
        }
        if (state is BusinessRegionError) {
          return Center(
            child: Text('Busines Region Error'),
          );
        }
        if (state is BusinessRegionLoaded) {
          return Center(
            child: Text('BusinessRegionLoaded'),
          );
        }
        return Center(
          child: CircularProgressIndicator(),
        );
      });
    

    when the callback is fired, I get the message:

    Error: Could not find the correct Provider above this BlocBuilder<BusinessRegionBloc, BusinessRegionState> Widget

    This likely happens because you used a BuildContext that does not include the provider of your choice.

    I made the assumption that in the call to ShowDialog(context), the context is the same one as the one passed into build? I checked this assumption by printing the hashCode of the context before the showDialog call and after the build implementation on the BusinessRegionPage,

    One of the sugestions in the error message was:

    Widget build(BuildContext context) {
      return Provider<Example>(
        create: (_) => Example(),
        builder: (context) {
          return Text(context.watch<Example>()),
        }
      ),
    }
    

    so I tried:

    void onAddBusinessRegion() {
      print("OnAddCallback: " + context.hashCode.toString());
      BusinessRegionClient brClient = BusinessRegionClient(client: http.Client());
      BusinessRegionRepository brRepository =
          BusinessRegionRepository(client: brClient);
    
      showDialog(
        context: context,
        barrierDismissible: false,
        builder: (BuildContext context) {
          return Provider<BusinessRegionBloc>(
              create: (_) => BusinessRegionBloc(repository: brRepository),
              builder: (context) {
                return BusinessRegionDialog(
                  positiveText: "Ok",
                  onPositive: onPositive,
                  negativeText: 'Cancel',
                  onNegative: onNegative,
                  businessId: widget.business.id,
                );
              });
        });
    }
    

    The IDE tells me: The argument type 'BusinessRegionDialog Function(BuildContext)' can't be assigned to the parameter type 'Widget Function(BuildContext, Widget)'.

    I clearly don't understand something. All I want to do is fill a dialog with information from a Future from my repository. Any guidance will be appreciated.