Looking up a deactivated widget's ancestor is unsafe when try use google map
392
You cannot call Provider.of(context)
inside dispose()
, because Flutter cannot guarantee that the context is at the right point in the tree when dispose()
is being called.
Instead, you could:
- call
Provider.of(context)
ininitState()
- store it in a field on your
State
class - use the field in
dispose()
For example:
class _MapScreenState extends State<MapScreen> {
late final ApplicationBloc applicationBloc;
@override
void initState() {
super.initState();
// ... other logic
applicationBloc = Provider.of<ApplicationBloc>(context, listen: false);
}
// build, etc
@override
void dispose() {
applicationBloc.dispose();
// other logic...
super.dispose();
}
}
Author by
Dilanka yapa
Updated on December 31, 2022Comments
-
Dilanka yapa over 1 year
The first time it's working then when I going back I get this error please help. I can't fix this issue. I try to use the map in my app. Can anyone help me? What about state management?Trying to integrate a map to a app
I want to know why is this happening and the app getting errors? the latest version of flutter using.
import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:geolocator/geolocator.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:medicine/models/geometry.dart'; import 'package:medicine/models/location.dart'; import 'package:medicine/models/place.dart'; import 'package:medicine/models/place_search.dart'; import 'package:medicine/services/geolocator_service.dart'; import 'package:medicine/services/marker_service.dart'; import 'package:medicine/services/places_service.dart'; class ApplicationBloc with ChangeNotifier { final geoLocatorService = GeolocatorService(); final placesService = PlacesService(); final markerService = MarkerService(); //Variables Position? currentLocation; List<PlaceSearch>? searchResults; StreamController<Place?> selectedLocation = StreamController<Place?>(); StreamController<LatLngBounds> bounds = StreamController<LatLngBounds>(); Place? selectedLocationStatic; String? placeType; List<Place>? placeResults; List<Marker> markers = []; ApplicationBloc() { setCurrentLocation(); } setCurrentLocation() async { currentLocation = await geoLocatorService.getCurrentLocation(); selectedLocationStatic = Place( name: null, geometry: Geometry( location: Location( lat: currentLocation!.latitude, lng: currentLocation!.longitude), ), ); notifyListeners(); } searchPlaces(String searchTerm) async { searchResults = await placesService.getAutocomplete(searchTerm); notifyListeners(); } setSelectedLocation(String placeId) async { var sLocation = await placesService.getPlace(placeId); selectedLocation.add(sLocation); selectedLocationStatic = sLocation; searchResults = null; notifyListeners(); } clearSelectedLocation() { selectedLocation.add(null); selectedLocationStatic = null; searchResults = null; placeType = null; notifyListeners(); } togglePlaceType(String value, bool selected) async { if (selected) { placeType = value; } else { placeType = null; } if (placeType != null) { var places = await placesService.getPlaces( selectedLocationStatic!.geometry.location.lat, selectedLocationStatic!.geometry.location.lng, placeType!); markers = []; if (places.length > 0) { var newMarker = markerService.createMarkerFromPlace(places[0], false); markers.add(newMarker); } var locationMarker = markerService.createMarkerFromPlace(selectedLocationStatic!, true); markers.add(locationMarker); var _bounds = markerService.bounds(Set<Marker>.of(markers)); bounds.add(_bounds!); notifyListeners(); } } @override void dispose() { selectedLocation.close(); bounds.close(); super.dispose(); } }
This is the map_screen.dart is
import 'dart:async'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:medicine/blocs/application_bloc.dart'; import 'package:medicine/models/place.dart'; import 'package:provider/provider.dart'; class MapScreen extends StatefulWidget { MapScreen({ Key? key, }) : super(key: key); @override _MapScreenState createState() => _MapScreenState(); } class _MapScreenState extends State<MapScreen> { Completer<GoogleMapController> _mapController = Completer(); late StreamSubscription locationSubscription; late StreamSubscription boundsSubscription; final _locationController = TextEditingController(); @override void initState() { final applicationBloc = Provider.of<ApplicationBloc>(context, listen: false); //Listen for selected Location locationSubscription = applicationBloc.selectedLocation.stream.listen((place) { if (place != null) { _locationController.text = place.name ?? ""; _goToPlace(place); } else _locationController.text = ""; }); applicationBloc.bounds.stream.listen((bounds) async { final GoogleMapController controller = await _mapController.future; controller.animateCamera(CameraUpdate.newLatLngBounds(bounds, 50)); }); super.initState(); } @override void dispose() { final applicationBloc = Provider.of<ApplicationBloc>(context, listen: false); applicationBloc.dispose(); _locationController.dispose(); locationSubscription.cancel(); boundsSubscription.cancel(); super.dispose(); } @override Widget build(BuildContext context) { final applicationBloc = Provider.of<ApplicationBloc>(context); return Scaffold( appBar: AppBar( title: Text('Map'), ), body: (applicationBloc.currentLocation == null) ? Center( child: CircularProgressIndicator(), ) : ListView( children: [ Padding( padding: const EdgeInsets.all(8.0), child: TextField( controller: _locationController, textCapitalization: TextCapitalization.words, decoration: InputDecoration( hintText: 'Search by City', suffixIcon: Icon(Icons.search), ), onChanged: (value) => applicationBloc.searchPlaces(value), onTap: () => applicationBloc.clearSelectedLocation(), ), ), Stack( children: [ Container( height: 300.0, child: GoogleMap( mapType: MapType.normal, myLocationEnabled: true, initialCameraPosition: CameraPosition( target: LatLng( applicationBloc.currentLocation!.latitude, applicationBloc.currentLocation!.longitude), zoom: 14, ), onMapCreated: (GoogleMapController controller) { _mapController.complete(controller); }, markers: Set<Marker>.of(applicationBloc.markers), ), ), if (applicationBloc.searchResults != null && applicationBloc.searchResults!.length != 0) Container( height: 300.0, width: double.infinity, decoration: BoxDecoration( color: Colors.black.withOpacity(.6), backgroundBlendMode: BlendMode.darken)), if (applicationBloc.searchResults != null) Container( height: 300.0, child: ListView.builder( itemCount: applicationBloc.searchResults!.length, itemBuilder: (context, index) { return ListTile( title: Text( applicationBloc .searchResults![index].description, style: TextStyle(color: Colors.white), ), onTap: () { applicationBloc.setSelectedLocation( applicationBloc .searchResults![index].placeId); }, ); }), ), ], ), SizedBox( height: 20.0, ), Padding( padding: const EdgeInsets.all(8.0), child: Text('Find Nearest', style: TextStyle( fontSize: 25.0, fontWeight: FontWeight.bold)), ), Padding( padding: const EdgeInsets.all(8.0), child: Wrap( spacing: 8.0, children: [ FilterChip( label: Text('Pharmacy'), onSelected: (val) => applicationBloc .togglePlaceType('pharmacy', val), selected: applicationBloc.placeType == 'pharmacy', selectedColor: Colors.blue), ], ), ) ], )); } Future<void> _goToPlace(Place place) async { final GoogleMapController controller = await _mapController.future; controller.animateCamera( CameraUpdate.newCameraPosition( CameraPosition( target: LatLng( place.geometry.location.lat, place.geometry.location.lng), zoom: 14.0), ), ); } }
Error is
The following assertion was thrown while finalizing the widget tree: Looking up a deactivated widget's ancestor is unsafe. At this point, the state of the widget's element tree is no longer stable. To safely refer to a widget's ancestor in its dispose() method, save a reference to the ancestor by calling dependOnInheritedWidgetOfExactType() in the widget's didChangeDependencies() method. When the exception was thrown, this was the stack #0 Element._debugCheckStateIsActiveForAncestorLookup.<anonymous closure> package:flutter/…/widgets/framework.dart:3944 #1 Element._debugCheckStateIsActiveForAncestorLookup package:flutter/…/widgets/framework.dart:3958 #2 Element.getElementForInheritedWidgetOfExactType package:flutter/…/widgets/framework.dart:3984 #3 Provider._inheritedElementOf package:provider/src/provider.dart:327 #4 Provider.of package:provider/src/provider.dart:284 ...