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"

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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions