Home > other >  The context you are using comes from the widget above the BlocProvider. I use Dialog
The context you are using comes from the widget above the BlocProvider. I use Dialog

Time:05-02

Faced a problem when adding Bloc. I use Counter which is in Dialog and do everything through Block but for some reason I got this error (see below). I did the same before and there was no error. I do not fully understand what the error is connected with and how to solve it correctly. I have a HomePage class in which I declare Bloc and in which the HomeBody class is nested, and in this class there is a button for opening Dialog (FilterDialog) and in this Dialog I have a Counter that I did through Bloc. I will be grateful for help.

home page

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider<CounterCubit>(
      create: (context) => CounterCubit(),
      child: Scaffold(
        extendBodyBehindAppBar: true,
        appBar: LogoAppBar(
            buttonIcon: SvgPicture.asset(constants.Assets.burgerMenu)),
        body: const HomeBody(),
      ),
    );
  }
}

home body

class HomeBody extends StatelessWidget {
  const HomeBody({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final Size size = MediaQuery.of(context).size;

    return Container(
      width: size.width,
      height: size.height,
      decoration: const BoxDecoration(
        image: DecorationImage(
          image: AssetImage('assets/images/background/main_background.png'),
          fit: BoxFit.cover,
        ),
      ),
      child: _child(context, size),
    );
  }

  Widget _child(context, Size size) => Padding(
        padding: const EdgeInsets.only(top: 121, right: 24),
        child: Align(
          alignment: Alignment.topRight,
          child: GestureDetector(
            onTap: () {
              showDialog(
                context: context,
                builder: (context) {
                  return const FilterDialog();
                },
              );
            },
            child: Container(
              height: 40,
              width: 50,
              decoration: const BoxDecoration(
                color: Colors.amber,
              ),
              alignment: Alignment.center,
              child: const Text('Dialog'),
            ),
          ),
        ),
      );
}

FilterDialog

Widget build(BuildContext context) {
    return SingleChildScrollView(
      child: Dialog(
        insetPadding: const EdgeInsets.only(top: 100, left: 24, right: 24),
        backgroundColor: Colors.transparent,
        shape: const RoundedRectangleBorder(
            borderRadius: BorderRadius.all(Radius.circular(24))),
        child: Container(
          width: MediaQuery.of(context).size.width,
          decoration: const BoxDecoration(
            color: constants.Colors.greyDark,
            borderRadius: BorderRadius.all(Radius.circular(24)),
          ),
          child: Padding(
            padding: const EdgeInsets.fromLTRB(21, 38, 21, 24),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                PriceCounter(title: 'From'),
              ]

price counter

    class PriceCounter extends StatelessWidget {
  final String title;

  const PriceCounter({Key? key, required this.title}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final CounterCubit cubit = BlocProvider.of<CounterCubit>(context);

    return Column(
      children: [
        BlocBuilder<CounterCubit, CounterState>(
          builder: (context, state) => InputField(
              price: state.countValue.toString(),
              textStyle: constants.Styles.normalBookTextStyleWhite),
        ),
        Row(
          children: [
            IconButton(
              onPressed: () => cubit.increment(),
              icon: SvgPicture.asset(constants.Assets.plus),
              constraints: const BoxConstraints(),
              padding: EdgeInsets.zero,
            ),
            Text('Test', style: constants.Styles.smallLtStdTextStyleWhite),
            IconButton(
              onPressed: () => cubit.decrement(),
              icon: SvgPicture.asset(constants.Assets.minus),
              constraints: const BoxConstraints(),
              padding: EdgeInsets.zero,
            ),
          ],
        )
      ],
    );
  }
}

counter state

class CounterState {
  final double countValue;
  const CounterState({required this.countValue});
}

counter cubit

class CounterCubit extends Cubit<CounterState> {
  CounterCubit() : super(const CounterState(countValue: 0.13));

  void increment() => emit(CounterState(countValue: state.countValue   0.1));

  void decrement() => emit(CounterState(countValue: state.countValue - 0.1));
}

The following assertion was thrown building PriceCounter(dirty): BlocProvider.of() called with a context that does not contain a CounterCubit.

    No ancestor could be found starting from the context that was passed to BlocProvider.of<CounterCubit>().

    This can happen if the context you used comes from a widget above the BlocProvider.

    The context used was: PriceCounter(dirty)

The relevant error-causing widget was PriceCounter lib\…\widgets\filter_dialog.dart:233 When the exception was thrown, this was the stack

enter image description here

CodePudding user response:

Unfortunately, your FilterDialog cannot find the CounterCubit provider, since showDialog is a bit tricky about it, so you have to re-supply your CounterCubit to FilterDialog in this way:

  Widget _child(context, Size size) => Padding(
        padding: const EdgeInsets.only(top: 121, right: 24),
        child: Align(
          alignment: Alignment.topRight,
          child: GestureDetector(
            onTap: () {
              showDialog(
                context: context,
                builder: (context) {
                  return BlocProvider.value( // in this way
                    value: CounterCubit(),
                    child: FilterDialog(),
                  );
                },
              );
            },
            child: Container(
              height: 40,
              width: 50,
              decoration: const BoxDecoration(
                color: Colors.amber,
              ),
              alignment: Alignment.center,
              child: const Text('Dialog'),
            ),
          ),
        ),
      );
}

With BlocProvider.value you do not create a Bloc, but only assign to its child the bloc that you have already created in HomePage.

That should work, but if for some reason you are going to use this CounterCubit in another page and you don't want to use BlocProvider.value again, I strongly suggest that you make it global, in other words that you provide the CounterCubit in all your application in this way :

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (context) => CounterCubit(), // here
      child: MaterialApp(
        title: 'Material App',
        debugShowCheckedModeBanner: false,
        home: const HomePage(), 
      ),
    );
  }

}

With this, now every application will be able to get the context of your CounterCubit.

With both codes, one with the BlocProvider.value or without it and using it global, it works.

  • Related