android: add top app bars and safe-area pass for main pages
Some checks failed
Android CI / android (push) Has started running
Android Release / release (push) Has been cancelled
CI / test (push) Has started running

This commit is contained in:
Codex
2026-03-09 21:16:06 +03:00
parent ee52785b1b
commit fdd877b49a
6 changed files with 34 additions and 21 deletions

View File

@@ -494,3 +494,8 @@
- unified selected/unselected item colors,
- stable 4-item navigation with icons + labels.
- Kept scroll-hide/show behavior and page-level navigation unchanged.
### Step 79 - Main pages app bars + safe-area pass
- Added top app bars for all 4 main pages (`Chats`, `Contacts`, `Settings`, `Profile`) to make them feel like proper standalone sections.
- Moved chats management toggle action into chats app bar.
- Kept safe-area handling and bottom insets consistent with shared floating tabs bar to avoid overlap.

View File

@@ -26,12 +26,14 @@ import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -172,6 +174,17 @@ fun ChatListScreen(
.then(if (isTabletLayout) Modifier.widthIn(max = 820.dp) else Modifier)
.padding(bottom = 92.dp),
) {
TopAppBar(
title = { Text("Chats") },
actions = {
IconButton(onClick = { managementExpanded = !managementExpanded }) {
Icon(
imageVector = if (managementExpanded) Icons.Filled.Close else Icons.Filled.Add,
contentDescription = if (managementExpanded) "Close management" else "Open management",
)
}
},
)
TabRow(
selectedTabIndex = if (state.selectedTab == ChatTab.ALL) 0 else 1,
) {
@@ -315,17 +328,6 @@ fun ChatListScreen(
}
}
}
Button(
onClick = { managementExpanded = !managementExpanded },
modifier = Modifier
.align(Alignment.BottomEnd)
.padding(end = 16.dp, bottom = 112.dp),
) {
Icon(
imageVector = if (managementExpanded) Icons.Filled.Close else Icons.Filled.Add,
contentDescription = if (managementExpanded) "Close management" else "Open management",
)
}
}
if (managementExpanded) {
Surface(

View File

@@ -16,6 +16,8 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
@@ -38,6 +40,7 @@ fun ContactsRoute(
}
@Composable
@OptIn(ExperimentalMaterial3Api::class)
private fun ContactsScreen(
onMainBarVisibilityChanged: (Boolean) -> Unit,
) {
@@ -87,9 +90,8 @@ private fun ContactsScreen(
.padding(start = 16.dp, top = 16.dp, end = 16.dp, bottom = 96.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Text(
text = "Contacts",
style = MaterialTheme.typography.headlineSmall,
TopAppBar(
title = { Text("Contacts") },
)
OutlinedTextField(
value = query,

View File

@@ -28,10 +28,12 @@ import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -67,6 +69,7 @@ fun ProfileRoute(
}
@Composable
@OptIn(ExperimentalMaterial3Api::class)
fun ProfileScreen(
onBackToChats: () -> Unit,
onOpenSettings: () -> Unit,
@@ -128,9 +131,8 @@ fun ProfileScreen(
.padding(start = 16.dp, top = 16.dp, end = 16.dp, bottom = 96.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Text(
text = "Profile",
style = MaterialTheme.typography.headlineSmall,
TopAppBar(
title = { Text("Profile") },
)
if (!avatarUrl.isBlank()) {
Box(

View File

@@ -16,10 +16,12 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -56,6 +58,7 @@ fun SettingsRoute(
}
@Composable
@OptIn(ExperimentalMaterial3Api::class)
fun SettingsScreen(
onBackToChats: () -> Unit,
onOpenProfile: () -> Unit,
@@ -108,9 +111,8 @@ fun SettingsScreen(
.padding(start = 16.dp, top = 16.dp, end = 16.dp, bottom = 96.dp),
verticalArrangement = Arrangement.spacedBy(12.dp),
) {
Text(
text = "Settings",
style = MaterialTheme.typography.headlineSmall,
TopAppBar(
title = { Text("Settings") },
)
Text("Appearance", style = MaterialTheme.typography.titleMedium)
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {

View File

@@ -8,7 +8,7 @@
- [x] Активный tab в фиолетовом pill-состоянии, неактивные — белые/серые.
- [x] Глобальная панель `Chats / Contacts / Settings / Profile` на уровне app-shell (одна на все 4 страницы).
- [x] Поведение панели: скрывается только при скролле вниз, возвращается при скролле вверх/в начале списка.
- [ ] Safe area для status/nav bars на всех экранах списка/настроек/профиля.
- [x] Safe area для status/nav bars на всех экранах списка/настроек/профиля.
## P0 — Settings Visual System
- [ ] Экран настроек из секционных rounded cards (а не плоский список).