fix: use offset+clipToBounds instead of graphicsLayer alpha for tab keep-alive

Hidden tabs with graphicsLayer{alpha=0f} still intercepted touch events.
Replaced with offset(x=2000.dp) + parent clipToBounds() so hidden composables
are off-screen and cannot capture touches meant for the visible tab.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-26 12:04:33 +08:00
parent 014437760d
commit 3c90adae6a
@@ -5,6 +5,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Chat import androidx.compose.material.icons.automirrored.filled.Chat
@@ -21,7 +22,8 @@ import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
@@ -145,28 +147,29 @@ fun MainScreen(
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.fillMaxHeight() .fillMaxHeight()
.clipToBounds()
.background(MaterialTheme.colorScheme.background), .background(MaterialTheme.colorScheme.background),
) { ) {
// Keep all tabs composed to avoid destroying ChatScreen on tab switch. // Keep all tabs alive by offsetting hidden ones off-screen.
// Hidden tabs use graphicsLayer { alpha = 0f } — invisible but alive. // clipToBounds ensures they don't intercept touches outside the visible area.
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.graphicsLayer { alpha = if (selectedTab == 0) 1f else 0f }, .offset(x = if (selectedTab == 0) 0.dp else 2000.dp),
) { ) {
ChatScreen() ChatScreen()
} }
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.graphicsLayer { alpha = if (selectedTab == 1) 1f else 0f }, .offset(x = if (selectedTab == 1) 0.dp else 2000.dp),
) { ) {
IoTScreen() IoTScreen()
} }
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.graphicsLayer { alpha = if (selectedTab == 2) 1f else 0f }, .offset(x = if (selectedTab == 2) 0.dp else 2000.dp),
) { ) {
ProfileScreen( ProfileScreen(
onNavigateToSettings = { navController.navigate(Routes.SETTINGS) }, onNavigateToSettings = { navController.navigate(Routes.SETTINGS) },