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, - unified selected/unselected item colors,
- stable 4-item navigation with icons + labels. - stable 4-item navigation with icons + labels.
- Kept scroll-hide/show behavior and page-level navigation unchanged. - 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.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Tab import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow import androidx.compose.material3.TabRow
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.pulltorefresh.PullToRefreshBox import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@@ -172,6 +174,17 @@ fun ChatListScreen(
.then(if (isTabletLayout) Modifier.widthIn(max = 820.dp) else Modifier) .then(if (isTabletLayout) Modifier.widthIn(max = 820.dp) else Modifier)
.padding(bottom = 92.dp), .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( TabRow(
selectedTabIndex = if (state.selectedTab == ChatTab.ALL) 0 else 1, 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) { if (managementExpanded) {
Surface( Surface(

View File

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

View File

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

View File

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

View File

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