android: fix main tabs bar layout and content overlap
Some checks failed
Android CI / android (push) Failing after 5m15s
Android Release / release (push) Failing after 5m18s
CI / test (push) Failing after 2m26s

This commit is contained in:
Codex
2026-03-09 20:29:05 +03:00
parent d29ad4cfb7
commit 3af90ec257
6 changed files with 41 additions and 59 deletions

View File

@@ -482,3 +482,8 @@
- Implemented scroll-direction behavior for all 4 main pages: - Implemented scroll-direction behavior for all 4 main pages:
- hide panel on downward scroll, - hide panel on downward scroll,
- show panel on upward scroll / at top. - show panel on upward scroll / at top.
### Step 77 - Main tabs bar UX/layout fix
- Replaced custom pill-row main bar with compact `NavigationBar` inside rounded container for stable 4-tab layout on small screens.
- Added bottom content paddings for `Chats/Contacts/Settings/Profile` pages so content is not obscured by the floating main bar.
- Raised chats management FAB offset to avoid overlap with the global bottom bar.

View File

@@ -169,7 +169,8 @@ fun ChatListScreen(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.then(if (isTabletLayout) Modifier.widthIn(max = 820.dp) else Modifier), .then(if (isTabletLayout) Modifier.widthIn(max = 820.dp) else Modifier)
.padding(bottom = 92.dp),
) { ) {
TabRow( TabRow(
selectedTabIndex = if (state.selectedTab == ChatTab.ALL) 0 else 1, selectedTabIndex = if (state.selectedTab == ChatTab.ALL) 0 else 1,
@@ -318,7 +319,7 @@ fun ChatListScreen(
onClick = { managementExpanded = !managementExpanded }, onClick = { managementExpanded = !managementExpanded },
modifier = Modifier modifier = Modifier
.align(Alignment.BottomEnd) .align(Alignment.BottomEnd)
.padding(end = 16.dp, bottom = 88.dp), .padding(end = 16.dp, bottom = 112.dp),
) { ) {
Icon( Icon(
imageVector = if (managementExpanded) Icons.Filled.Close else Icons.Filled.Add, imageVector = if (managementExpanded) Icons.Filled.Close else Icons.Filled.Add,

View File

@@ -84,7 +84,7 @@ private fun ContactsScreen(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.then(if (isTabletLayout) Modifier.widthIn(max = 820.dp) else Modifier) .then(if (isTabletLayout) Modifier.widthIn(max = 820.dp) else Modifier)
.padding(16.dp), .padding(start = 16.dp, top = 16.dp, end = 16.dp, bottom = 96.dp),
verticalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp),
) { ) {
Text( Text(

View File

@@ -8,9 +8,9 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.safeDrawing import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -24,8 +24,9 @@ import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
@@ -42,8 +43,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.contentDescription import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavType import androidx.navigation.NavType
@@ -295,71 +295,47 @@ private fun MainBottomBar(
onNavigate: (String) -> Unit, onNavigate: (String) -> Unit,
) { ) {
Surface( Surface(
modifier = Modifier
.padding(horizontal = 12.dp)
.fillMaxWidth(),
shape = CircleShape, shape = CircleShape,
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.92f), color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.92f),
) { ) {
androidx.compose.foundation.layout.Row( NavigationBar(
modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp), containerColor = androidx.compose.ui.graphics.Color.Transparent,
horizontalArrangement = Arrangement.spacedBy(8.dp), tonalElevation = 0.dp,
modifier = Modifier.padding(horizontal = 8.dp),
) { ) {
MainTabPill( NavigationBarItem(
label = "Chats",
selected = currentRoute == Routes.Chats, selected = currentRoute == Routes.Chats,
icon = { Icon(Icons.AutoMirrored.Filled.Chat, contentDescription = null) },
onClick = { onNavigate(Routes.Chats) }, onClick = { onNavigate(Routes.Chats) },
) icon = { Icon(Icons.AutoMirrored.Filled.Chat, contentDescription = "Chats") },
MainTabPill( label = {
label = "Contacts", androidx.compose.material3.Text("Chats", maxLines = 1, overflow = TextOverflow.Ellipsis)
selected = currentRoute == Routes.Contacts,
icon = { Icon(Icons.Filled.Contacts, contentDescription = null) },
onClick = { onNavigate(Routes.Contacts) },
)
MainTabPill(
label = "Settings",
selected = currentRoute == Routes.Settings,
icon = { Icon(Icons.Filled.Settings, contentDescription = null) },
onClick = { onNavigate(Routes.Settings) },
)
MainTabPill(
label = "Profile",
selected = currentRoute == Routes.Profile,
icon = { Icon(Icons.Filled.Person, contentDescription = null) },
onClick = { onNavigate(Routes.Profile) },
)
}
}
}
@Composable
private fun MainTabPill(
label: String,
selected: Boolean,
icon: @Composable () -> Unit,
onClick: () -> Unit,
) {
Surface(
shape = CircleShape,
color = if (selected) {
MaterialTheme.colorScheme.primaryContainer
} else {
MaterialTheme.colorScheme.surface
}, },
modifier = Modifier )
.clickable(onClick = onClick) NavigationBarItem(
.semantics { contentDescription = "$label tab" }, selected = currentRoute == Routes.Contacts,
) { onClick = { onNavigate(Routes.Contacts) },
androidx.compose.foundation.layout.Row( icon = { Icon(Icons.Filled.Contacts, contentDescription = "Contacts") },
modifier = Modifier.padding(horizontal = 10.dp, vertical = 6.dp), label = {
horizontalArrangement = Arrangement.spacedBy(6.dp), androidx.compose.material3.Text("Contacts", maxLines = 1, overflow = TextOverflow.Ellipsis)
verticalAlignment = Alignment.CenterVertically, },
) { )
icon() NavigationBarItem(
Text( selected = currentRoute == Routes.Settings,
text = label, onClick = { onNavigate(Routes.Settings) },
color = if (selected) { icon = { Icon(Icons.Filled.Settings, contentDescription = "Settings") },
MaterialTheme.colorScheme.onPrimaryContainer label = {
} else { androidx.compose.material3.Text("Settings", maxLines = 1, overflow = TextOverflow.Ellipsis)
MaterialTheme.colorScheme.onSurface },
)
NavigationBarItem(
selected = currentRoute == Routes.Profile,
onClick = { onNavigate(Routes.Profile) },
icon = { Icon(Icons.Filled.Person, contentDescription = "Profile") },
label = {
androidx.compose.material3.Text("Profile", maxLines = 1, overflow = TextOverflow.Ellipsis)
}, },
) )
} }

View File

@@ -125,7 +125,7 @@ fun ProfileScreen(
.fillMaxWidth() .fillMaxWidth()
.then(if (isTabletLayout) Modifier.widthIn(max = 720.dp) else Modifier) .then(if (isTabletLayout) Modifier.widthIn(max = 720.dp) else Modifier)
.verticalScroll(scrollState) .verticalScroll(scrollState)
.padding(16.dp), .padding(start = 16.dp, top = 16.dp, end = 16.dp, bottom = 96.dp),
verticalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp),
) { ) {
Text( Text(

View File

@@ -105,7 +105,7 @@ fun SettingsScreen(
.fillMaxWidth() .fillMaxWidth()
.then(if (isTabletLayout) Modifier.widthIn(max = 720.dp) else Modifier) .then(if (isTabletLayout) Modifier.widthIn(max = 720.dp) else Modifier)
.verticalScroll(scrollState) .verticalScroll(scrollState)
.padding(16.dp), .padding(start = 16.dp, top = 16.dp, end = 16.dp, bottom = 96.dp),
verticalArrangement = Arrangement.spacedBy(12.dp), verticalArrangement = Arrangement.spacedBy(12.dp),
) { ) {
Text( Text(