Abdullah Al Masum
Abdullah Al Masum

Reputation: 21

Could not find the correct Provider above this Test widget

======== 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:

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];
}

Upvotes: 2

Views: 6756

Answers (2)

Abi&#250;d Mota
Abi&#250;d Mota

Reputation: 1

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(),
    ),
  );
}

Upvotes: -1

mkobuolys
mkobuolys

Reputation: 5333

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();
          },
        ),
      ),
    );
  }
}

Upvotes: 1

Related Questions