We have seen a difference in the behavior with the...
# appyx
p
We have seen a difference in the behavior with the interaction with parent views while a child is attached. Our use case in our app is: We show a Google Maps on the background the whole time and we have our own UI on top of it. Users can interact with our UI and also with the map on the background. With 1.3.0 this was the behavior by default but bumping to 2.0.0-alpha04 we see the parent view stops getting any interaction (see video attached).
Minimum reproducible code 1.3.0
Copy code
class MainActivity : ComponentActivity() {

    private lateinit var appyxIntegrationPoint: ActivityIntegrationPoint

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        appyxIntegrationPoint = ActivityIntegrationPoint(
            activity = this,
            savedInstanceState = savedInstanceState
        )
        setContent {
            NodeHost(appyxIntegrationPoint) {
                RootNode(it)
            }
        }
    }
}

class RootNode(
    buildContext: BuildContext,
    private val backStack: BackStack<NavTarget> = BackStack(
        initialElement = NavTarget.Child1,
        savedStateMap = buildContext.savedStateMap
    )
) : ParentNode<RootNode.NavTarget>(
    buildContext = buildContext,
    navModel = backStack
) {

    sealed class NavTarget : Parcelable {
        @Parcelize
        data object Child1 : NavTarget()
    }

    override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node =
        when (navTarget) {
            NavTarget.Child1 -> Child1Node(
                buildContext = buildContext
            )
        }

    @Composable
    override fun View(modifier: Modifier) {

        Box(modifier = modifier.fillMaxSize()) {
            var selected by remember { mutableStateOf(true) }
            Box(
                modifier = modifier
                    .fillMaxSize()
                    .background(
                        if (selected) {
                            Color.Cyan
                        } else {
                            Color.Red
                        }
                    ).clickable {
                        selected = !selected
                    },
                contentAlignment = Alignment.Center,
            ) {
                Text(
                    text = "Google Maps Appyx 1.3.0"
                )
            }
            Children(
                modifier = modifier.fillMaxSize(),
                navModel = backStack,
                transitionHandler = rememberBackstackSlider(transitionSpec = { spring() })
            )
        }
    }
}

class Child1Node(
    buildContext: BuildContext
) : Node(buildContext) {

    @Composable
    override fun View(modifier: Modifier) {
        Box(
            modifier = modifier,
            contentAlignment = Alignment.BottomCenter
        ) {
            Box(modifier = Modifier
                .fillMaxWidth()
                .height(200.dp)
                .padding(10.dp)
                .background(Color.Black.copy(alpha = 0.3f))
                .clickable {  }
            )
        }
    }
}
Minimum reproducible code 2.0.0-alpha04
Copy code
class MainActivity : ComponentActivity() {

    private lateinit var appyxIntegrationPoint: ActivityIntegrationPoint

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        appyxIntegrationPoint = ActivityIntegrationPoint(
            activity = this,
            savedInstanceState = savedInstanceState
        )
        setContent {
            NodeHost(
                lifecycle = AndroidLifecycle(LocalLifecycleOwner.current.lifecycle),
                integrationPoint = appyxIntegrationPoint
            ) {
                RootNode(it)
            }
        }
    }
}

class RootNode(
    buildContext: BuildContext,
    private val backStack: BackStack<NavTarget> = BackStack(
        model = BackStackModel(
            initialTarget = NavTarget.Child1,
            savedStateMap = buildContext.savedStateMap
        ),
        motionController = { BackStackSlider(it) }
    )
) : ParentNode<RootNode.NavTarget>(
    appyxComponent = backStack,
    buildContext = buildContext
) {

    sealed class NavTarget : Parcelable {
        @Parcelize
        data object Child1 : NavTarget()
    }

    override fun resolve(interactionTarget: NavTarget, buildContext: BuildContext): Node =
        when (interactionTarget) {
            NavTarget.Child1 -> Child1Node(
                buildContext = buildContext
            )
        }

    @Composable
    override fun View(modifier: Modifier) {

        Box(modifier = modifier.fillMaxSize()) {
            var selected by remember { mutableStateOf(true) }
            Box(
                modifier = modifier
                    .fillMaxSize()
                    .background(
                        if (selected) {
                            Color.Cyan
                        } else {
                            Color.Red
                        }
                    ).clickable {
                        selected = !selected
                    },
                contentAlignment = Alignment.Center,
            ) {
                Text(
                    text = "Google Maps Appyx 2.0.0-alpha04"
                )
            }
            AppyxComponent(
                appyxComponent = backStack,
                modifier = modifier.fillMaxSize()
            )
        }
    }
}

class Child1Node(
    buildContext: BuildContext
) : Node(buildContext) {

    @Composable
    override fun View(modifier: Modifier) {
        Box(
            modifier = modifier,
            contentAlignment = Alignment.BottomCenter
        ) {
            Box(modifier = Modifier
                .fillMaxWidth()
                .height(200.dp)
                .padding(10.dp)
                .background(Color.Black.copy(alpha = 0.3f))
                .clickable {  }
            )
        }
    }
}
a
Hi @pepos thanks for sharing this! I believe this will be fixed once https://github.com/bumble-tech/appyx/pull/585 is merged. (Check the bottom of PR description)
The latest alpha has a known bug where clicks on children UI don’t work (intercepted by gesture navigation handling)
z
@pepos The fix has been merged and will be published with the next alpha release. Thanks for the report!
p
2.0.0 upgrade is looking amazing
thank you for the work!
z
thank you! much appreciated
p
@Andrei Kovalev @Zsolt just letting you know: I tested on 2.0.0-alpha05 but still having same issue
a
@pepos could you submit an issue and provide the code snippet please?
p
yep, will do today 👍
gratitude thank you 1
a
Thanks! We’ll take a look
z
Thank you @pepos! My an initial impression is: 1. You have the clickable
Box
as a sibling in the hierarchy to
Children
(1.3) /
AppyxComponent
(2.0) 2. In Appyx 1.x we don’t have gesture control, so
Children
does not add any pointerInput-related code to the composition – meaning when compose figures out which element can handle the input, it goes to your
Box
3. In Appyx 2.x we have gesture control for every component, and
AppyxComponent
adds relevant pointerInput handling – but whenever the event is not a drag but a click, compose will bubble up the handling to other composables; however, your clickable is set on the
Box
which is not a parent in the composable hierarchy but a siblint, so the bubbling up is never passed to it I assume the same scenario would happen even if not using Appyx 2.0 just any other composable with pointerInput handling on it. We can consider adding a flag for disabling gestures, but the same scenario could still happen if client code adds gesture handling anywhere down in the composition.
@pepos from https://developer.android.com/jetpack/compose/touch-input/pointer-input/understand-gestures:
• By default, when there are multiple eligible composables on the same level of the tree, only the composable with the highest z-index is “hit”. For example, when you add two overlapping
Button
composables to a
Box
, only the one drawn on top receives any pointer events. You can theoretically override this behavior by creating your own
PointerInputModifierNode
implementation and setting
sharePointerInputWithSiblings
to true.
I wonder if this would help your case.
p
@Zsolt I somehow missed this message sorry
we have been using successfully 2.0.0 with a modified version of
AppyxInteractionsContainer
opened a PR with our changes: https://github.com/bumble-tech/appyx/pull/707/files