skip to Main Content

I have a page which contains a button. When the user presses the button, a dialog box shows up which contains a bunch of textfields, etc. There is a toggle in that dialog box which changes the GridView based on what the user toggles.

The set up is:
In my main class, I have:

void main() {
  Bloc.observer = const GlobalApplicationObserver();
  ExpenditureRepository expenditureRepository = ExpenditureRepository();
  //runApp(MyApp());
  runApp(MaterialApp(
      home: MultiBlocProvider(
    providers: [
      // BlocProvider<OnboardingBloc>(
      //     create: (BuildContext context) => OnboardingBloc()),
      BlocProvider<FinanceBloc>(
          create: (BuildContext context) => FinanceBloc(expenditureRepository)),
    ],
    child: OnboardingPageWidget(),
  )));
}

In my OnboardingPageWidget (a stateful widget, I have, I have a button):

Center(
child: IconButton(
style: TextButton.styleFrom(
backgroundColor: Colors.black,
foregroundColor: Colors.white),
onPressed: () => showDialog<String>(
context: context,
builder: (BuildContext _) => expenditureForm
.showAddExpenditureDialogBox(context, true),
),
tooltip: 'Click to add an expense!',
icon: const Icon(Icons.add),
))

And in my showAddExpenditureDialogBox method, it consists of:

class ExpenditureForm {
  AlertDialog showAddExpenditureDialogBox(
      BuildContext context, isFixedExpenditure) {
    final amountTextEditingController = TextEditingController();
    final IncomeOrExpenseToggle incomeExpense = IncomeOrExpenseToggle(context);
    final List<String> list = <String>['Travel', 'Food', 'Bill'];
    final nameOfExpenditureTextEditingController = TextEditingController();
    String categoryOfExpenditure = "Travel";

    return AlertDialog(
      title: const Text('Expenditure'),
      content: const Text('Input details about it!'),
      actions: <Widget>[
        Column(
          children: [
            Container(child: Center(child: incomeExpense)),

            Row(
              children: [
                Expanded(
                  flex: 3,
                  child: Padding(
                    padding: const EdgeInsets.all(16.0),
                    child: TextField(
                      controller: nameOfExpenditureTextEditingController,
                      decoration: InputDecoration(hintText: 'Name of expense'),
                    ),
                  ),
                ),
                Expanded(
                  flex: 1,
                  child: Container(
                    width: MediaQuery.of(context).size.width * 0.3,
                    child: TextField(
                      controller: amountTextEditingController,
                      decoration: InputDecoration(hintText: 'Amount - £'),
                    ),
                  ),
                ),
              ],
            ),

            //This is practising drop down menus
            DropdownMenu<String>(
              initialSelection: list.first,
              onSelected: (value) => {categoryOfExpenditure = value!},
              dropdownMenuEntries:
                  list.map<DropdownMenuEntry<String>>((String value) {
                return DropdownMenuEntry<String>(value: value, label: value);
              }).toList(),
            ),
            // SizedBox(
            //   child: BlocBuilder<FinanceBloc, FinanceState>(
            //       buildWhen: (previous, current) => current is TypeToggledState,
            //       builder: (context, state) {
            //         if (state is TypeToggledState) {
            //           if (state.income) {
            //             return SizedBox(
            //               width: MediaQuery.of(context).size.width * 1,
            //               height: MediaQuery.of(context).size.height * 0.5,
            //               child: GridView.count(
            //                 crossAxisCount: 2,
            //                 children: List.generate(100, (index) {
            //                   return Center(
            //                     child: Text(
            //                       'Item $index',
            //                       style:
            //                           Theme.of(context).textTheme.headlineSmall,
            //                     ),
            //                   );
            //                 }),
            //               ),
            //             );
            //           } else {
            //             return SizedBox(
            //               width: MediaQuery.of(context).size.width * 1,
            //               height: MediaQuery.of(context).size.height * 0.5,
            //               child: GridView.count(
            //                 crossAxisCount: 2,
            //                 children: List.generate(23, (index) {
            //                   return Center(
            //                     child: Text(
            //                       'Item $index',
            //                       style:
            //                           Theme.of(context).textTheme.headlineSmall,
            //                     ),
            //                   );
            //                 }),
            //               ),
            //             );
            //           }
            //         }
            //         return SizedBox(
            //           width: MediaQuery.of(context).size.width * 1,
            //           height: MediaQuery.of(context).size.height * 0.5,
            //           child: GridView.count(
            //             crossAxisCount: 2,
            //             children: List.generate(100, (index) {
            //               return Center(
            //                 child: Text(
            //                   'Item $index',
            //                   style: Theme.of(context).textTheme.headlineSmall,
            //                 ),
            //               );
            //             }),
            //           ),
            //         );
            //       }),
            // ),

            TextButton(
              onPressed: () => Navigator.of(context, rootNavigator: true).pop(),
              child: const Text('Cancel'),
            ),

            TextButton(
              onPressed: () => saveExpenditure(
                  context,
                  amountTextEditingController.text,
                  categoryOfExpenditure,
                  nameOfExpenditureTextEditingController.text,
                  incomeExpense.getToogle(),
                  isFixedExpenditure),
              child: const Text('Save'),
            )
          ],
        )
      ],
    );
  }

  void saveExpenditure(
      BuildContext context,
      String amountOfExpenditure,
      String categoryOfExpenditure,
      String nameOfExpenditure,
      bool isIncome,
      bool isFixed) {
    Expenditure expenditure = Expenditure(
        nameOfExpenditure: nameOfExpenditure,
        amountOfExpenditure: amountOfExpenditure,
        categoryOfExpenditure: categoryOfExpenditure,
        typeOfExpenditure: isIncome == true ? "Income" : "Expense",
        isFixedExpenditure: isFixed);

    context.read<FinanceBloc>().add(SaveFinanceEvent(expenditure));

    //Closing the popup
    Navigator.of(context, rootNavigator: true).pop();
  }
}

Now when I uncomment out the bloc of code in the showAddExpenditureDialogBox method, I get the following:

Error: Could not find the correct Provider<FinanceBloc> above this BlocBuilder<FinanceBloc,
FinanceState> 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 BlocBuilder<FinanceBloc, FinanceState> is under your
MultiProvider/Provider<FinanceBloc>.

I don’t see how or why the error is occurring. From what I see, the bloc provider is being created at the entry point of the app, being passed into OnboardingPageWidget, and then being passed into the ExpenditureForm class via constructor. So surely I should be able to use the context form the constructor to use BlocBuilder? I think it might be stemming from the fact that there is not overrided build method in my ExpenditureForm class? The reason why I created an explicit class and not extending a stateful/stateless widget is because this alert dialog box is being used in another place in my app.

2

Answers


  1. You should provide the BLoc above the MaterialApp widget:

    that’s because BLoCs are provided above the home screen only, whenever you navigate to another screen the home screen is removed from the MaterialApp and another screen takes its place, hence providers (FinanceBloc, OnbardingBloc) are removed with home screen.

    So, make the roviders above the material app:

    runApp(
      MultiBlocProvider(
        providers: [
           BlocProvider<OnboardingBloc>(
              create: (BuildContext context) => OnboardingBloc()),
          BlocProvider<FinanceBloc>(
              create: (BuildContext context) => FinanceBloc(expenditureRepository)),
        ],
        child: MaterialApp(
          home: OnboardingPageWidget(),
      ),
     ),
    );
    
    Login or Signup to reply.
  2. The dialog is separated for the Material context. You can pass the instance like

    onPressed: () async {
      final provider = BlocProvider.of<FinanceBloc>(context);
      await showDialog<String>(
        context: context,
        builder: (BuildContext _) {
          return BlocProvider.value(
            value: provider,
            child: YourDialogWidget(),
          );
        },
      );
    },
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search