ProviderNotFoundException was thrown building ConsumptionDialog because used `BuildContext` that does not include the provider

Issue

I’m a student and I don’t learn mobile development at my school. I really appreciate it if anyone could help me. I dunno how to fix the error and I tried countless times.

Error: Could not find the correct Provider above this Builder Widget. This happens because you used a ‘BuildContext’ that does not include the provider of your choice.

The relevant error-causing widget was
ConsumptionDialog. 2 files were involved in this error.

1st File: consumption_dialog.dart

class ConsumptionDialog extends StatefulWidget {
  @override
  _ConsumptionDialogState createState() => _ConsumptionDialogState();
}

class _ConsumptionDialogState extends State<ConsumptionDialog> {
  final _form = GlobalKey<FormState>();
  String? _text;

  String? _validateText(String? value) {
    if (value == null) {
      return "2000 ml minimun";
    }

    final number = int.tryParse(value);
    if (number != null && number >= 2000) {
      return null;
    }

    return "2000 ml minimum";
  }

  @override
  Widget build(BuildContext context) {
    final bloc = context.watch<WaterBloc>();
    return AlertDialog(
      title: Text(
        "Daily consumption",
        textAlign: TextAlign.center,
        style: TextStyle(fontWeight: FontWeight.bold),
      ),
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
      content: Form(
        key: _form,
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          mainAxisSize: MainAxisSize.min,
          children: [
            Text(
              "Change your daily water consumption goal, in milliliters.",
              textAlign: TextAlign.center,
            ),
            SizedBox(height: 12),
            TextFormField(
              maxLength: 4,
              initialValue: bloc.state.recommendedMilliliters.toString(),
              keyboardType: TextInputType.number,
              onSaved: (value) => _text = value,
              validator: _validateText,
              autovalidateMode: AutovalidateMode.onUserInteraction,
              decoration: InputDecoration(
                hintText: "2000 ml",
                counterText: "",
              ),
            ),
            SizedBox(height: 24),
            PrimaryButton(
              onPressed: () {
                if (_form.currentState?.validate() ?? false) {
                  _form.currentState?.save();
                  FocusScope.of(context).unfocus();
                  context.read<WaterBloc>().setRecommendedMilliliters(
                        int.parse(_text!),
                      );
                  Navigator.of(context).pop();
                }
              },
              title: "Confirm",
            ),
            SizedBox(height: 10),
            SecondaryButton(
              onPressed: () => Navigator.of(context).pop(),
              title: "Cancel",
            ),
          ],
        ),
      ),
    );
  }
}

The error is shown inline:

  Widget build(BuildContext context) {
    final bloc = context.watch<WaterBloc>();
    return AlertDialog(

2nd File: dialog.dart

import 'package:flutter/material.dart';
import 'package:animations/animations.dart';

import 'package:stayhydratedpal/widgets/confirmation_dialog.dart';
import 'package:stayhydratedpal/widgets/consumption_dialog.dart';

Future<bool> showConfirmationDialog(
  BuildContext context, {
  required String title,
  required String content,
}) async {
  final bool confirmed = await showModal(
        context: context,
        builder: (context) {
          return ConfirmationDialog(
            title: title,
            content: content,
            onConfirm: () => Navigator.of(context).pop(true),
            onCancel: () => Navigator.of(context).pop(false),
          );
        },
      ) ??
      false;

  return confirmed;
}

Future<void> showConsumptionDialog(BuildContext context) {
  return showModal(
    context: context,
    builder: (context) => ConsumptionDialog(),
  );
}

The error is shown inline:

Future<void> showConsumptionDialog(BuildContext context) {
  return showModal(
    context: context,
    builder: (context) => ConsumptionDialog(),
  );
}

Solution

The way provider works is when you do Provider.of<T>(context)(The context must be a descendent of the widget where you injected T) it looks up the tree to find the T that you injected using Provider(create: (_)=> T())(can be ChangeNotifierProvider too doesn’t matter). Also routes in the navigator stack aren’t a parent of each other
they are

-> page1
-> page2

not

-> page1
   -> page2

So this means when you use the Navigator to push a new page, Provider won’t be able to find the injected provider you put on page1. And showModal uses Navigator push to open a dialog so basically just like any other route which means your ConfirmationDialog isn’t finding the WaterBloc which you probably injected in the page you are opening it from.

One way to solve this is inject WaterBloc above the Navigator, MaterialApp contains the root navigator so inject the provider above Material App.

Another way is when opening the dialog you can do

Future<void> showConsumptionDialog(BuildContext context) {
  return showModal(
    context: context,
    builder: (_) => Provider.value(
      value: context.read<WaterBloc>(), // this context must be a descendent of the widget where you injected WaterBloc
      child: ConsumptionDialog(),
    ),
  );
}

A small tip, I would recommend you to learn Inherited Widgets a bit if you learn them well you can use Provider pretty easily, because Provider is just a wrapper over InheritedWidget

Answered By – MD Ismail Alam Khan

Answer Checked By – Jay B. (FlutterFixes Admin)

Leave a Reply

Your email address will not be published. Required fields are marked *