Open hellomentorplus opened 1 year ago
Thanks for the report. Using code sample provided and running on iOS, observed that only onCameraIdle
is printed first but not when tapping the button.
flutter: onCamera idle
Problem The Google Maps Flutter library simply calls the onCameraIdle callback whenever the associated onCameraIdle callback is called within the platform specific SDK. In the Google Maps SDK for Android, the map doesn't have to move for onCameraIdle to be called. In the Google Maps SDK for iOS the map does have to move for onCameraIdle to be called.
Best fix I believe this bug should be fixed in the Google Maps iOS SDK. Google built both SDKs so they should have a consistent definition of what "idle" means. If the map moves from its current position to its current position, did it idle? I don't see why this should be platform dependent.
Package fix If it's determined that the SDKs should not be updated, then the Flutter plugin should check if the camera update being given to the iOS SDK is "significant". If it's not a "significant" change, the Flutter plugin should call onCameraIdle itself since it knows that the iOS SDK won't.
Workaround Extend or create a wrapper around your GoogleMapController. Any time, the moveCamera or animateCamera method is called, check if the camera change is "significant" by comparing it to the last camera position given by the onCameraMoved callback. If it is, then the onCameraIdle callback will be called properly so you can call the method on the actual controller as usual. Else, call the onCameraIdle callback yourself since you know that the package won't.
What is "significant"? You can't just check if the change to the camera is zero. Instead, you must check if it's "significant". This is because the iOS maps plugin uses a float32 for latitude, longitude, and zoom (bearing and tilt are float64). Whereas, Dart uses a double. This means that if the change you request is small enough, but not zero, the iOS maps plugin may consider it as no change. For example, if the current zoom of the map is 13 and you request 13.0000001, the Flutter plugin will cast it to a float32, which gives: 13.000000. Since, this is equivalent to the last zoom, it will not move and not call onCameraIdle. Therefore, you can't just check:
if (previousZoom != newZoom || previousLatitude != newLatitude || previousLongitude != newLongitude)
You need to cast all values to Float32 first, then compare them:
Future<bool?> _isSignificantChange(LatLng target, double zoom) async {
final currentRoundedLatitude = _getFloat32(
currentCameraPosition.target.latitude,
);
final currentRoundedLongitude = _getFloat32(
currentCameraPosition.target.longitude,
);
final currentRoundedZoom = _getFloat32(
currentCameraPosition.zoom,
);
final currentRoundedZoom = _getFloat32(
currentCameraPosition.zoom,
);
final targetRoundedLatitude = _getFloat32(target.latitude);
final targetRoundedLongitude = _getFloat32(target.longitude);
final targetRoundedZoom = _getFloat32(zoom);
return currentRoundedLatitude != targetRoundedLatitude ||
currentRoundedLongitude != targetRoundedLongitude ||
currentRoundedZoom != targetRoundedZoom;
}
double _getFloat32(double value) {
final bytes = ByteData(4);
bytes.setFloat32(0, value);
return bytes.getFloat32(0);
}
Steps to Reproduce
Create a simple GoogleMap widget
Observation
On Android device, when tapping on 'trigger move camera', both
onCameraMoveStarted
andonCameraIdle
will be triggered although camera has the same camera position.On iOS device, only 1 line 'flutter: onCamera idle' is printed, tapping on 'trigger move camera' without actually move camera will not trigger moving camera or call
onCameraIdle
. When move map to different camera position, and tap on 'trigger move camera', then bothonCameraMoveStarted
andonCameraIdle
are called respectivelyExpectation
Behaviour to be the same on both Android and iOS.