Flutter: dispose()错误后调用setState()



这实际上是一个带有谷歌位置搜索功能的工作谷歌地图。问题是在处理这个有状态的小部件时,我得到这个错误:

E/flutter (6017): [ERROR:flutter/lib/ui/ui_dart_state.cc(177)]未处理的异常:setState()在dispose()之后调用:_MapScreenState#79287(生命周期状态:失效,未安装,tickers:跟踪0个tickers)E/flutter(6017):如果您对不再出现在小部件树中的小部件(例如,其父小部件不再在其构建中包含该小部件)的状态对象调用setState(),则会发生此错误。当代码从计时器或动画回调调用setState()时,可能会发生此错误。E/flutter(6017):首选的解决方案是取消定时器或停止监听dispose()回调中的动画。另一个解决方案是检查"安装"。属性,然后调用setState(),以确保该对象仍在树中。E/flutter(6017):如果正在调用setState(),因为另一个对象在从树中删除后保留对该状态对象的引用,则此错误可能表明存在内存泄漏。为避免内存泄漏,请考虑在dispose()期间中断对该对象的引用。

谁能帮我找出我错过了什么?顺便说一句,这是我的代码片段:

import 'package:CaterChive/model/configKey.dart';
import 'package:CaterChive/services/hiveController.dart';
import 'package:CaterChive/style/textStyles.dart';
import 'package:CaterChive/view/homeScreen.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_svg/svg.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:geocoder/geocoder.dart';
import 'package:geocoder/model.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:hive/hive.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:search_map_place_v2/search_map_place_v2.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
class MapScreen extends StatefulWidget {
@override
_MapScreenState createState() => _MapScreenState();
}
class _MapScreenState extends State<MapScreen> with TickerProviderStateMixin {
GoogleMapController _mapController;
bool isMapCreated = false;
String address = Hive.box('userLocation').get(1).userAddress;
double mapLatitude = Hive.box('userLocation').get(1).addressLatitude;
double mapLongitude = Hive.box('userLocation').get(1).addressLongitude;
String selectedAddress;
bool _hasClearButton = true;
LatLng mapLocation = LatLng(Hive.box('userLocation').get(1).addressLatitude,
Hive.box('userLocation').get(1).addressLongitude);
int mapRadius = 30000;
bool strictBounds = false;
PlaceType mapPlaceType = PlaceType.address;
String language = 'en';
final _textEditingController = TextEditingController();
AnimationController _animationController;
// SearchContainer height.
// Animation _containerHeight;
// Place options opacity.
Animation _listOpacity;
List<dynamic> _placePredictions = [];
bool _isEditing = false;
Geocoding geocode;
String _tempInput = '';
String _currentInput = '';
final _fn = FocusNode();
CrossFadeState _crossFadeState;
bool locBusy = false;
LatLng newLocation;
String newLocationText;
mapStyle() {
getJsonFile('assets/json/mapStyle.json').then(setMapStyle);
}
Future<String> getJsonFile(String path) async {
return await rootBundle.loadString(path);
}
void setMapStyle(String mapStyle) {
_mapController.setMapStyle(mapStyle);
}
@override
void initState() {
super.initState();
fetchData();
}
void fetchData() {
print(mapLatitude);
print(mapLongitude);
if (isMapCreated) {
mapStyle();
}
geocode = Geocoding(apiKey: mapKey, language: 'en');
_animationController = AnimationController(
vsync: this, duration: const Duration(milliseconds: 500));
// _containerHeight = Tween<double>(begin: 0, end: 364).animate(
//   CurvedAnimation(
//     curve: const Interval(0.0, 0.5, curve: Curves.easeInOut),
//     parent: _animationController,
//   ),
// );
_listOpacity = Tween<double>(
begin: 0,
end: 1,
).animate(
CurvedAnimation(
curve: const Interval(0.5, 1.0, curve: Curves.easeInOut),
parent: _animationController,
),
);
_textEditingController.addListener(_autocompletePlace);
customListener();
if (_hasClearButton) {
_fn.addListener(() async {
if (_fn.hasFocus) {
setState(() => _crossFadeState = CrossFadeState.showSecond);
} else {
setState(() => _crossFadeState = CrossFadeState.showFirst);
}
});
_crossFadeState = CrossFadeState.showFirst;
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
resizeToAvoidBottomPadding: false,
appBar: AppBar(
centerTitle: true,
backgroundColor: Color(0xff009245),
title: Row(
children: [
GestureDetector(
onTap: () {
Get.off(HomeScreen());
},
child: Icon(Icons.arrow_back)),
Expanded(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15.0),
child: Container(
height: 40,
decoration: BoxDecoration(
color: Colors.white,
borderRadius:
const BorderRadius.all(Radius.circular(6.0)),
boxShadow: [
const BoxShadow(
color: Colors.black12,
blurRadius: 20,
spreadRadius: 10)
]),
child: Row(
children: [
Expanded(
child: TextField(
style: TextStyle(fontWeight: FontWeight.bold),
onTap: _textEditingController.text ==
'Selected Location'
? () {
_textEditingController.clear();
}
: () {},
controller: _textEditingController,
onSubmitted: (_) => _selectPlace(),
onEditingComplete: _selectPlace,
autofocus: false,
focusNode: _fn,
decoration: InputDecoration(
hintText: Hive.box('userCredentials')
.get(1)
.acctType ==
1
? 'Set Delivery Address'
: 'Set Location',
border: InputBorder.none,
contentPadding:
const EdgeInsets.fromLTRB(10, 0, 0, 5),
hintStyle: TextStyle(
color: Colors.grey[500],
fontWeight: FontWeight.normal)),
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Container(
child: _hasClearButton
? GestureDetector(
onTap: () {
if (_crossFadeState ==
CrossFadeState.showSecond) {
_textEditingController.clear();
}
},
// child: Icon(_inputIcon, color: this.widget.iconColor),
child: AnimatedCrossFade(
crossFadeState: _crossFadeState,
duration:
const Duration(milliseconds: 300),
firstChild: Icon(Icons.search,
color: Color(0xff009245)),
secondChild: Icon(Icons.clear,
color: Color(0xff009245)),
),
)
: Icon(Icons.search, color: Color(0xff009245)),
),
),
],
)),
),
),
GestureDetector(
onTap: () {
checkLocationPermission();
print('tapped gps');
},
child: Icon(Icons.gps_fixed)),
],
),
),
body: Stack(
children: [
GestureDetector(
onTap: () {
print('hello');
},
child: Container(
height: double.infinity,
width: double.infinity,
child: GoogleMap(
onCameraMoveStarted: _onCameraMoveStarted,
onCameraMove: _onCameraMove,
onCameraIdle: _onCameraIdle,
onTap: _mapTap,
onLongPress: _mapLongPress,
zoomControlsEnabled: false,
onMapCreated: (GoogleMapController googleMapController) {
_mapController = googleMapController;
isMapCreated = true;
mapStyle();
setState(() {});
},
initialCameraPosition: CameraPosition(
zoom: 18.0, target: LatLng(mapLatitude, mapLongitude)),
),
),
),
Container(
alignment: Alignment(0, 0),
child: SvgPicture.asset(
"assets/images/ccGPSshadow.svg",
width: locBusy ? 15 : 23,
fit: BoxFit.cover,
alignment: Alignment.center,
),
),
Container(
alignment: Alignment(0, locBusy ? -0.1 : -0.06),
child: SvgPicture.asset(
"assets/images/ccGPS.svg",
fit: BoxFit.cover,
width: 28,
alignment: Alignment.center,
),
),
AnimatedBuilder(
animation: _animationController,
builder: (context, _) {
return Container(
height: 65.0 * _placePredictions.length,
alignment: Alignment(0, -1),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: const BorderRadius.only(
bottomLeft: Radius.circular(6.0),
bottomRight: Radius.circular(6.0)),
boxShadow: [
const BoxShadow(
color: Colors.black12,
blurRadius: 20,
spreadRadius: 10)
]),
child: Column(
children: <Widget>[
// Padding(
//   padding: const EdgeInsets.only(
//       left: 12.0, right: 12.0, top: 4),
//   child: child,
// ),
// if (_placePredictions.isEmpty)
Opacity(
opacity: _listOpacity.value,
child: Column(
children: <Widget>[
for (var prediction in _placePredictions)
_placeOption(Place.fromJSON(prediction, geocode)),
],
),
),
],
),
);
}),
Container(
alignment: Alignment(0, 0.9),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: MaterialButton(
minWidth: double.infinity,
onPressed: locBusy
? () {}
: () async {
if (mapLocation.isNullOrBlank) {
print('empty');
Get.off(HomeScreen());
} else {
// convert coordinates to valid address string
final coordinates = Coordinates(
mapLocation.latitude, mapLocation.longitude);
var addresses = await Geocoder.local
.findAddressesFromCoordinates(coordinates);
address = addresses.first.addressLine;
print(address);
await HiveController().putLocationOnHive(address,
mapLocation.latitude, mapLocation.longitude);
Get.off(HomeScreen());
}
},
child: Padding(
padding: const EdgeInsets.all(15.0),
child: Text(
locBusy ? 'Loading...' : 'Find Nearby Services',
style: lblBtn,
)),
color: locBusy ? Colors.grey : Color(0xff009245),
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
),
),
),
// Container(
//   alignment: Alignment(0, -0.9),
//   child: Padding(
//     padding: const EdgeInsets.only(top: 15),
//     child: Column(
//       children: [
//         SearchMapPlaceWidget(
//           apiKey: mapKey,
//           hasClearButton: true,
//           location: LatLng(mapLatitude, mapLongitude),
//           placeType: PlaceType.address,
//           radius: 30000,
//           placeholder: 'Set delivery address',
//           onSelected: (Place place) async {
// Geolocation geolocation = await place.geolocation;
// _mapController.animateCamera(
//     CameraUpdate.newLatLng(geolocation.coordinates));
// _mapController.animateCamera(
//     CameraUpdate.newLatLngBounds(geolocation.bounds, 0));
// print(place.description);
// var newLoc = await Geocoder.local
//     .findAddressesFromQuery(place.description);
// print(geolocation.coordinates);
// mapLatitude = newLoc.first.coordinates.latitude;
// mapLongitude = newLoc.first.coordinates.longitude;
// print(mapLatitude);
// print(mapLongitude);
//           },
//         ),
//         FlatButton(
//           child: Text(
//             "Use Location",
//             style: TextStyle(color: Colors.white),
//           ),
//           onPressed: () {
//             print('Hi');
//           },
//         ),
//       ],
//     ),
//   ),
// ),
],
),
);
}
/*
WIDGETS
*/
Widget _placeOption(Place prediction) {
final place = prediction.description;
return MaterialButton(
padding: const EdgeInsets.symmetric(horizontal: 5, vertical: 3),
onPressed: () => _selectPlace(prediction: prediction),
child: ListTile(
title: Text(
place.length < 45
? '$place'
: '${place.replaceRange(45, place.length, "")} ...',
style: TextStyle(
fontSize: MediaQuery.of(context).size.width * 0.04,
// color: widget.darkMode ? Colors.grey[100] : Colors.grey[850],
),
maxLines: 1,
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 0,
),
),
);
}
/*
METHODS
*/
/// Will be called everytime the input changes. Making callbacks to the Places
/// Api and giving the user Place options
void _onCameraMoveStarted() {
_fn.unfocus();
_closeSearch();
locBusy = true;
}
void _onCameraMove(CameraPosition position) {
_closeSearch();
newLocation = position.target;
locBusy = true;
_textEditingController.text = 'Selected Location';
_fn.unfocus();
}
void _onCameraIdle() {
if (newLocationText != '') _textEditingController.text = newLocationText;
mapLocation = newLocation;
print(mapLocation);
locBusy = false;
newLocationText = '';
}
void _mapTap(LatLng latlng) {
_fn.unfocus();
_closeSearch();
print(latlng);
_mapController.animateCamera(CameraUpdate.newLatLng(latlng));
// _mapController.animateCamera(CameraUpdate.newLatLngBounds(
//     LatLngBounds(
//       southwest: latlng,
//       northeast: latlng,
//     ),
//     100));
}
void _mapLongPress(LatLng latlng) {
_fn.unfocus();
_closeSearch();
print(latlng);
}
void _autocompletePlace() async {
if (_fn.hasFocus) {
setState(() {
_currentInput = _textEditingController.text;
_isEditing = true;
});
_textEditingController.removeListener(_autocompletePlace);
if (_currentInput.isEmpty) {
// if (_currentInput != "Selected Location") _closeSearch();
_textEditingController.addListener(_autocompletePlace);
return;
}
if (_currentInput == _tempInput) {
final predictions = await _makeRequest(_currentInput);
await _animationController.animateTo(0.5);
setState(() => _placePredictions = predictions);
await _animationController.forward();
_textEditingController.addListener(_autocompletePlace);
return;
}
Future.delayed(const Duration(milliseconds: 500), () {
_textEditingController.addListener(_autocompletePlace);
if (_isEditing == true) _autocompletePlace();
});
}
}
Future<void> checkLocationPermission() async {
var locationPermission = Permission.location;
if (await locationPermission.request().isGranted) {
_getCurrentLocation();
} else if (await locationPermission.request().isDenied) {
Fluttertoast.showToast(
gravity: ToastGravity.CENTER,
msg: "Location Permission Denied",
);
} else if (await locationPermission.request().isPermanentlyDenied) {
Fluttertoast.showToast(
gravity: ToastGravity.CENTER,
msg:
"Location Permission Permanently Denied, Cannot Request Permission",
);
} else {
Fluttertoast.showToast(
gravity: ToastGravity.CENTER,
msg: "Please check location permission",
);
}
}
void _getCurrentLocation() async {
// GoogleMapController _mapController;
// Position position;
// Position res = await Geolocator.getCurrentPosition();
// position = res;
Position geoposition = await Geolocator.getCurrentPosition(
desiredAccuracy: LocationAccuracy.high);
if (await Permission.locationWhenInUse.serviceStatus.isEnabled) {
print('GPS enabled');
print(geoposition.latitude);
print(geoposition.longitude);
final coordinates =
Coordinates(geoposition.latitude, geoposition.longitude);
print(coordinates);
var addresses =
await Geocoder.local.findAddressesFromCoordinates(coordinates);
selectedAddress = addresses.first.addressLine;
mapLatitude = geoposition.latitude;
mapLongitude = geoposition.longitude;
newLocationText = selectedAddress;
_mapController.animateCamera(CameraUpdate.newLatLng(
LatLng(geoposition.latitude, geoposition.longitude)));
// _mapController.animateCamera(CameraUpdate.newLatLngBounds(
//     LatLngBounds(
//       southwest: LatLng(
//           geoposition.latitude - 0.0002, geoposition.longitude - 0.0002),
//       northeast: LatLng(
//           geoposition.latitude + 0.0002, geoposition.longitude + 0.0002),
//     ),
//     100));
// readOnly.value = false;
// tapped.value = true;
} else {
Fluttertoast.showToast(
gravity: ToastGravity.CENTER,
msg: "Please turn on your GPS",
);
}
}
/// API request function. Returns the Predictions
Future<dynamic> _makeRequest(input) async {
var url =
// ignore: unnecessary_brace_in_string_interps
'https://maps.googleapis.com/maps/api/place/autocomplete/json?input=$input&key=${mapKey}&language=${language}';
if (mapLocation != null && mapRadius != null) {
url +=
// ignore: unnecessary_brace_in_string_interps
'&location=${mapLocation.latitude},${mapLocation.longitude}&radius=${mapRadius}';
if (strictBounds) {
url += '&strictbounds';
}
if (mapPlaceType != null) {
url += '&types=${mapPlaceType.apiString}';
}
}
final response = await http.get(url);
final json = jsonDecode(response.body);
if (json['error_message'] != null) {
var error = json['error_message'];
if (error == 'This API project is not authorized to use this API.') {
error +=
' Make sure the Places API is activated on your Google Cloud Platform';
}
throw Exception(error);
} else {
final predictions = json['predictions'];
return predictions;
}
}
/// Will be called when a user selects one of the Place options
void _selectPlace({Place prediction}) async {
if (prediction != null) {
// _textEditingController.value = TextEditingValue(
//   text: prediction.description,
//   selection: TextSelection.collapsed(
//     offset: prediction.description.length,
//   ),
// );
newLocationText = prediction.description;
} else {
await Future.delayed(const Duration(milliseconds: 500));
}
// Makes animation
_closeSearch();
// Calls the `onSelected` callback
if (prediction is Place) onSelected(prediction);
}
onSelected(Place place) async {
Geolocation geolocation = await place.geolocation;
_mapController
.animateCamera(CameraUpdate.newLatLng(geolocation.coordinates));
// _mapController
//     .animateCamera(CameraUpdate.newLatLngBounds(geolocation.bounds, 0));
print(place.description);
var newLoc = await Geocoder.local.findAddressesFromQuery(place.description);
print(geolocation.coordinates);
mapLatitude = newLoc.first.coordinates.latitude;
mapLongitude = newLoc.first.coordinates.longitude;
print(mapLatitude);
print(mapLongitude);
}
/// Closes the expanded search box with predictions
void _closeSearch() async {
if (!_animationController.isDismissed) {
await _animationController.animateTo(0.5);
}
_fn.unfocus();
setState(() {
_placePredictions = [];
_isEditing = false;
});
await _animationController.reverse();
_textEditingController.addListener(_autocompletePlace);
}
/// Will listen for input changes every 0.5 seconds, allowing us to make API requests only when the user stops typing.
void customListener() {
if (!mounted) {
return;
}
Future.delayed(const Duration(milliseconds: 500), () {
setState(() => _tempInput = _textEditingController.text);
customListener();
});
}
@override
void dispose() {
_mapController.dispose();
_animationController.dispose();
_textEditingController.dispose();
_fn.dispose();
super.dispose();
}
}

此错误暗示您正在调用setState((){})时,小部件已不在树中。

我没有检查你的整个代码得到问题到底在哪里,但你可以在调用setState((){})之前添加这个:

if(mounted){
// mounted returns true only if the widget is in the tree
setState((){
// do your stuff
});
}

通常,这发生在有一些期货,监听器,http调用或类似的东西,你有。

最新更新