Skip to content

Allow GoogleMap opt out of keyboard focus traversal #938

Description

@svendroid

Summary

Please expose an official way to make GoogleMap opt out of keyboard focus traversal.

Problem

In our app, GoogleMap is used as a background, which is overlayed by different content e.g. sheets. In accessibility mode the user can control the map via additional ui e.g. zoom-buttons, pan-buttons, etc.

In this setup, the map itself should not receive focus during keyboard traversal because to the user it seems like the focus is hidden behind the overlaying content. It seems like a bug that the map is focusable because you cannot interact with it as a keyboard user and therefore the keyboard focus gets hidden.

Compose focus APIs such as:

Modifier.focusProperties { canFocus = false }

does not work.

Expected behavior

A supported API such as:

GoogleMap(
    modifier = Modifier.fillMaxSize(),
    focusable = false,
)

Similiar to mergeDescendants for controlling talkback.

Reproduction

Place GoogleMap behind several focusable Compose controls
Traverse with Tab / Shift+Tab
Focus still lands on the map (see video, the background with the map gets dark)

Example

For a minimal example see https://github.com/user-attachments/assets/85a14302-5da9-42c3-a10c-9b0fc75386f3. The app is traverserd with tab from a keyboard. The background with the map should not receive focus, only the buttons and the zoom buttons.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContent {
            FocusComposeMapProblemTheme {
                MapScreen()
            }
        }
    }
}

@Composable
fun MapScreen() {
    val context = LocalContext.current

    // Default camera position: Berlin
    val berlin = remember { LatLng(52.5200, 13.4050) }
    val cameraPositionState = rememberCameraPositionState {
        position = CameraPosition.fromLatLngZoom(berlin, 12f)
    }

    Box(modifier = Modifier.fillMaxSize()) {

        // Fullscreen Google Map
        GoogleMap(
            modifier = Modifier.fillMaxSize()
                .focusable(false),
            cameraPositionState = cameraPositionState
        ) {
            Marker(
                state = MarkerState(position = berlin),
                title = "Berlin",
                snippet = "Hauptstadt von Deutschland"
            )
        }

        // 3 overlay buttons at the bottom center
        Row(
            modifier = Modifier
                .align(Alignment.BottomCenter)
                .navigationBarsPadding()
                .padding(bottom = 24.dp),
            horizontalArrangement = Arrangement.spacedBy(12.dp)
        ) {
            Button(
                onClick = {
                    Toast.makeText(context, "Standort", Toast.LENGTH_SHORT).show()
                },
                colors = ButtonDefaults.buttonColors(
                    containerColor = MaterialTheme.colorScheme.primaryContainer,
                    contentColor = MaterialTheme.colorScheme.onPrimaryContainer
                )
            ) {
                Text("📍 Standort")
            }

            Button(
                onClick = {
                    Toast.makeText(context, "Route", Toast.LENGTH_SHORT).show()
                },
                colors = ButtonDefaults.buttonColors(
                    containerColor = MaterialTheme.colorScheme.secondaryContainer,
                    contentColor = MaterialTheme.colorScheme.onSecondaryContainer
                )
            ) {
                Text("🗺️ Route")
            }

            Button(
                onClick = {
                    Toast.makeText(context, "Suche", Toast.LENGTH_SHORT).show()
                },
                colors = ButtonDefaults.buttonColors(
                    containerColor = MaterialTheme.colorScheme.tertiaryContainer,
                    contentColor = MaterialTheme.colorScheme.onTertiaryContainer
                )
            ) {
                Text("🔍 Suche")
            }
        }
    }
}
[versions]
agp = "9.2.1"
coreKtx = "1.19.0"
junit = "4.13.2"
junitVersion = "1.3.0"
espressoCore = "3.7.0"
lifecycleRuntimeKtx = "2.11.0"
activityCompose = "1.13.0"
kotlin = "2.4.0"
composeBom = "2026.06.00"
mapsCompose = "8.3.0"
playServicesMaps = "20.0.0"

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    triage meI really want to be triaged.type: feature request‘Nice-to-have’ improvement, new feature or different behavior or design.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions