mapbox-maps-android icon indicating copy to clipboard operation
mapbox-maps-android copied to clipboard

java.lang.IllegalStateException: Mutating MapInitOptions during composition is not allowed.

Open anonym24 opened this issue 1 year ago • 5 comments

Environment

  • Android OS version: 34 API
  • Devices affected: all
  • Maps SDK Version: 11 beta 4 + Compose extension 1.0.0

I'm trying to use MapBox in Compose and set the needed location on the map using mapViewportState.flyTo() function but it fails with the mentioned exception for some reason, and it's related to using mapInitOptionsFactory = { MapInitOptions(...) }, if I remove mapInitOptionsFactory line code then it works, but it means of course I get default map style

val mapViewportState = rememberMapViewportState
LaunchedEffect(key1 = userInitialLocation) {
    userInitialLocation?.let { userLocation ->
        mapViewportState.flyTo(
            cameraOptions = mapViewportState.cameraState.toCamera
                .center(
                    Point.fromLngLat(
                        userLocation.longitude,
                       userLocation.latitude
                    )
                )
                .zoom(16.0)
                .build()
        )
    }
}
val context = LocalContext.current
if (isLocationPermissionGranted) {
    MapboxMap(
        modifier = Modifier.fillMaxSize(),
        mapViewportState = mapViewportState,
         mapInitOptionsFactory = { // if I comment out this part then it works fine
             MapInitOptions(
                 context = context,
                 styleUri = Style.DARK,
             )
         }
    )

Error was captured in composition while live edit was enabled.
                 java.lang.IllegalStateException: Mutating MapInitOptions during composition is not allowed.
                 	at com.mapbox.maps.extension.compose.internal.MapboxMapNodeKt$MapboxMapComposeNode$2$1.invoke(MapboxMapNode.kt:136)
                 	at com.mapbox.maps.extension.compose.internal.MapboxMapNodeKt$MapboxMapComposeNode$2$1.invoke(MapboxMapNode.kt:135)
                 	at androidx.compose.runtime.ComposerImpl$apply$operation$1.invoke(Composer.kt:1712)
                 	at androidx.compose.runtime.ComposerImpl$apply$operation$1.invoke(Composer.kt:1710)
                 	at androidx.compose.runtime.CompositionImpl.applyChangesInLocked(Composition.kt:818)
                 	at androidx.compose.runtime.CompositionImpl.applyChanges(Composition.kt:849)
                 	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:625)
                 	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:537)
                 	at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:41)
                 	at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109)
                 	at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
                 	at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
                 	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1229)
                 	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1239)
                 	at android.view.Choreographer.doCallbacks(Choreographer.java:899)
                 	at android.view.Choreographer.doFrame(Choreographer.java:827)
                 	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1214)
                 	at android.os.Handler.handleCallback(Handler.java:942)
                 	at android.os.Handler.dispatchMessage(Handler.java:99)
                 	at android.os.Looper.loopOnce(Looper.java:201)
                 	at android.os.Looper.loop(Looper.java:288)
                 	at android.app.ActivityThread.main(ActivityThread.java:7872)
                 	at java.lang.reflect.Method.invoke(Native Method)
                 	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
                 	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)

anonym24 avatar Sep 25 '23 16:09 anonym24

Hey @anonym24 thanks for trying out the compose extension. At the moment, the MapInitOptions is not mutable and can not be used to alter the style during composition.

Until we expose proper runtime styling APIs for compose extension, you can get the raw map controller using MapEffect and load the customised style with it.

pengdev avatar Nov 06 '23 16:11 pengdev

@anonym24 Took a second look at the report, seems the crash happened when live edit is enabled:

Error was captured in composition while live edit was enabled.

Could you confirm the crash does not happen without live edit enabled?

pengdev avatar Nov 13 '23 13:11 pengdev

@anonym24 Took a second look at the report, seems the crash happened when live edit is enabled:

Error was captured in composition while live edit was enabled.

Could you confirm the crash does not happen without live edit enabled?

I can confirm this happens even without live edit. Below is the stack trace. I wanted to change the Style URI on runtime (based on user selection), but using MapEffect seems like didn't change anything, so I tried to do it in MapInitOptions and the crash happened

23:46:55.970 ACRA                       E  ACRA caught a IllegalStateException for com.anrapps.zenit.tracks.debug
                                       java.lang.IllegalStateException: Mutating MapInitOptions during composition is not allowed.
                                       	at com.mapbox.maps.extension.compose.internal.MapboxMapNodeKt$MapboxMapComposeNode$2$1.invoke(MapboxMapNode.kt:136)
                                       	at com.mapbox.maps.extension.compose.internal.MapboxMapNodeKt$MapboxMapComposeNode$2$1.invoke(MapboxMapNode.kt:135)
                                       	at androidx.compose.runtime.ComposerImpl$apply$operation$1.invoke(Composer.kt:1712)
                                       	at androidx.compose.runtime.ComposerImpl$apply$operation$1.invoke(Composer.kt:1710)
                                       	at androidx.compose.runtime.CompositionImpl.applyChangesInLocked(Composition.kt:818)
                                       	at androidx.compose.runtime.CompositionImpl.applyChanges(Composition.kt:849)
                                       	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:625)
                                       	at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:537)
                                       	at androidx.compose.ui.platform.AndroidUiFrameClock$withFrameNanos$2$callback$1.doFrame(AndroidUiFrameClock.android.kt:41)
                                       	at androidx.compose.ui.platform.AndroidUiDispatcher.performFrameDispatch(AndroidUiDispatcher.android.kt:109)
                                       	at androidx.compose.ui.platform.AndroidUiDispatcher.access$performFrameDispatch(AndroidUiDispatcher.android.kt:41)
                                       	at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.doFrame(AndroidUiDispatcher.android.kt:69)
                                       	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1299)
                                       	at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1309)
                                       	at android.view.Choreographer.doCallbacks(Choreographer.java:923)
                                       	at android.view.Choreographer.doFrame(Choreographer.java:847)
                                       	at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1283)
                                       	at android.os.Handler.handleCallback(Handler.java:942)
                                       	at android.os.Handler.dispatchMessage(Handler.java:99)
                                       	at android.os.Looper.loopOnce(Looper.java:226)
                                       	at android.os.Looper.loop(Looper.java:313)
                                       	at android.app.ActivityThread.main(ActivityThread.java:8762)
                                       	at java.lang.reflect.Method.invoke(Native Method)
                                       	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604)
                                       	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)

The code

    @OptIn(MapboxExperimental::class)
    @Composable
    private fun Map(mapStyleUri: String) {
        MapboxMap(
            modifier = Modifier.fillMaxSize(),
            mapInitOptionsFactory = { context ->
                MapInitOptions(
                    context = context,
                    styleUri = mapStyleUri,
                    cameraOptions = CameraOptions.Builder()
                        .center(Point.fromLngLat(24.9384, 60.1699))
                        .zoom(9.0)
                        .build()
                )
            }
        ) {
            // CHANGING URI HERE DOESN'T HAVE ANY EFFECT
    //        MapEffect(Unit) { mapView ->
    //            mapView.mapboxMap.loadStyle(
    //                style(mapStyleUri) {
    //                    +projection(ProjectionName.GLOBE)
    //                    +rasterDemSource("raster-dem") {
    //                        url("mapbox://mapbox.terrain-rgb")
    //                    }
    //                    +terrain("raster-dem") {
    //                        exaggeration(1.25)
    //                    }
    //                }
    //            )
    //        }
        }
    }

bamsbamx avatar Nov 15 '23 22:11 bamsbamx

I can confirm this happens even without live edit. Below is the stack trace. I wanted to change the Style URI on runtime (based on user selection), but using MapEffect seems like didn't change anything, so I tried to do it in MapInitOptions and the crash happened

Hey @bamsbamx. MapEffect worked for me but instead of using Unit as the MapEffect key input, I used the variable that holds the styleUri:

val styleUri = if (isSystemInDarkTheme()) Style.DARK else Style.LIGHT

MapboxMap(
    modifier = Modifier.fillMaxSize(),
    mapInitOptionsFactory = { context ->
        MapInitOptions(
            context = context,
            styleUri = Style.DARK,
            cameraOptions = CameraOptions.Builder()
                .center(Point.fromLngLat(24.9384, 60.1699))
                .zoom(9.0)
                .build()
        )
    }
) {
    MapEffect(styleUri) { mapView ->
        mapView.mapboxMap.loadStyle(styleUri)
    }
}

joaovalentee avatar Dec 05 '23 18:12 joaovalentee

Hi @joaovalentee @bamsbamx @anonym24 we have recently introduced style composable functions in the latest 11.3.0-beta.1 release, please feel free to give it a try and see it fulfils your needs. Please note we are still iterating on this API so expect more changes to this API.

pengdev avatar Mar 26 '24 12:03 pengdev

Closing as related APIs have been refined, please check the latest compose extension release and test app for reference.

pengdev avatar May 02 '24 21:05 pengdev