raamcosta / compose-destinations

Annotation processing library for type-safe Jetpack Compose navigation with no boilerplate.
https://composedestinations.rafaelcosta.xyz
Apache License 2.0
3.2k stars 132 forks source link

Possible dependencies' versions mismatch (v2) #610

Closed marcelskotnickifm closed 5 months ago

marcelskotnickifm commented 5 months ago

Hi! First of all great work, much appreciated đź‘Ź I am creating a Snackbar-like component

@Composable
fun MainScreen(viewModel: MainViewModel) {
    val scope = rememberCoroutineScope()
    val navController = rememberNavController()
    val snackbarState = remember(scope) { SnackbarHandler(scope) }

    AppTheme {
        Box(...) {
            Scaffold(...)
            Snackbar(
                snackbarHandler = snackbarState,
                navController = navController,
            )
        }
    }
}

where Snackbar is

@Composable
fun Snackbar(
    navController: NavController,
    snackbarHandler: SnackbarHandler,
    ...
) {
    ...
    val snackbarHostState = remember { SnackbarHostState() }
    var isVisible by remember { mutableStateOf(false) }
    val currentSnackbarData = snackbarHostState.currentSnackbarData

    val destinationChangedListener = OnDestinationChangedListener { _, _, _ ->
        snackbarHostState.currentSnackbarData?.dismiss()
        isVisible = false
    }

    DisposableEffect(Unit) {
        navController.addOnDestinationChangedListener(destinationChangedListener)
        onDispose {
            navController.removeOnDestinationChangedListener(destinationChangedListener)
        }
    }

    LaunchedEffect(Unit) {
        snackbarHandler.showSnackBarEvent.collect { data ->
            val message = ...
            message?.let { snackbarHostState.showSnackbar(it) }
        }
    }

    val dismissCurrentSnackbar: suspend () -> Unit = {
        currentSnackbarData?.let {
            isVisible = false
            ...
            it.dismiss()
        }
    }

    LaunchedEffect(currentSnackbarData) {
        ...
        if (currentSnackbarData != null) {
            val duration = calculateSnackbarDuration(currentSnackbarData.visuals.message)
            isVisible = true
            delay(duration)
            dismissCurrentSnackbar()
        }
    }

    AnimatedVisibility(
        visible = isVisible,
        enter = slideInVertically(initialOffsetY = { it }),
        exit = slideOutVertically(targetOffsetY = { it }),
        modifier = modifier
            .padding(...)
            .clipToBounds()
    ) {                                                            // <--- STACK TRACE POINTS TO THIS LINE 
        currentSnackbarData?.let { snackbarData ->
            Box(...) {
                Text(
                    text = snackbarData.visuals.message,
                    ...
                )
            }
        }
    }
}

class SnackbarHandler(private val scope: CoroutineScope) {

    private val _showSnackBarEvent = MutableSharedFlow<SnackbarData>() // extraBufferCapacity = 1?
    val showSnackBarEvent: SharedFlow<SnackbarData> = _showSnackBarEvent

    fun showSnackbar(...) {
        scope.launch { _showSnackBarEvent.emit(SnackbarData(...)) }
    }

and the stack trace to the crash (happens the moment app opens) is

FATAL EXCEPTION: main
                 Process: pl.bigstar.app, PID: 22582
                 java.lang.ClassCastException: androidx.compose.runtime.DisposableEffectImpl cannot be cast to androidx.compose.runtime.internal.ComposableLambdaImpl
                    at androidx.compose.runtime.internal.ComposableLambdaKt.rememberComposableLambda(ComposableLambda.kt:629)
                    at pl.bigstar.app.presentation.common.components.SnackbarKt.Snackbar(Snackbar.kt:114)
                    at pl.bigstar.app.presentation.common.components.SnackbarKt$Snackbar$7.invoke(Unknown Source:19)
                    at pl.bigstar.app.presentation.common.components.SnackbarKt$Snackbar$7.invoke(Unknown Source:10)
                    at androidx.compose.runtime.RecomposeScopeImpl.compose(RecomposeScopeImpl.kt:192)
                    at androidx.compose.runtime.ComposerImpl.recomposeToGroupEnd(Composer.kt:2763)
                    at androidx.compose.runtime.ComposerImpl.skipCurrentGroup(Composer.kt:3054)
                    at androidx.compose.runtime.ComposerImpl.doCompose(Composer.kt:3545)
                    at androidx.compose.runtime.ComposerImpl.recompose$runtime_release(Composer.kt:3490)
                    at androidx.compose.runtime.CompositionImpl.recompose(Composition.kt:948)
                    at androidx.compose.runtime.Recomposer.performRecompose(Recomposer.kt:1196)
                    at androidx.compose.runtime.Recomposer.access$performRecompose(Recomposer.kt:130)
                    at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:607)
                    at androidx.compose.runtime.Recomposer$runRecomposeAndApplyChanges$2$1.invoke(Recomposer.kt:576)
                    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:7898)
                    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)
                    Suppressed: kotlinx.coroutines.internal.DiagnosticCoroutineContextException: [androidx.compose.runtime.PausableMonotonicFrameClock@3bfacfa, androidx.compose.ui.platform.MotionDurationScaleImpl@2ad02ab, StandaloneCoroutine{Cancelling}@5bc6d08, AndroidUiDispatcher@6a30ca1]

while using

compose = "1.7.0-alpha06"
destinations = "2.1.0-beta01"
compose-material3 = "1.3.0-alpha04"

Placing Snackbar in Scaffold's snackbarHost didn't change a thing. Removing references from AnimatedVisibilityScope to the outside fields and values stops the app from crashing , but of course breaks the snackbar's look & behavior. Changing dependencies' versions to

compose = "1.6.5"
destinations = "2.0.0-beta01"
compose-material3 = "1.2.1"

makes everything work properly, but update any of them and the issue arise.

raamcosta commented 5 months ago

Doesn't seem like anything related with the library.

I'm closing, let me know if you really think it is related to CD and why.

marcelskotnickifm commented 5 months ago

@raamcosta Because using it (snackbarHandler) in @Destination<RootGraph> screen crashes the app, meanwhile using it outside of navigation scope works just fine. As I said, it only happens when using the latest compose, CD & material3 versions.

raamcosta commented 5 months ago

As you can see in the stack trace, there’s no mention of compose destinations package.

I don’t have time to get to the root cause, but I’m confident this is not related to this library. Maybe it is with official compose navigation (which we use internally).

raamcosta commented 5 months ago

If you’re still convinced it’s related to this library directly, try a sample with official library directly and no compose destinations. If the problem doesn’t exist there, do let me know.