v0.2.4: One UI 9 & Material 3 Expressive UI/UX Redesign

This commit is contained in:
2026-08-20 23:28:58 +00:00
parent b070d317c5
commit 7c311d7d3f
@@ -12,7 +12,6 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.* import androidx.compose.material.icons.outlined.*
@@ -22,22 +21,26 @@ import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
import androidx.compose.ui.draw.scale
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.hotdeal.alarm.domain.model.SiteType import com.hotdeal.alarm.domain.model.SiteType
import com.hotdeal.alarm.presentation.components.* import com.hotdeal.alarm.presentation.components.*
import com.hotdeal.alarm.presentation.main.MainUiState import com.hotdeal.alarm.presentation.main.MainUiState
import com.hotdeal.alarm.presentation.main.MainViewModel import com.hotdeal.alarm.presentation.main.MainViewModel
import com.hotdeal.alarm.ui.theme.getSiteColor import com.hotdeal.alarm.ui.theme.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
/**
* One UI 9 & Material 3 Expressive Deal Feed Screen
* 실시간 Now 브리핑, 일체형 검색 & Sticky Pill 필터링을 지원하는 메인 핫딜 피드
*/
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun DealListScreen( fun DealListScreen(
@@ -47,20 +50,18 @@ fun DealListScreen(
val uiState by viewModel.uiState.collectAsStateWithLifecycle() val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val context = LocalContext.current val context = LocalContext.current
// Pull to Refresh 상태 - 민감도 대폭 향상 // Pull to Refresh (초고감도 설정)
val pullToRefreshState = rememberPullToRefreshState( val pullToRefreshState = rememberPullToRefreshState(
positionalThreshold = 40.dp // 기본값의 1/3로 설정하여 매우 쉽게 새로고침 positionalThreshold = 40.dp
) )
var isRefreshing by remember { mutableStateOf(false) } var isRefreshing by remember { mutableStateOf(false) }
// List state for scroll detection
val listState = rememberLazyListState() val listState = rememberLazyListState()
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val showScrollToTop by remember { val showScrollToTop by remember {
derivedStateOf { listState.firstVisibleItemIndex > 3 } derivedStateOf { listState.firstVisibleItemIndex > 3 }
} }
// 새로고침 완료 감지
LaunchedEffect(uiState) { LaunchedEffect(uiState) {
if (uiState !is MainUiState.Loading) { if (uiState !is MainUiState.Loading) {
isRefreshing = false isRefreshing = false
@@ -68,79 +69,69 @@ fun DealListScreen(
} }
} }
// 필터 상태
var searchText by remember { mutableStateOf("") } var searchText by remember { mutableStateOf("") }
var selectedSiteFilter by remember { mutableStateOf<SiteType?>(null) } var selectedSiteFilter by remember { mutableStateOf<SiteType?>(null) }
var showFavoritesOnly by remember { mutableStateOf(false) } var showFavoritesOnly by remember { mutableStateOf(false) }
var showPopularOnly by remember { mutableStateOf(false) }
var showKeywordMatchOnly by remember { mutableStateOf(false) } var showKeywordMatchOnly by remember { mutableStateOf(false) }
var showFilterMenu by remember { mutableStateOf(false) }
// 필터 메뉴 자동 닫기 (5초)
LaunchedEffect(showFilterMenu) {
if (showFilterMenu) {
delay(5000L)
showFilterMenu = false
}
}
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( TopAppBar(
title = { title = {
Row(verticalAlignment = Alignment.CenterVertically) { Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp)
) {
// One UI 9 App Logo Icon
Box( Box(
modifier = Modifier modifier = Modifier
.size(36.dp) .size(36.dp)
.background(MaterialTheme.colorScheme.primary, CircleShape), .clip(CornerRadius.shapeSmall)
.background(MaterialTheme.colorScheme.primary),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Icon( Icon(
imageVector = Icons.Filled.Notifications, imageVector = Icons.Filled.LocalFireDepartment,
contentDescription = null, contentDescription = null,
tint = MaterialTheme.colorScheme.onPrimary, tint = MaterialTheme.colorScheme.onPrimary,
modifier = Modifier.size(20.dp) modifier = Modifier.size(20.dp)
) )
} }
Spacer(modifier = Modifier.width(12.dp))
Text( Column {
text = "핫딜 알람", Text(
style = MaterialTheme.typography.titleLarge, text = "핫딜 알람",
fontWeight = FontWeight.Bold style = MaterialTheme.typography.titleMedium,
) fontWeight = FontWeight.Bold,
} letterSpacing = (-0.3).sp
},
actions = {
BadgedBox(
badge = {
if (selectedSiteFilter != null || showFavoritesOnly || showKeywordMatchOnly) {
Badge(
containerColor = MaterialTheme.colorScheme.primary,
contentColor = MaterialTheme.colorScheme.onPrimary
)
}
}
) {
IconButton(onClick = { showFilterMenu = !showFilterMenu }) {
Icon(
imageVector = if (showFilterMenu) Icons.Filled.FilterList else Icons.Outlined.FilterList,
contentDescription = "필터"
) )
} }
} }
},
actions = {
// 실시간 상태 요약 캡슐
if (uiState is MainUiState.Success) {
val activeSitesCount = (uiState as MainUiState.Success).siteConfigs.count { it.isEnabled }
NowStatusPill(
text = if (activeSitesCount > 0) "${activeSitesCount}개 사이트 감시 중" else "모니터링 대기",
isLive = activeSitesCount > 0,
accentColor = MaterialTheme.colorScheme.primary
)
Spacer(modifier = Modifier.width(4.dp))
}
IconButton(onClick = { viewModel.refresh() }) { IconButton(onClick = { viewModel.refresh() }) {
Icon( Icon(
imageVector = Icons.Outlined.Refresh, imageVector = Icons.Outlined.Refresh,
contentDescription = "새로고침" contentDescription = "새로고침",
) tint = MaterialTheme.colorScheme.onSurfaceVariant
}
IconButton(onClick = { onNavigateToSettings() }) {
Icon(
imageVector = Icons.Outlined.Settings,
contentDescription = "설정"
) )
} }
}, },
colors = TopAppBarDefaults.topAppBarColors( colors = TopAppBarDefaults.topAppBarColors(
containerColor = MaterialTheme.colorScheme.surface, containerColor = MaterialTheme.colorScheme.background,
scrolledContainerColor = MaterialTheme.colorScheme.surface scrolledContainerColor = MaterialTheme.colorScheme.surface
), ),
windowInsets = WindowInsets(0, 0, 0, 0) windowInsets = WindowInsets(0, 0, 0, 0)
@@ -181,111 +172,30 @@ fun DealListScreen(
.padding(paddingValues) .padding(paddingValues)
.consumeWindowInsets(paddingValues) .consumeWindowInsets(paddingValues)
) { ) {
// 필터 메뉴 // ============================================
AnimatedVisibility( // 1. One UI 9 Seamless Search Field
visible = showFilterMenu, // ============================================
enter = expandVertically(animationSpec = spring(stiffness = Spring.StiffnessLow)) + fadeIn(),
exit = shrinkVertically(animationSpec = spring(stiffness = Spring.StiffnessLow)) + fadeOut()
) {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
shape = RoundedCornerShape(16.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f)
)
) {
Column(modifier = Modifier.padding(16.dp)) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
imageVector = Icons.Outlined.FilterAlt,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(20.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = "사이트 필터",
style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.Bold
)
}
Spacer(modifier = Modifier.height(12.dp))
Row(
modifier = Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState()),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
EnhancedFilterChip(
selected = selectedSiteFilter == null,
onClick = { selectedSiteFilter = null },
label = "전체",
color = MaterialTheme.colorScheme.primary
)
SiteType.entries.forEach { siteType ->
val color = getSiteColor(siteType)
EnhancedFilterChip(
selected = selectedSiteFilter == siteType,
onClick = {
selectedSiteFilter = if (selectedSiteFilter == siteType) null else siteType
},
label = siteType.displayName,
color = color
)
}
}
Spacer(modifier = Modifier.height(8.dp))
// 특수 필터
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
// 내 키워드 필터
EnhancedFilterChip(
selected = showKeywordMatchOnly,
onClick = { showKeywordMatchOnly = !showKeywordMatchOnly },
label = "내 키워드",
color = MaterialTheme.colorScheme.primary
)
// 즐겨찾기 필터
EnhancedFilterChip(
selected = showFavoritesOnly,
onClick = { showFavoritesOnly = !showFavoritesOnly },
label = "즐겨찾기",
color = MaterialTheme.colorScheme.error
)
}
}
}
}
// 검색창
OutlinedTextField( OutlinedTextField(
value = searchText, value = searchText,
onValueChange = { searchText = it }, onValueChange = { searchText = it },
placeholder = { placeholder = {
Text( Text(
text = "제목으로 검색...", text = "키워드, 브랜드, 상품명 검색...",
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.6f) style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.55f)
) )
}, },
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp), .padding(horizontal = 16.dp, vertical = 6.dp),
singleLine = true, singleLine = true,
shape = RoundedCornerShape(16.dp), shape = CornerRadius.shapeNormal,
leadingIcon = { leadingIcon = {
Icon( Icon(
imageVector = Icons.Outlined.Search, imageVector = Icons.Outlined.Search,
contentDescription = "검색", contentDescription = "검색",
tint = MaterialTheme.colorScheme.onSurfaceVariant tint = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.7f),
modifier = Modifier.size(20.dp)
) )
}, },
trailingIcon = { trailingIcon = {
@@ -294,31 +204,127 @@ fun DealListScreen(
Icon( Icon(
imageVector = Icons.Filled.Close, imageVector = Icons.Filled.Close,
contentDescription = "지우기", contentDescription = "지우기",
tint = MaterialTheme.colorScheme.onSurfaceVariant tint = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.size(18.dp)
) )
} }
} }
}, },
colors = OutlinedTextFieldDefaults.colors( colors = OutlinedTextFieldDefaults.colors(
focusedContainerColor = MaterialTheme.colorScheme.surface,
unfocusedContainerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.4f),
focusedBorderColor = MaterialTheme.colorScheme.primary, focusedBorderColor = MaterialTheme.colorScheme.primary,
unfocusedBorderColor = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f) unfocusedBorderColor = MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.35f)
) )
) )
// 딜 리스트 // ============================================
// 2. One UI 9 Sticky Horizontal Pill Filter Bar
// ============================================
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 6.dp)
.horizontalScroll(rememberScrollState()),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Spacer(modifier = Modifier.width(8.dp))
// "전체" 필터 칩
FilterPillChip(
selected = selectedSiteFilter == null && !showFavoritesOnly && !showPopularOnly && !showKeywordMatchOnly,
onClick = {
selectedSiteFilter = null
showFavoritesOnly = false
showPopularOnly = false
showKeywordMatchOnly = false
},
label = "전체",
accentColor = MaterialTheme.colorScheme.primary
)
// "🔥 HOT 인기" 필터 칩
FilterPillChip(
selected = showPopularOnly,
onClick = {
showPopularOnly = !showPopularOnly
if (showPopularOnly) {
showFavoritesOnly = false
showKeywordMatchOnly = false
}
},
label = "🔥 인기",
accentColor = HotDealFlameColor
)
// "⭐ 내 키워드" 필터 칩
FilterPillChip(
selected = showKeywordMatchOnly,
onClick = {
showKeywordMatchOnly = !showKeywordMatchOnly
if (showKeywordMatchOnly) {
showFavoritesOnly = false
showPopularOnly = false
}
},
label = "⭐ 내 키워드",
accentColor = KeywordGold
)
// "❤️ 즐겨찾기" 필터 칩
FilterPillChip(
selected = showFavoritesOnly,
onClick = {
showFavoritesOnly = !showFavoritesOnly
if (showFavoritesOnly) {
showPopularOnly = false
showKeywordMatchOnly = false
}
},
label = "❤️ 보관함",
accentColor = FavoriteColor
)
// 사이트별 개별 필터 칩들 (뽐뿌, 클리앙, 루리웹, 쿨엔조이, 아카라이브)
SiteType.entries.forEach { siteType ->
val siteColor = getSiteColor(siteType)
FilterPillChip(
selected = selectedSiteFilter == siteType,
onClick = {
selectedSiteFilter = if (selectedSiteFilter == siteType) null else siteType
},
label = siteType.displayName,
accentColor = siteColor
)
}
Spacer(modifier = Modifier.width(8.dp))
}
// ============================================
// 3. 핫딜 목록 본문 (List Content)
// ============================================
when (val state = uiState) { when (val state = uiState) {
is MainUiState.Loading -> { is MainUiState.Loading -> {
DealListSkeleton(count = 5) DealListSkeleton(count = 6)
} }
is MainUiState.Success -> { is MainUiState.Success -> {
val filteredDeals = remember(state.deals, searchText, selectedSiteFilter, showFavoritesOnly, showKeywordMatchOnly) { val filteredDeals = remember(
state.deals,
searchText,
selectedSiteFilter,
showFavoritesOnly,
showPopularOnly,
showKeywordMatchOnly
) {
state.deals.filter { deal -> state.deals.filter { deal ->
val matchesSearch = searchText.isBlank() || deal.title.contains(searchText, ignoreCase = true) val matchesSearch = searchText.isBlank() || deal.title.contains(searchText, ignoreCase = true)
val matchesSite = selectedSiteFilter == null || deal.siteType == selectedSiteFilter val matchesSite = selectedSiteFilter == null || deal.siteType == selectedSiteFilter
val matchesFavorite = !showFavoritesOnly || deal.isFavorite val matchesFavorite = !showFavoritesOnly || deal.isFavorite
val matchesPopular = !showPopularOnly || deal.isPopular
val matchesKeyword = !showKeywordMatchOnly || deal.isKeywordMatch val matchesKeyword = !showKeywordMatchOnly || deal.isKeywordMatch
matchesSearch && matchesSite && matchesFavorite && matchesKeyword matchesSearch && matchesSite && matchesFavorite && matchesPopular && matchesKeyword
} }
} }
@@ -326,90 +332,69 @@ fun DealListScreen(
if (state.deals.isEmpty()) { if (state.deals.isEmpty()) {
NoDealsState(onRefresh = { viewModel.refresh() }) NoDealsState(onRefresh = { viewModel.refresh() })
} else { } else {
val selectedFilter = selectedSiteFilter
val message = when { val message = when {
showKeywordMatchOnly -> "키워드 매칭된 핫딜이 없습니다" showKeywordMatchOnly -> "키워드 매칭된 핫딜이 없습니다"
showPopularOnly -> "인기 핫딜이 없습니다"
showFavoritesOnly -> "즐겨찾기한 핫딜이 없습니다" showFavoritesOnly -> "즐겨찾기한 핫딜이 없습니다"
selectedFilter != null -> "${selectedFilter.displayName}의 핫딜이 없습니다" selectedSiteFilter != null -> "${selectedSiteFilter?.displayName}의 핫딜이 없습니다"
searchText.isNotBlank() -> "'$searchText'에 대한 검색 결과가 없습니다" searchText.isNotBlank() -> "'$searchText'에 대한 검색 결과가 없습니다"
else -> "표시할 핫딜이 없습니다" else -> "표시할 핫딜이 없습니다"
} }
EmptyState( EmptyState(
title = "결과가 없습니다", title = "핫딜이 없습니다",
message = message, message = message,
icon = Icons.Outlined.Search icon = Icons.Outlined.Search
) )
} }
} else { } else {
Surface( // 리스트 상단 카운터 바
modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp), Row(
shape = RoundedCornerShape(12.dp), modifier = Modifier
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f) .fillMaxWidth()
.padding(horizontal = 18.dp, vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically
) { ) {
Row( Text(
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp), text = "${filteredDeals.size}개의 핫딜",
verticalAlignment = Alignment.CenterVertically style = MaterialTheme.typography.labelMedium,
) { fontWeight = FontWeight.SemiBold,
Icon( color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.8f)
imageVector = Icons.Outlined.Inventory2, )
contentDescription = null,
tint = MaterialTheme.colorScheme.primary, if (selectedSiteFilter != null) {
modifier = Modifier.size(16.dp)
)
Spacer(modifier = Modifier.width(6.dp)) Spacer(modifier = Modifier.width(6.dp))
Text( Text(
text = "${filteredDeals.size}개의 핫딜", text = "${selectedSiteFilter?.displayName}",
style = MaterialTheme.typography.labelLarge, style = MaterialTheme.typography.labelSmall,
fontWeight = FontWeight.Medium, color = getSiteColor(selectedSiteFilter)
color = MaterialTheme.colorScheme.onSurfaceVariant
) )
if (selectedSiteFilter != null) {
val filter = selectedSiteFilter!!
Spacer(modifier = Modifier.width(8.dp))
Text(
text = "${filter.displayName}",
style = MaterialTheme.typography.labelMedium,
color = getSiteColor(filter)
)
}
} }
} }
LazyColumn( LazyColumn(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
state = listState, state = listState,
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp), contentPadding = PaddingValues(horizontal = 16.dp, vertical = 6.dp),
verticalArrangement = Arrangement.spacedBy(12.dp) verticalArrangement = Arrangement.spacedBy(10.dp)
) { ) {
items( items(
items = filteredDeals, items = filteredDeals,
key = { it.id } key = { it.id }
) { deal -> ) { deal ->
AnimatedVisibility( DealItem(
visible = true, deal = deal,
enter = fadeIn(animationSpec = tween(300)) + onClick = {
slideInVertically( val intent = Intent(Intent.ACTION_VIEW, Uri.parse(deal.url))
animationSpec = tween(300), context.startActivity(intent)
initialOffsetY = { it / 8 } },
), onFavoriteToggle = { dealId ->
exit = fadeOut(animationSpec = tween(200)) viewModel.toggleFavorite(dealId)
) { }
DealItem( )
deal = deal,
onClick = {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(deal.url))
context.startActivity(intent)
},
onFavoriteToggle = { dealId ->
viewModel.toggleFavorite(dealId)
}
)
}
} }
// EdgeToEdge: 하단 네비게이션 바 공간 확보
item { item {
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(24.dp))
} }
} }
} }
@@ -424,7 +409,7 @@ fun DealListScreen(
} }
} }
// Pull to Refresh 인디케이터 - 절대 위치로 배치하여 간격 벌어짐 방지 // One UI 9 Pull to Refresh 인디케이터
val progress = pullToRefreshState.progress val progress = pullToRefreshState.progress
val showIndicator = pullToRefreshState.isRefreshing || progress > 0 val showIndicator = pullToRefreshState.isRefreshing || progress > 0
@@ -433,14 +418,13 @@ fun DealListScreen(
state = pullToRefreshState, state = pullToRefreshState,
modifier = Modifier modifier = Modifier
.align(Alignment.TopCenter) .align(Alignment.TopCenter)
.padding(top = 100.dp) .padding(top = 90.dp)
.zIndex(999f), .zIndex(999f),
containerColor = MaterialTheme.colorScheme.primaryContainer, containerColor = MaterialTheme.colorScheme.surfaceVariant,
contentColor = MaterialTheme.colorScheme.onPrimaryContainer contentColor = MaterialTheme.colorScheme.primary
) )
} }
// 새로고침 트리거
LaunchedEffect(pullToRefreshState.isRefreshing) { LaunchedEffect(pullToRefreshState.isRefreshing) {
if (pullToRefreshState.isRefreshing) { if (pullToRefreshState.isRefreshing) {
isRefreshing = true isRefreshing = true
@@ -451,46 +435,52 @@ fun DealListScreen(
} }
} }
/**
* One UI 9 Capsule Pill Filter Chip
*/
@Composable @Composable
private fun EnhancedFilterChip( private fun FilterPillChip(
selected: Boolean, selected: Boolean,
onClick: () -> Unit, onClick: () -> Unit,
label: String, label: String,
color: Color accentColor: Color
) { ) {
val scale by animateFloatAsState( val bgColor = if (selected) {
targetValue = if (selected) 1.05f else 1f, accentColor.copy(alpha = 0.16f)
animationSpec = spring(stiffness = Spring.StiffnessLow), } else {
label = "chip_scale" MaterialTheme.colorScheme.surface
) }
val textColor = if (selected) {
accentColor
} else {
MaterialTheme.colorScheme.onSurfaceVariant
}
val borderColor = if (selected) {
accentColor.copy(alpha = 0.6f)
} else {
MaterialTheme.colorScheme.outlineVariant.copy(alpha = 0.35f)
}
Surface( Surface(
onClick = onClick, shape = CornerRadius.shapePill,
color = bgColor,
border = androidx.compose.foundation.BorderStroke(0.8.dp, borderColor),
modifier = Modifier modifier = Modifier
.height(36.dp) .height(34.dp)
.scale(scale), .elasticPressClickable(onClick = onClick)
shape = RoundedCornerShape(18.dp),
color = if (selected) color else MaterialTheme.colorScheme.surface,
border = if (!selected) androidx.compose.foundation.BorderStroke(1.dp, color.copy(alpha = 0.3f)) else null
) { ) {
Row( Box(
modifier = Modifier.padding(horizontal = 16.dp), contentAlignment = Alignment.Center,
verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(horizontal = 14.dp, vertical = 6.dp)
horizontalArrangement = Arrangement.spacedBy(6.dp)
) { ) {
if (selected) {
Icon(
imageVector = Icons.Filled.Check,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(16.dp)
)
}
Text( Text(
text = label, text = label,
style = MaterialTheme.typography.labelLarge, style = MaterialTheme.typography.labelMedium.copy(
fontWeight = if (selected) FontWeight.Bold else FontWeight.Medium, fontWeight = if (selected) FontWeight.Bold else FontWeight.Medium
color = if (selected) Color.White else color ),
color = textColor
) )
} }
} }