如何解决FLUTTER:如何完全处置有状态的小部件?
这实际上是一个带有谷歌地方搜索功能的谷歌地图。问题是在处理这个有状态小部件时,我收到了这个错误:
E/Flutter (6017): [ERROR:Flutter/lib/ui/ui_dart_state.cc(177)] 未处理的异常: setState() 在 dispose() 之后调用: _MapScreenState#79287(lifecycle state: defunct,not installed,股票代码:跟踪0个股票代码) E/Flutter (6017):如果您对不再出现在小部件树中的小部件(例如,其父小部件不再包含其构建中的小部件)的 State 对象调用 setState() ,则会发生此错误。当代码从计时器或动画回调调用 setState() 时,可能会发生此错误。 E/Flutter(6017):首选的解决方案是在dispose()回调中取消定时器或停止监听动画。另一种解决方案是在调用 setState() 之前检查此对象的“mounted”属性,以确保该对象仍在树中。 E/Flutter (6017):如果 setState() 被调用,则此错误可能表示内存泄漏,因为另一个对象在从树中删除后仍保留对此 State 对象的引用。为避免内存泄漏,请考虑在 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,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,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,)
: Icon(Icons.search,],)),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,locBusy ? -0.1 : -0.06),child: SvgPicture.asset(
"assets/images/ccGPS.svg",width: 28,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,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)),);
}),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
});
}
通常,当您拥有一些 Futures、侦听器、http 调用或类似的东西时,就会发生这种情况。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。