简体   繁体   中英

BlocProvider.of() called with a context that does not contain a Bloc of type TrackingBloc

I'm trying to provide a TrackingBloc to MapScreen but when sending an event from onPressed I get the error BlocProvider.of() called with a context that does not contain a Bloc of type TrackingBloc. MapScreen also uses a MapBloc provided from main() , but for TrackingBloc I want to make it local, not to clutter MultiBlocProvider in main() . I tried:

  1. To use the bloc: parameter in the BlocListener<TrackingBloc, TrackingState> , as I've been told that it just provides the bloc as a BlocProvider would( https://github.com/felangel/bloc/issues/930#issuecomment-593790702 ) but it didn't work.
  2. Then I tried making MultiBlocLister a child of a MultiBlocProvider and set TrackingBloc there, but still get the message.
  3. Set TrackingBloc in the MultiBlocProvider in `main() and worked as expected.

Why 1 and 2 don't provide TrackingBloc to the tree? Many thanks for your help.

MapScreen:

class MapScreen extends StatefulWidget {
  final String name;
  final MapRepository _mapRepository;

  MapScreen(
      {Key key, @required this.name, @required MapRepository mapRepository})
      : assert(mapRepository != null),
        _mapRepository = mapRepository,
        super(key: key);

  @override
  _MapScreenState createState() => _MapScreenState();
}

class _MapScreenState extends State<MapScreen> {
  List<Marker> alerts;
  LatLng userLocation;

  MapController _mapController = MapController();

  @override
  Widget build(BuildContext context) {
    return MultiBlocProvider(
      providers: [
        BlocProvider<TrackingBloc>(create: (context) {
          return TrackingBloc();
        }),
      ],
      child: MultiBlocListener(
        listeners: [
          BlocListener<MapBloc, MapState>(
              listener: (BuildContext context, MapState state) {
            if (state is LocationStream) {
              setState(() {
                userLocation = (state).location;
//              print(
//                  ' @@@@ MapBloc actual user location from stream  is : $userLocation');
              });
            }
            if (state is MapCenter) {
              userLocation = (state).location;
//            print(' @@@@ MapBloc initial center location is : $userLocation');
              _mapController.move(userLocation, 16);
            }
          }),
          BlocListener<TrackingBloc, TrackingState>(
//              bloc: TrackingBloc(),
              listener: (BuildContext context, TrackingState state) {
            if (state is TrackedRoute) {
              List<Position> route = (state).trackedRoute;
              print(route);
            }
          }),
        ],
        child: Scaffold(

main():

  runApp(
    MultiBlocProvider(
      providers: [
        BlocProvider<AuthenticationBloc>(
          create: (context) {
            return AuthenticationBloc(
              userRepository: UserRepository(),
            )..add(AppStarted());
          },
        ),
        BlocProvider<MapBloc>(create: (context) {
          return MapBloc(
            mapRepository: mapRepository,
          )
            ..add(GetLocationStream())
            ..add(GetLocation());
        }),
        BlocProvider<TrackingBloc>(create: (context) {
          return TrackingBloc();
        }),
//        BlocProvider<AlertBloc>(create: (context) {
//          return AlertBloc(
//            alertRepository: alertRepository,
//          )..add(LoadAlerts());
//        }),
      ],
      child:

Right of the bat, I can see two things are wrong with your code.

First: You provide multiple TrackingBloc, in main and MapScreen.

Second: You are accessing TrackingBloc via BlocListener within the same context where you provide it (the second BlocProvider(create: (context) {return TrackingBloc();}) ). My guess is this is what causing the error.

BlocProvider.of() called with a context that does not contain a Bloc of type TrackingBloc

I think by simply removing BlocProvider in the MapScreen will do the job.

I was providing TrackingBloc from the wrong place in the widget tree. I can provide the bloc globally which I don't need, so to provide it locally as I want, I have to provide it from Blocbuilder in main() which is returning MapScreen .

Changing main() from:

    return MaterialApp(
      home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
        builder: (context, state) {
          if (state is Unauthenticated) {
            return LoginScreen(userRepository: _userRepository);
          }
          if (state is Authenticated) {
//            BlocProvider.of<MapBloc>(context).add(GetLocationStream());
//            BlocProvider.of<AlertBloc>(context).add(LoadAlerts());
            return MapScreen(
              mapRepository: _mapRepository,
              name: state.displayName,
//              alertRepository: FirebaseAlertRepository(),
            );
          }
          if (state is Unauthenticated) {
            return LoginScreen(userRepository: _userRepository);
          }
          return SplashScreen();
        },
      ),
    );

to:

return MaterialApp(
      home: BlocBuilder<AuthenticationBloc, AuthenticationState>(
        builder: (context, state) {
          if (state is Unauthenticated) {
            return LoginScreen(userRepository: _userRepository);
          }
          if (state is Authenticated) {
//            BlocProvider.of<MapBloc>(context).add(GetLocationStream());
//            BlocProvider.of<AlertBloc>(context).add(LoadAlerts());
            return MultiBlocProvider(
              providers: [
                BlocProvider<TrackingBloc>(create: (context) {
                  return TrackingBloc();
                }),
              ],
              child: MapScreen(
                mapRepository: _mapRepository,
                name: state.displayName,
//              alertRepository: FirebaseAlertRepository(),
              ),
            );
            return MapScreen(
              mapRepository: _mapRepository,
              name: state.displayName,
//              alertRepository: FirebaseAlertRepository(),
            );
          }
          if (state is Unauthenticated) {
            return LoginScreen(userRepository: _userRepository);
          }
          return SplashScreen();
        },
      ),
    );

makes it work as I intended. Then in MapScreen I just use different BlocListener to listen to blocs being global as MapBloc or local as TrackingBloc :

class _MapScreenState extends State<MapScreen> {
  List<Marker> alerts;
  LatLng userLocation;

  MapController _mapController = MapController();

  @override
  Widget build(BuildContext context) {
    return MultiBlocListener(
      listeners: [
        BlocListener<MapBloc, MapState>(
            listener: (BuildContext context, MapState state) {
          if (state is LocationStream) {
            setState(() {
              userLocation = (state).location;
//              print(
//                  ' @@@@ MapBloc actual user location from stream  is : $userLocation');
            });
          }
          if (state is MapCenter) {
            userLocation = (state).location;
//            print(' @@@@ MapBloc initial center location is : $userLocation');
            _mapController.move(userLocation, 16);
          }
        }),
        BlocListener<TrackingBloc, TrackingState>(
//              bloc: TrackingBloc(),
            listener: (BuildContext context, TrackingState state) {
//      userLocation = (state as LocationStream).location;
          if (state is TrackedRoute) {
            List<Position> route = (state).trackedRoute;
            print(route);
//            initialLocation = (state).location.then((value) {
//              print('@@@@@@ value is : $value');
////              _mapController.move(value, 16.0);
//              return value;
//            }
//            );
          }
        }),
      ],
      child: Scaffold(

Hope this will help others just starting out with flutter_bloc that might not find documentation usage explanation of its widgets clearly enough. Still have to fully understand BlocProvider 's and BlocListener 's bloc: property dough.. Cheers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM