Home > Blockchain >  Which Buildcontext to use in context.watch<SomeBloc>()?
Which Buildcontext to use in context.watch<SomeBloc>()?

Time:09-04

I am doing an application with one cubit and one bloc(at least for now), and on one page they have to interact with each other. the official documentation advices not to use one bloc inside of another, and do not intersect them in any way but presentational. So, in my app I am trying to see if a user does not have connection to internet(from InternetCubit) or if there are any errors from LoginBloc, i want to show it.

This is my main.dart:

class MyApp extends StatelessWidget {
  final Connectivity connectivity;

  const MyApp({super.key, required this.connectivity});

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider<InternetCubit>(
            create: (context) => InternetCubit(connectivity: connectivity),
            lazy: false)
      ],
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'Test',
        theme: ThemeData(
            fontFamily: 'Test',
            inputDecorationTheme: InputDecorationTheme(
                border: OutlineInputBorder(
                  borderSide: const BorderSide(color: kBlack, width: 1),
                  borderRadius: BorderRadius.circular(0),
                ),
                focusedBorder: OutlineInputBorder(
                  borderSide: const BorderSide(color: kGreen, width: 2),
                  borderRadius: BorderRadius.circular(0),
                ),
                labelStyle: const TextStyle(color: kBlack)),
            textTheme: const TextTheme(
                displayLarge: TextStyle(
                    fontSize: 50, fontWeight: FontWeight.w500, color: kBlack),
                labelSmall: TextStyle(
                    fontSize: 15, fontWeight: FontWeight.w400, color: kRed))),
        home: const LoginScreen(),
        // onGenerateRoute: GeneratedRouter.generateRoute,
        // initialRoute: meetPageRouteName,
      ),
    );
  }
}

This is my loginscreen.dart:

@override
  Widget build(BuildContext context) {

    final size = MediaQuery.of(context).size;

    return Scaffold(
      resizeToAvoidBottomInset: false,
      appBar: AppBar(
        elevation: 0,
        backgroundColor: Colors.transparent,
        leading: IconButton(
          icon: const Icon(Icons.arrow_back_ios, color: Colors.black),
          onPressed: () => {Navigator.pop(context)},
          splashRadius: 25,
        ),
      ),
      body: BlocProvider(
        create: (_) => LoginBloc(),
        child: Form(
          key: _formKey,
          child: Padding(
            padding: const EdgeInsets.symmetric(horizontal: 20),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                Builder(
                  builder: (buildCtx){
                    final internetState = buildCtx.watch<InternetCubit>().state;
                    final loginState = buildCtx.watch<LoginBloc>().state;
                    if(internetState is InternetDisconnected){
                      error = "No internet Connection";
                    }else if(loginState is LoginError){
                      error = loginState.error;
                    }else{
                      error = null;
                    }
                    setState(() {});
                    return errorText(error);
                  },
                )
              ],
            ),
          ),
        ),
      ),
    );
  }

when i run the code, I get this error

Error: Could not find the correct Provider<InternetCubit> above this Builder Widget

This happens because you used a BuildContext that does not include the provider
of your choice.

I tried changing from BuildCtx to context, but it does not seem to help. What is my issue here?

CodePudding user response:

I have tried to recreate your problem using the below attached minimal reproducible code. It is runnable.

Unfortunately (or luckily) it works without your issue. Have a look through my code and see if I've missed something, or if you missed something to make it work.

It is not advicable to provide a bloc all over your app all unless you use them all over your app. I have answered this previously (Providing BLoCs - Global vs Specific)

Also, consider using BlocBuilder instead of the regular Builder widget to get hold of the state without manually writing the .watch() statement.

import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [BlocProvider(create: (_) => InternetConnectivity(), lazy: false)],
      child: MaterialApp(
        title: 'Flutter Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
        ),
        home: const Demo(),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: BlocProvider(
        create: (context) => Login(),
        lazy: false,
        child: Center(
          child: Builder(
            builder: (context) {
              final internetCubit = context.watch<InternetConnectivity>();
              final loginCubit = context.watch<Login>();
              return Text('User ${loginCubit.state.username} have internet connection: ${internetCubit.state.temp}');
            },
          ),
        ),
      ),
    );
  }
}

class InternetConnectivity extends Cubit<InternetConnectivityState> {
  InternetConnectivity() : super(InternetConnectivityInitial());
}

abstract class InternetConnectivityState extends Equatable {
  final bool temp = true;
  const InternetConnectivityState();

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

class InternetConnectivityInitial extends InternetConnectivityState {}

class Login extends Cubit<LoginState> {
  Login() : super(LoginInitial());
}

abstract class LoginState extends Equatable {
  final username = 'EpicUsername';
  const LoginState();

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

class LoginInitial extends LoginState {}

CodePudding user response:

Well, it seems you are indeed trying to use one BlocProvider inside another...

What happens if you do this, in your main.dart:

class MyApp extends StatelessWidget {
  final Connectivity connectivity;

  const MyApp({super.key, required this.connectivity});

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
        providers: [
          BlocProvider<InternetCubit>(
              create: (context) => InternetCubit(connectivity: connectivity),
              lazy: false),
          // Added the below:
          BlocProvider(
            create: (_) => LoginBloc(),
          ),
        ],
        child: MaterialApp(/*The rest of your content*/),
        }
    );
  }
}

?

Then in your LoginScreen, you remove the widget BlocProvider?


Edit:

If, just as an experiment, you try and exchange your Scaffold() with the below and run it, do you still get the same error, "This context does not contain your InternetCubit BlocProvider"?

    return Scaffold(
      body: Form(
        key: _formKey,
        child: Padding(
          padding: const EdgeInsets.symmetric(horizontal: 20),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            children: [
              Builder(
                builder: (buildCtx){
                  final internetState = buildCtx.watch<InternetCubit>().state;
                  // final loginState = buildCtx.watch<LoginBloc>().state;
                  // if(internetState is InternetDisconnected){
                  //   error = "No internet Connection";
                  // }else if(loginState is LoginError){
                  //   error = loginState.error;
                  // }else{
                  //   error = null;
                  // }
                  setState(() {});
                  return errorText(error);
                },
              )
            ],
          ),
        ),
      ),
    );
  • Related