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

When used in a LazyColumn, GoogleMap cannot be scrolled vertically

Open JRR-OSU opened this issue 2 years ago • 17 comments

Thanks for your work on this one, it's really great to finally have an official Compose wrapper for Google Maps!

In our use case, we have a custom GoogleMaps wrapper using AndroidView that sits inside a LazyColumn. It is able to be scrolled vertically and horizontally without scrolling the LazyColumn by using a transparent view and calling .requestDisallowInterceptTouchEvent(true) on the parent.

We are hoping to switch to this, but we noticed it still suffers from the original issue we had to work around. Is usage in a LazyColumn a supported use case and is there a more Compose-friendly way to get nested scrolling working properly with this wrapper?

Thanks!

Steps to reproduce

  1. Include GoogleMap in a LazyColumn
  2. Vertically drag on the map to attempt to scroll it
  3. Observe the LazyColumn scrolls instead of the map

Thanks!

JRR-OSU avatar Feb 07 '22 16:02 JRR-OSU

@JRR-OSU Thank you for opening this issue. 🙏 Please check out these other resources that might be applicable:

This is an automated message, feel free to ignore.

jpoehnelt avatar Feb 07 '22 16:02 jpoehnelt

I have not yet been able to test this scenario. Perhaps a boolean property should be exposed in the GoogleMap composable to support the case when the map is nested in a scrollable parent as implementing the default GoogleMap using the solution you had mentioned may have unintended consequences in other use cases. Feel free to chime in with other ideas as well.

arriolac avatar Feb 08 '22 22:02 arriolac

I successfully solved it with cameraPositionState: Column ( .verticalScroll(rememberScrollState(), enabled = !cameraPositionState.isMoving) )

sandorbogyo avatar Mar 06 '22 12:03 sandorbogyo

Thanks @sandorbogyo. Just confirmed that that solution works.

arriolac avatar Mar 07 '22 17:03 arriolac

I think it'd be nice to have that solution in the documentation, what do you think?

LouisCAD avatar Mar 07 '22 18:03 LouisCAD

@LouisCAD good idea. Would be good to have under a "Common Patterns" section. I'd be happy to add but feel free to send a pull request as well.

arriolac avatar Mar 07 '22 18:03 arriolac

If you have the time, it's best that you do it, I'm quite busy working with OSS projects and starting a company.

LouisCAD avatar Mar 07 '22 18:03 LouisCAD

This solution does not work as mentioned here link

Please re open this issue and make sure to test it for yourself before accepting a solution

basurahan avatar Apr 09 '22 23:04 basurahan

I'd like to request for this issue to be reopened, as it has not been adequately addressed. There is a documented workaround available now thanks to @barbeau, but that's all it is, a workaround, and it's fairly messy. It's also unclear what parts of the elaborate sample code are needed to actually make it work and what can be left out. (I figured it out via my own minimal sample project.)

A Compose component that is draggable by nature should expose draggable behavior out of the box even in the context of nested scrolling, without additional configuration needed.

A custom wrapper using requestDisallowInterceptTouchEvent()as described in the OP may be a good approach, and as @arriolac suggested it can be controlled by a flag for the time being to gather feedback. This is the approach that I've used for years, but maps-compose has taken it away for now.

Another, preferable, avenue that should be fully explored is the standard Compose nested scrolling support that has been worked on here: https://issuetracker.google.com/issues/174348612. The work is ongoing and I have pointed out its current inapplicability to MapView, so this would be the time to connect with the people behind it and see if it can be done.

bubenheimer avatar Jun 10 '22 16:06 bubenheimer

Sorry for the very late reply here, I've finally had some time to circle back to this library.

In #78 a solution was added to allow for scrolling working inside a normal Column. The solution doesn't work for LazyColumn, but I've found it can easily be adapted to work. Just swap out the MapInColumn with the code below in the test app to see it in action. Seems to work well!

If others can verify this works for them, I'd like to see the docs just updated to show something like this as an example, and then we can again close this issue. Thanks all!

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun MapInLazyColumn(modifier: Modifier = Modifier,
                        cameraPositionState: CameraPositionState,
                        columnScrollingEnabled: Boolean,
                        onMapTouched: () -> Unit,
                        onMapLoaded: () -> Unit,) {
    var isMapLoaded by remember { mutableStateOf(false) }
    LazyColumn(modifier, userScrollEnabled = columnScrollingEnabled) {
        item {
            Box(
                Modifier
                    .fillMaxWidth()
                    .height(200.dp)
            ) {
                GoogleMapViewInColumn(
                    modifier = Modifier
                        .fillMaxSize()
                        .testTag("Map")
                        .pointerInteropFilter(
                            onTouchEvent = {
                                when (it.action) {
                                    MotionEvent.ACTION_DOWN -> {
                                        onMapTouched()
                                        false
                                    }
                                    else -> {
                                        Log.d(
                                            TAG,
                                            "MotionEvent ${it.action} - this never triggers."
                                        )
                                        true
                                    }
                                }
                            }
                        ),
                    cameraPositionState = cameraPositionState,
                    onMapLoaded = {
                        isMapLoaded = true
                        onMapLoaded()
                    },
                )
                if (!isMapLoaded) {
                    AnimatedVisibility(
                        modifier = Modifier
                            .fillMaxSize(),
                        visible = !isMapLoaded,
                        enter = EnterTransition.None,
                        exit = fadeOut()
                    ) {
                        CircularProgressIndicator(
                            modifier = Modifier
                                .background(MaterialTheme.colors.background)
                                .wrapContentSize()
                        )
                    }
                }
            }
        }

        items(100) { item ->
            Text("$item", modifier = Modifier
                .padding(start = 10.dp, bottom = 10.dp)
                .testTag("Item $item"))
        }
    }
}

JRR-OSU avatar Oct 07 '22 20:10 JRR-OSU

Sorry for the very late reply here, I've finally had some time to circle back to this library.

In #78 a solution was added to allow for scrolling working inside a normal Column. The solution doesn't work for LazyColumn, but I've found it can easily be adapted to work. Just swap out the MapInColumn with the code below in the test app to see it in action. Seems to work well!

If others can verify this works for them, I'd like to see the docs just updated to show something like this as an example, and then we can again close this issue. Thanks all!

@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun MapInLazyColumn(modifier: Modifier = Modifier,
                        cameraPositionState: CameraPositionState,
                        columnScrollingEnabled: Boolean,
                        onMapTouched: () -> Unit,
                        onMapLoaded: () -> Unit,) {
    var isMapLoaded by remember { mutableStateOf(false) }
    LazyColumn(modifier, userScrollEnabled = columnScrollingEnabled) {
        item {
            Box(
                Modifier
                    .fillMaxWidth()
                    .height(200.dp)
            ) {
                GoogleMapViewInColumn(
                    modifier = Modifier
                        .fillMaxSize()
                        .testTag("Map")
                        .pointerInteropFilter(
                            onTouchEvent = {
                                when (it.action) {
                                    MotionEvent.ACTION_DOWN -> {
                                        onMapTouched()
                                        false
                                    }
                                    else -> {
                                        Log.d(
                                            TAG,
                                            "MotionEvent ${it.action} - this never triggers."
                                        )
                                        true
                                    }
                                }
                            }
                        ),
                    cameraPositionState = cameraPositionState,
                    onMapLoaded = {
                        isMapLoaded = true
                        onMapLoaded()
                    },
                )
                if (!isMapLoaded) {
                    AnimatedVisibility(
                        modifier = Modifier
                            .fillMaxSize(),
                        visible = !isMapLoaded,
                        enter = EnterTransition.None,
                        exit = fadeOut()
                    ) {
                        CircularProgressIndicator(
                            modifier = Modifier
                                .background(MaterialTheme.colors.background)
                                .wrapContentSize()
                        )
                    }
                }
            }
        }

        items(100) { item ->
            Text("$item", modifier = Modifier
                .padding(start = 10.dp, bottom = 10.dp)
                .testTag("Item $item"))
        }
    }
}

I wasn't able to get it working exactly like this, but the experimental motionEventSpy worked like a charm:

var columnScrollingEnabled: Boolean by remember { mutableStateOf(true) }

...

LazyColumn(userScrollEnabled = columnScrollingEnabled)

...

item {
    GoogleMap(Modifier.motionEventSpy {
        when (it.action) {
            MotionEvent.ACTION_DOWN -> {
                columnScrollingEnabled = false
            }
            MotionEvent.ACTION_UP -> {
                columnScrollingEnabled = true
            }
        }
    },
...

ptornhult avatar Nov 29 '22 14:11 ptornhult

I successfully solved it with cameraPositionState: Column ( .verticalScroll(rememberScrollState(), enabled = !cameraPositionState.isMoving) )

Didn't work for me

funyin avatar Dec 18 '22 21:12 funyin

I wasn't able to get it working exactly like this, but the experimental motionEventSpy worked like a charm:

var columnScrollingEnabled: Boolean by remember { mutableStateOf(true) }

...

LazyColumn(userScrollEnabled = columnScrollingEnabled)

...

item {
    GoogleMap(Modifier.motionEventSpy {
        when (it.action) {
            MotionEvent.ACTION_DOWN -> {
                columnScrollingEnabled = false
            }
            MotionEvent.ACTION_UP -> {
                columnScrollingEnabled = true
            }
        }
    },
...

By @ptornhult , This worked for me

funyin avatar Dec 18 '22 21:12 funyin

This issue has been automatically marked as stale because it has not had recent activity. Please comment here if it is still valid so that we can reprioritize. Thank you!

stale[bot] avatar Jun 18 '23 08:06 stale[bot]

the experimental motionEventSpy worked like a charm:

var columnScrollingEnabled: Boolean by remember { mutableStateOf(true) }

...

LazyColumn(userScrollEnabled = columnScrollingEnabled)

...

item {
    GoogleMap(Modifier.motionEventSpy {
        when (it.action) {
            MotionEvent.ACTION_DOWN -> {
                columnScrollingEnabled = false
            }
            MotionEvent.ACTION_UP -> {
                columnScrollingEnabled = true
            }
        }
    },
...

Indeed it works fine. Tip for those who have a map nested in multiple composable layers, but don't want to pass state parameter through each. Use CompositionLocalProvider to incapsulate logic of accessing/providing current pressed state in a custom class.

flaringapp avatar Aug 04 '23 12:08 flaringapp

TODO: update the docs / sample code to demonstrate this LazyColumn workaround.

wangela avatar Aug 11 '23 23:08 wangela