Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Showing Overlay causes crash when using AnimatedContent to animate screens #987

Open
blakelee opened this issue Apr 8, 2023 · 2 comments

Comments

@blakelee
Copy link

blakelee commented Apr 8, 2023

I have been using workflow in a personal project and I'm trying to use screen animations. The screen animations work fine for screen but if I try and use it on an overlay by using BodyAndOverlaysScreen it will crash the app.

Here is a test workflow that will cause it to crash. I simplified the workflow significantly to show the issue.

In my own app I can navigate between screen normally with the AnimatedContent but when I open an Overlay it will crash. Another thing I tried is to return my BodyAndOverlaysScreen without the AnimatedContent when I see that the overlays are not empty. That also caused a crash.

import androidx.compose.animation.AnimatedContent
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
import com.squareup.workflow1.Snapshot
import com.squareup.workflow1.StatefulWorkflow
import com.squareup.workflow1.ui.Screen
import com.squareup.workflow1.ui.compose.ComposeScreen
import com.squareup.workflow1.ui.compose.WorkflowRendering
import com.squareup.workflow1.ui.container.AlertOverlay
import com.squareup.workflow1.ui.container.BodyAndOverlaysScreen

object TestWorkflow : StatefulWorkflow<Unit, Boolean, Unit, Screen>() {
  override fun initialState(props: Unit, snapshot: Snapshot?): Boolean = false

  @OptIn(ExperimentalAnimationApi::class)
  override fun render(renderProps: Unit, renderState: Boolean, context: RenderContext): Screen {
    val dialog = AlertOverlay(title = "Test") {}.takeIf { renderState }

    val screen = BodyAndOverlaysScreen(
      body = ComposeScreen {
        Button(
          onClick = context.eventHandler { state = true },
          modifier = Modifier.wrapContentSize(),
          content = { Text("Open Dialog") }
        )
      },
      overlays = listOfNotNull(dialog)
    )

    return ComposeScreen { viewEnvironment ->
      AnimatedContent(targetState = screen) { targetScreen ->
        WorkflowRendering(rendering = targetScreen, viewEnvironment = viewEnvironment)
      }
    }
  }

  override fun snapshotState(state: Boolean): Snapshot? = null
}

Here is the logcat associated with it

    java.lang.IllegalStateException: Expected a ViewTreeLifecycleOwner on com.squareup.workflow1.ui.container.BodyAndOverlaysContainer{560c713 V.E...... ......I. 0,0-0,0 #7f0801cb app:id/workflow_body_and_modals_container}
        at com.squareup.workflow1.ui.container.LayeredDialogSessions$Companion$forView$2.invoke(LayeredDialogSessions.kt:300)
        at com.squareup.workflow1.ui.container.LayeredDialogSessions$Companion$forView$2.invoke(LayeredDialogSessions.kt:303)
        at com.squareup.workflow1.ui.container.LayeredDialogSessions.update(LayeredDialogSessions.kt:173)
        at com.squareup.workflow1.ui.container.BodyAndOverlaysContainer.update(BodyAndOverlaysContainer.kt:63)
        at com.squareup.workflow1.ui.container.BodyAndOverlaysContainer$Companion$1$1$1.showRendering(BodyAndOverlaysContainer.kt:159)
        at com.squareup.workflow1.ui.container.BodyAndOverlaysContainer$Companion$1$1$1.showRendering(BodyAndOverlaysContainer.kt:158)
        at com.squareup.workflow1.ui.RealScreenViewHolder.runner$lambda$0(RealScreenViewHolder.kt:19)
        at com.squareup.workflow1.ui.RealScreenViewHolder.$r8$lambda$yL9n1S2Um7r5e-awSSvIzkyIEu4(Unknown Source:0)
        at com.squareup.workflow1.ui.RealScreenViewHolder$$ExternalSyntheticLambda0.showRendering(Unknown Source:4)
        at com.squareup.workflow1.ui.ScreenViewHolderKt.show(ScreenViewHolder.kt:84)
        at com.squareup.workflow1.ui.ScreenViewFactoryKt$startShowing$1$4.invoke(ScreenViewFactory.kt:336)
        at com.squareup.workflow1.ui.ScreenViewFactoryKt$startShowing$1$4.invoke(ScreenViewFactory.kt:335)
        at com.squareup.workflow1.ui.ScreenViewFactoryKt.startShowing$lambda$3$lambda$0(ScreenViewFactory.kt:298)
        at com.squareup.workflow1.ui.ScreenViewFactoryKt.$r8$lambda$f7ANZNINIUUEMwpxjsGnHJmWNsE(Unknown Source:0)
        at com.squareup.workflow1.ui.ScreenViewFactoryKt$$ExternalSyntheticLambda0.startView(Unknown Source:0)
        at com.squareup.workflow1.ui.ScreenViewFactoryKt.startShowing(ScreenViewFactory.kt:335)
        at com.squareup.workflow1.ui.ScreenViewFactoryKt.startShowing$default(ScreenViewFactory.kt:285)
        at com.squareup.workflow1.ui.compose.WorkflowRenderingKt$asComposeViewFactory$1$Content$1.invoke(WorkflowRendering.kt:189)
        at com.squareup.workflow1.ui.compose.WorkflowRenderingKt$asComposeViewFactory$1$Content$1.invoke(WorkflowRendering.kt:184)
@lucamtudor
Copy link

If you need a bandaid solution to get you moving forward, wrap your root rendering in a legacy view system WorkflowLayout.

return ComposeScreen { viewEnvironment ->
    AndroidView(
        factory = { context ->
            WorkflowLayout(context)
        },
        update = { workflowLayout ->
            workflowLayout.show(rendering, viewEnvironment)
        },
    )
}

@blakelee
Copy link
Author

blakelee commented May 3, 2023

The legacy layout worked. Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants