RedZ
RedZ

Reputation: 1013

Flutter - Cant find bloc in the parent tree when using showDialog

Im using Flutter and flutter_bloc for a small app, and i used MultiBlocProvider to use multiple BlocProviders that i need in the main home page, and under the home page, there is a MainWidget, which can access the given Bloc easily by: BlocProvider.of<OldBloc>(context) The MainWidget calls NewWidget as a dialog by: showDialog(context: context, builder: (context) => NewWidget()) The problem is, i cannot access OldBloc from NewWidget(), so i assumed that MainWidget isnt passing its context to NewWidget when using showDialog ?

HomeScreen.dart

import 'package:flutter/material.dart';

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: MultiBlocProvider(
      providers: [
        BlocProvider(
          create: (context) => OldBloc()..add(Initialize()),
        ),
        BlocProvider(
          create: (context) => OtherBloc()..add(Initialize()),
        ),
      ],
      child: Stack(
        children: [
          MainWidget(),
          MenuWidget(),
        ],
      ),
    ));
  }
}

MainWidget.dart

import 'package:flutter/material.dart';

class MainWidget extends StatelessWidget {
  const MainWidget({
    Key key,
  }) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return TextField(onTap: () => showDialog(context: context, builder: (context) => NewWidget()));
  }
}

NewWidget.dart

import 'package:flutter/material.dart';

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

  @override
  Widget build(context) {
    return Text(BlocProvider.of<OldBloc>(context).name); // <------- THIS GIVES AN ERROR THAT IT CANT FIND A BLOC OF THE TYPE OldBloc
  }
}

Upvotes: 12

Views: 5942

Answers (3)

Mustafa Fahimi
Mustafa Fahimi

Reputation: 550

You can simply use this (suggested by Flex Angelov):

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

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => FilesBloc(),
      child: Builder(
        builder: (builderContext) => TextButton(
          onPressed: () => _onTapSubmit(builderContext),
          child: Text('Submit'),
        ),
      ),
    );
  }

  void _onTapSubmit(BuildContext builderContext) {
    showDialog(
      context: builderContext,
      builder: (_) {
        return BlocProvider.value(
          value: builderContext.read<FilesBloc>(),
          child: const MyDialogWidget(),
        );
      },
    );
  }
}`

You can replace Builder with a BlocBuilder.

Upvotes: 14

Egor Polyakov
Egor Polyakov

Reputation: 89

I solved this problem.

I use cubit instead of bloc but it doesn't matter.

ShowDialog doesn't pass parent context. So you should create a new cubit and pass your parent cubit and state in arguments for a new cubit. I called main cubit FooCubit, new cubit FooCubitWrapper and DialogWidget is child widget that needs some BLOC logic in showDialog.


    var fooCubit = context.read<FooCubit>(); // your parrent cubit and state
    return BlocBuilder<FooCubit , FooState>(
        builder: (_, state) {
.
.///some logic///
.
          showDialog(
            context: context,
            builder: (_) => // here you have to create new cubit 
                BlocProvider( // because there is no access to parent cubit
                  create: (_) => FooCubitWrapper(cubit, state),
                  child: DialogWidget(),
                ),
          );

This is FooCubitWrapper. For example, we need the boo method from the parent cubit. So we need to create here boo method and inside we need to reference to parent boo method and emit parent state.

class FooCubitWrapper extends Cubit<FooState> {
  FooCubit fooCubit;

  FooCubitWrapper(this.fooCubit, FooState initialState) : super(initialState);

  boo() {
    fooCubit.boo();

    emit(fooCubit.state);
  }
}

And finally, in your DialogWidget, you do all like usual

    var cubit = context.read<TagsCubitWrapper>();
    return BlocBuilder<TagsCubitWrapper, TagsState>(
        builder: (context, state) {

// work with methods and fields as usual
    cubit.boo();
    if !(state.someField) {}

});

Upvotes: 0

Simon Sot
Simon Sot

Reputation: 3136

You have no access to BuildContext in your showDialog method, documentation:

The widget returned by the builder does not share a context with the location that showDialog is originally called from.

The context argument is used to look up the Navigator and Theme for the dialog. It is only used when the method is called.

Its also recommended to use a StatefulBuilder or a custom StatefulWidget, here is an example.

Upvotes: 4

Related Questions