-
-
Notifications
You must be signed in to change notification settings - Fork 30
Description
Platforms
Android
Version of flutter-maplibre
0.3.3+2
Bug Description
Crash Log
Caused by com.github.dart_lang.jni.PortProxyBuilder$DartException: Bad
state: Use after release error
at
com.github.dart_lang.jni.PortProxyBuilder._invoke(PortProxyBuilder.java)
at
com.github.dart_lang.jni.PortProxyBuilder.invoke(PortProxyBuilder.java:160)
at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
at $Proxy8.onCameraMove()
at org.maplibre.android.maps.CameraChangeDispatcher.executeOnCameraMo
ve(CameraChangeDispatcher.java:121)
at org.maplibre.android.maps.CameraChangeDispatcher$CameraChangeHandl
er.handleMessage(CameraChangeDispatcher.java:172)
at android.os.Handler.dispatchMessage(Handler.java:114)
at android.os.Looper.loopOnce(Looper.java:266)
at android.os.Looper.loop(Looper.java:361)
at android.app.ActivityThread.main(ActivityThread.java:10320)
at java.lang.reflect.Method.invoke(Method.java)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(Runtim
eInit.java:675)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1002)
Claude Analysis
Fix: MapLibre "Use after release" crash on Android
Context
The app crashes on Android when navigating away from a map screen. The
native CameraChangeDispatcher continues to fire onCameraMove() after the
Dart-side JNI proxy objects have been released during widget disposal.
Root cause in maplibre-0.3.3+2 at
lib/src/platform/android/map_state.dart:268-280: the dispose() method
calls _jMap?.release() which releases the Dart JNI reference, but never
unregisters the camera/click listeners from the native MapLibreMap. The
native side still holds references to the proxy objects and tries to
invoke them after release.
The JNI bindings already have removeOnCameraMoveListener(),
removeOnCameraIdleListener(), removeOnCameraMoveStartedListener(),
removeOnMapClickListener(), and removeOnMapLongClickListener() — they just
aren't called.
Approach: Patch maplibre via local copy
Since this is a single-file fix in a pub package, we'll copy the package
source, patch it, and use dependency_overrides.
Step 1: Copy the package
Copy /Users/tom/.pub-cache/hosted/pub.dev/maplibre-0.3.3+2/ into
packages/maplibre/ in the project.
Step 2: Patch packages/maplibre/lib/src/platform/android/map_state.dart
Store listener references — add fields after line 33:
jni.MapLibreMap$OnCameraMoveListener? _onCameraMoveListener;
jni.MapLibreMap$OnCameraIdleListener? _onCameraIdleListener;
jni.MapLibreMap$OnCameraMoveStartedListener? _onCameraMoveStartedListener;
jni.MapLibreMap$OnMapClickListener? _onMapClickListener;
jni.MapLibreMap$OnMapLongClickListener? _onMapLongClickListener; Capture listeners in onMapReady() — assign each .implement() result to its
field before passing to add*Listener(). For example:
_onCameraMoveListener =
jni.MapLibreMap$OnCameraMoveListener.implement(...);
_jMap.addOnCameraMoveListener(_onCameraMoveListener!);
Same for idle, move-started, click, and long-click listeners.
Remove listeners in dispose() — before _jMap?.release(), add:
if (_onCameraMoveListener != null) {
_jMap?.removeOnCameraMoveListener(_onCameraMoveListener!);
_onCameraMoveListener?.release();
}
if (_onCameraIdleListener != null) {
_jMap?.removeOnCameraIdleListener(_onCameraIdleListener!);
_onCameraIdleListener?.release();
}
if (_onCameraMoveStartedListener != null) {
_jMap?.removeOnCameraMoveStartedListener(_onCameraMoveStartedListener!);
_onCameraMoveStartedListener?.release();
}
if (_onMapClickListener != null) {
_jMap?.removeOnMapClickListener(_onMapClickListener!);
_onMapClickListener?.release();
}
if (_onMapLongClickListener != null) {
_jMap?.removeOnMapLongClickListener(_onMapLongClickListener!);
_onMapLongClickListener?.release();
}
Step 3: Add dependency override in pubspec.yaml
dependency_overrides:
maplibre:
path: packages/maplibre
Files to modify
- pubspec.yaml — add dependency override
- packages/maplibre/lib/src/platform/android/map_state.dart — the fix
(store + remove listeners)
Verification
- flutter pub get
- Build and run on an Android device/emulator
- Navigate to map screen, pan/zoom the map
- Press back to leave the map screen — should not crash
- Repeat rapidly several times
- Check adb logcat for any "Use after release" errors
Steps to Reproduce
open a map via Navigator.push, move around, trigger Navigator.pop
Expected Results
no crash
Actual Results
crash log, see above
Code Sample
// Paste your code hereMetadata
Metadata
Assignees
Labels
Projects
Status