1

======== Exception caught by gesture =============================================================== The following ProviderNotFoundException was thrown while handling a gesture: Error: Could not find the correct Provider above this Test 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 Test is under your MultiProvider/Provider. This usually happens when you are creating a provider and trying to read it immediately.

    For example, instead of:

    Widget build(BuildContext context) {
      return Provider<Example>(
        create: (_) => Example(),
        // Will throw a ProviderNotFoundError, because `context` is associated
        // to the widget that is the parent of `Provider<Example>`
        child: Text(context.watch<Example>()),
      ),
    }
    

    consider using builder like so:

    Widget build(BuildContext context) {
      return Provider<Example>(
        create: (_) => Example(),
        // we use `builder` to obtain a new `BuildContext` that has access to the provider
        builder: (context) {
          // No longer throws
          return Text(context.watch<Example>()),
        }
      ),
    }
    

If none of these solutions work, consider asking for help on StackOverflow: https://stackoverflow.com/questions/tagged/flutter

I am building an Widget "Test" to search users by their username. This is the widget Test with Bloc.

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

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => DonorsCubit(),
      child: BlocListener<DonorsCubit, DonorsState>(
        listener: (context, state) {
          print(state);
        },
        child: Scaffold(
          appBar: AppBar(),
          body: IconButton(
            onPressed: () {
              context.read<DonorsCubit>().searchDonors(searchKey: "masum");
            },
            icon: BlocBuilder<DonorsCubit, DonorsState>(
              builder: (context, state) {
                if (state is DonorsInitialState) return const Icon(Icons.add);
                if (state is DonorsLoadedState) return const Icon(Icons.done);
                if (state is DonorsLoadingState) return const Icon(Icons.circle);
                return const SizedBox();
              },
            ),
          ),
        ),
      ),
    );
  }
}

I used this cubit to manage states.

class DonorsCubit extends Cubit<DonorsState> {
  List<MyUser> users = <MyUser>[];
  final FirebaseDBRepo _firebaseDBRepo = FirebaseDBRepo();
  late StreamSubscription _streamSubscription;

  DonorsCubit() : super(DonorsInitialState()) {
    _streamSubscription =
        _firebaseDBRepo.usersStream().listen((List<MyUser> users) {
      this.users = users;
    });
  }

  void searchDonors({required String? searchKey}) {
    emit(DonorsLoadingState());
    List<MyUser> searchedUser = <MyUser>[];
    searchedUser.clear();
    if (searchKey == null) {
      emit(DonorsLoadedState(users: users));
    } else {
      for (MyUser user in users) {
        if (user.username!.toLowerCase().contains(searchKey.toLowerCase())) {
          searchedUser.add(user);
        }
      }
      emit(DonorsLoadedState(users: searchedUser));
    }
  }

  @override
  Future<void> close() {
    _streamSubscription.cancel();
    return super.close();
  }
}



abstract class DonorsState extends Equatable {
  const DonorsState();
}

class DonorsLoadingState extends DonorsState {
  @override
  List<Object> get props => [];
}

class DonorsInitialState extends DonorsState {
  @override
  List<Object> get props => [];
}

class DonorsLoadedState extends DonorsState {
  final List<MyUser> users;

  const DonorsLoadedState({required this.users});

  @override
  List<Object?> get props => [users];
}

2 Answers2

1

The problem you get is related to how the provider package works. In order to access the cubit, you should provide it above in the widget tree. Now, you provide and listen to the cubit in the same context. There are several ways how you could handle it.

  1. Use the Builder widget.
class Test extends StatelessWidget {
  const Test({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => DonorsCubit(),
      child: Builder(
        builder: (context) => BlocListener<DonorsCubit, DonorsState>(
          listener: (context, state) {
            print(state);
          },
          child: Scaffold(
            appBar: AppBar(),
            body: IconButton(
              onPressed: () {
                context.read<DonorsCubit>().searchDonors(searchKey: "masum");
              },
              icon: BlocBuilder<DonorsCubit, DonorsState>(
                builder: (context, state) {
                  if (state is DonorsInitialState) return const Icon(Icons.add);
                  if (state is DonorsLoadedState) return const Icon(Icons.done);
                  if (state is DonorsLoadingState)
                    return const Icon(Icons.circle);
                  return const SizedBox();
                },
              ),
            ),
          ),
        ),
      ),
    );
  }
}
  1. Split your widget into two and provide your cubit in the parent widget:
class TestWrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => DonorsCubit(),
      child: const Test(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return BlocListener<DonorsCubit, DonorsState>(
      listener: (context, state) {
        print(state);
      },
      child: Scaffold(
        appBar: AppBar(),
        body: IconButton(
          onPressed: () {
            context.read<DonorsCubit>().searchDonors(searchKey: "masum");
          },
          icon: BlocBuilder<DonorsCubit, DonorsState>(
            builder: (context, state) {
              if (state is DonorsInitialState) return const Icon(Icons.add);
              if (state is DonorsLoadedState) return const Icon(Icons.done);
              if (state is DonorsLoadingState) return const Icon(Icons.circle);
              return const SizedBox();
            },
          ),
        ),
      ),
    );
  }
}

I am a fan of option 2 since it is more clear that you are splitting your code and working in separate contexts.

BONUS

Instead of using BlocListener and BlocBuilder separately, you could use the BlocConsumer widget:

class TestWrapper extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider(
      create: (_) => DonorsCubit(),
      child: const Test(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: IconButton(
        onPressed: () {
          context.read<DonorsCubit>().searchDonors(searchKey: "masum");
        },
        icon: BlocConsumer<DonorsCubit, DonorsState>(
          listener: (context, state) {
            print(state);
          },
          builder: (context, state) {
            if (state is DonorsInitialState) return const Icon(Icons.add);
            if (state is DonorsLoadedState) return const Icon(Icons.done);
            if (state is DonorsLoadingState) return const Icon(Icons.circle);
            return const SizedBox();
          },
        ),
      ),
    );
  }
}
mkobuolys
  • 4,499
  • 1
  • 11
  • 27
0

I have the same problem, I use the MultiProvider to list my providers like this:

@override
Widget build(BuildContext context) {
  return MultiProvider(
    providers: [
      ChangeNotifierProvider(create: (_) => Example()),
    ],
    child: MaterialApp(
      title: 'Example',
      debugShowCheckedModeBanner: false,
      theme: ThemeData.dark().copyWith(
        textTheme: GoogleFonts.poppinsTextTheme(Theme.of(context).textTheme)
      ),
      // here I set my first screen...
      home: HomePage(),
    ),
  );
}
Tyler2P
  • 2,324
  • 26
  • 22
  • 31