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:
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.MultiBlocLister
a child of a MultiBlocProvider
and set TrackingBloc
there, but still get the message.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.