From 3e3d99f1f253ced4c811855214e1ef3c5baf5cd2 Mon Sep 17 00:00:00 2001 From: Barnabas Balogh Date: Wed, 29 Apr 2026 18:04:26 +0200 Subject: [PATCH] feat: added HomeSearchFullScreen as a starting point for search screen --- .../components/search/HomeSearchFullScreen.kt | 407 ++++++++++++++++++ 1 file changed, 407 insertions(+) create mode 100644 app/src/main/java/hu/bbara/purefin/ui/screen/home/components/search/HomeSearchFullScreen.kt diff --git a/app/src/main/java/hu/bbara/purefin/ui/screen/home/components/search/HomeSearchFullScreen.kt b/app/src/main/java/hu/bbara/purefin/ui/screen/home/components/search/HomeSearchFullScreen.kt new file mode 100644 index 00000000..c0a8cc65 --- /dev/null +++ b/app/src/main/java/hu/bbara/purefin/ui/screen/home/components/search/HomeSearchFullScreen.kt @@ -0,0 +1,407 @@ +package hu.bbara.purefin.ui.screen.home.components.search + +import androidx.compose.foundation.BorderStroke +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.LocalFireDepartment +import androidx.compose.material.icons.outlined.Menu +import androidx.compose.material.icons.outlined.Movie +import androidx.compose.material.icons.outlined.Person +import androidx.compose.material.icons.outlined.Search +import androidx.compose.material.icons.outlined.TrendingUp +import androidx.compose.material.icons.outlined.Tune +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.text.font.FontStyle +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import hu.bbara.purefin.ui.common.image.PurefinAsyncImage +import hu.bbara.purefin.ui.common.image.PurefinLogo +import hu.bbara.purefin.ui.theme.AppTheme + +@OptIn(ExperimentalLayoutApi::class) +@Composable +fun HomeSearchFullScreen( + modifier: Modifier = Modifier +) { + val scheme = MaterialTheme.colorScheme + + Column( + modifier = modifier + .fillMaxSize() + .background(scheme.background) + .statusBarsPadding() + .verticalScroll(rememberScrollState()) + .padding(horizontal = 20.dp) + .padding(bottom = 28.dp) + ) { + SearchHeader(modifier = Modifier.padding(top = 12.dp)) + Spacer(modifier = Modifier.height(24.dp)) + SearchField() + Spacer(modifier = Modifier.height(30.dp)) + SectionTitle( + text = "Trending Searches", + icon = { + Icon( + imageVector = Icons.Outlined.LocalFireDepartment, + contentDescription = null, + tint = scheme.primary, + modifier = Modifier.size(26.dp) + ) + } + ) + Spacer(modifier = Modifier.height(16.dp)) + FlowRow( + horizontalArrangement = Arrangement.spacedBy(10.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + previewTrendingSearches.forEach { title -> + TrendingSearchChip(title = title) + } + } + Spacer(modifier = Modifier.height(34.dp)) + SectionTitle(text = "Browse Categories") + Spacer(modifier = Modifier.height(18.dp)) + CategoryGrid(categories = previewCategories) + Spacer(modifier = Modifier.height(24.dp)) + OutlinedButton( + onClick = {}, + shape = RoundedCornerShape(28.dp), + border = BorderStroke(1.dp, scheme.primary.copy(alpha = 0.52f)), + colors = ButtonDefaults.outlinedButtonColors(contentColor = scheme.primary), + modifier = Modifier.align(Alignment.CenterHorizontally) + ) { + Text( + text = "View All Genres", + style = MaterialTheme.typography.labelLarge, + fontWeight = FontWeight.SemiBold, + modifier = Modifier.padding(horizontal = 20.dp, vertical = 4.dp) + ) + } + } +} + +@Composable +private fun SearchHeader( + modifier: Modifier = Modifier +) { + val scheme = MaterialTheme.colorScheme + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = modifier.fillMaxWidth() + ) { + IconButton(onClick = {}) { + Icon( + imageVector = Icons.Outlined.Menu, + contentDescription = "Menu", + tint = scheme.primary, + modifier = Modifier.size(30.dp) + ) + } + Spacer(modifier = Modifier.weight(1f)) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(10.dp) + ) { + PurefinLogo( + contentDescription = "PureFin", + contentScale = ContentScale.Fit, + modifier = Modifier.size(38.dp) + ) + Text( + text = "PureFin", + color = scheme.primary, + fontSize = 30.sp, + fontWeight = FontWeight.Bold, + fontStyle = FontStyle.Italic + ) + } + Spacer(modifier = Modifier.weight(1f)) + Surface( + shape = CircleShape, + color = scheme.surfaceContainer, + border = BorderStroke(1.dp, scheme.outlineVariant.copy(alpha = 0.28f)), + modifier = Modifier.size(48.dp) + ) { + Box(contentAlignment = Alignment.Center) { + Icon( + imageVector = Icons.Outlined.Person, + contentDescription = "Profile", + tint = scheme.onSurfaceVariant, + modifier = Modifier.size(26.dp) + ) + } + } + } +} + +@Composable +private fun SearchField( + modifier: Modifier = Modifier +) { + val scheme = MaterialTheme.colorScheme + + Surface( + shape = RoundedCornerShape(24.dp), + color = scheme.surfaceContainer, + border = BorderStroke(1.dp, scheme.outlineVariant.copy(alpha = 0.16f)), + modifier = modifier.fillMaxWidth() + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .height(68.dp) + .padding(horizontal = 20.dp) + ) { + Icon( + imageVector = Icons.Outlined.Search, + contentDescription = null, + tint = scheme.onSurfaceVariant, + modifier = Modifier.size(30.dp) + ) + Spacer(modifier = Modifier.width(16.dp)) + Text( + text = "Search movies, shows, genres...", + style = MaterialTheme.typography.titleMedium, + color = scheme.onSurfaceVariant, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier.weight(1f) + ) + Spacer(modifier = Modifier.width(16.dp)) + Box( + modifier = Modifier + .height(34.dp) + .width(1.dp) + .background(scheme.outlineVariant.copy(alpha = 0.18f)) + ) + Spacer(modifier = Modifier.width(14.dp)) + Icon( + imageVector = Icons.Outlined.Tune, + contentDescription = "Filter", + tint = scheme.primary, + modifier = Modifier.size(30.dp) + ) + } + } +} + +@Composable +private fun SectionTitle( + text: String, + modifier: Modifier = Modifier, + icon: @Composable (() -> Unit)? = null +) { + val scheme = MaterialTheme.colorScheme + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp), + modifier = modifier.fillMaxWidth() + ) { + icon?.invoke() + Text( + text = text, + style = MaterialTheme.typography.headlineSmall, + color = scheme.onBackground, + fontWeight = FontWeight.Bold + ) + } +} + +@Composable +private fun TrendingSearchChip( + title: String, + modifier: Modifier = Modifier +) { + val scheme = MaterialTheme.colorScheme + + Surface( + shape = RoundedCornerShape(24.dp), + color = scheme.surfaceContainer, + border = BorderStroke(1.dp, scheme.outlineVariant.copy(alpha = 0.14f)), + modifier = modifier + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp), + modifier = Modifier.padding(horizontal = 18.dp, vertical = 11.dp) + ) { + Icon( + imageVector = Icons.Outlined.TrendingUp, + contentDescription = null, + tint = scheme.onSurfaceVariant, + modifier = Modifier.size(18.dp) + ) + Text( + text = title, + style = MaterialTheme.typography.labelLarge, + color = scheme.onSurface, + fontWeight = FontWeight.SemiBold + ) + } + } +} + +@Composable +private fun CategoryGrid( + categories: List, + modifier: Modifier = Modifier +) { + Column( + verticalArrangement = Arrangement.spacedBy(16.dp), + modifier = modifier.fillMaxWidth() + ) { + categories.chunked(2).forEach { rowItems -> + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier.fillMaxWidth() + ) { + rowItems.forEach { category -> + CategoryCard( + category = category, + modifier = Modifier.weight(1f) + ) + } + if (rowItems.size == 1) { + Spacer(modifier = Modifier.weight(1f)) + } + } + } + } +} + +@Composable +private fun CategoryCard( + category: SearchCategory, + modifier: Modifier = Modifier +) { + val scheme = MaterialTheme.colorScheme + val shape = RoundedCornerShape(18.dp) + + Surface( + shape = shape, + color = scheme.surfaceContainer, + modifier = modifier + .clip(shape) + .aspectRatio(1.45f) + ) { + Box(modifier = Modifier.fillMaxSize()) { + PurefinAsyncImage( + model = category.imageUrl, + contentDescription = category.name, + fallbackIcon = Icons.Outlined.Movie, + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize() + ) + Box( + modifier = Modifier + .matchParentSize() + .background( + Brush.verticalGradient( + colors = listOf( + Color.Black.copy(alpha = 0.18f), + Color.Black.copy(alpha = 0.42f), + Color.Black.copy(alpha = 0.76f) + ) + ) + ) + ) + Text( + text = category.name, + style = MaterialTheme.typography.titleLarge, + color = Color.White, + fontWeight = FontWeight.Bold, + maxLines = 1, + overflow = TextOverflow.Ellipsis, + modifier = Modifier + .align(Alignment.BottomStart) + .padding(16.dp) + ) + } + } +} + +@Preview(showBackground = true) +@Composable +private fun HomeSearchFullScreenPreview() { + AppTheme { + HomeSearchFullScreen(modifier = Modifier.fillMaxSize()) + } +} + +private data class SearchCategory( + val name: String, + val imageUrl: String +) + +private val previewTrendingSearches = listOf( + "Dune", + "Severance", + "The Last of Us", + "Blade Runner", + "Foundation" +) + +private val previewCategories = listOf( + SearchCategory( + name = "Action", + imageUrl = "https://images.unsplash.com/photo-1535016120720-40c646be5580" + ), + SearchCategory( + name = "Adventure", + imageUrl = "https://images.unsplash.com/photo-1500530855697-b586d89ba3ee" + ), + SearchCategory( + name = "Comedy", + imageUrl = "https://images.unsplash.com/photo-1527224857830-43a7acc85260" + ), + SearchCategory( + name = "Fantasy", + imageUrl = "https://images.unsplash.com/photo-1518709268805-4e9042af2176" + ), + SearchCategory( + name = "Romance", + imageUrl = "https://images.unsplash.com/photo-1516589178581-6cd7833ae3b2" + ), + SearchCategory( + name = "Sci-Fi", + imageUrl = "https://images.unsplash.com/photo-1446776811953-b23d57bd21aa" + ) +)