尝试使用谷歌地图时查找已停用小部件的祖先是不安全的

Posted

技术标签:

【中文标题】尝试使用谷歌地图时查找已停用小部件的祖先是不安全的【英文标题】:Looking up a deactivated widget's ancestor is unsafe when try use google map 【发布时间】:2021-09-27 03:44:33 【问题描述】:

第一次工作,然后当我回去我得到这个错误,请帮助。我无法解决这个问题。我尝试在我的应用程序中使用地图。谁能帮我?状态管理呢?尝试将地图集成到应用中

我想知道为什么发生这种情况并且应用程序出现错误?最新版本的颤振使用。

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

这是map_screen.dart是

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

错误是

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
...

【问题讨论】:

【参考方案1】:

你不能在dispose() 内部调用Provider.of(context),因为当dispose() 被调用时,Flutter 不能保证上下文在树中的正确位置。

相反,您可以:

    initState() 中致电Provider.of(context) 将其存储在 State 类的字段中 使用dispose()中的字段

例如:

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

【讨论】:

以上是关于尝试使用谷歌地图时查找已停用小部件的祖先是不安全的的主要内容,如果未能解决你的问题,请参考以下文章

如果添加到动画列表,Flutter 查找已停用小部件的祖先是不安全的

使用提供程序和快餐栏查找已停用小部件的祖先是不安全的

提供者:查找已停用小部件的祖先是不安全的

Flutter:查找已停用小部件的祖先是不安全的

未处理的异常:查找已停用小部件的祖先是不安全的。如何解决这个问题?

在 InitState 未处理异常中显示对话框:查找已停用小部件的祖先是不安全的