
Pairel
Your Matches Row
package com.composables.pairel.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
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.width
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import com.composables.compose.ripple.rememberRippleIndication
import com.composables.pairel.Person
import com.composables.pairel.theme.Theme
import com.composables.uripainter.rememberUriPainter
import kotlin.time.Duration.Companion.milliseconds
@Composable
fun YourMatchesRow(
people: List<Person>,
onPersonClick: (Person) -> Unit,
modifier: Modifier = Modifier,
) {
LazyRow(
modifier = modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(20.dp),
contentPadding = PaddingValues(horizontal = 20.dp),
) {
items(people, key = { it.name }) { person ->
MatchTile(person, onClick = { onPersonClick(person) })
}
}
}
@Composable
private fun MatchTile(
person: Person,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
Column(
modifier =
modifier
.width(100.dp)
.clip(Theme.shapes.small)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRippleIndication(color = Theme.colors.onBackground),
onClickLabel = "View ${person.name}'s profile",
onClick = onClick,
)
.padding(vertical = 8.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Avatar(
initials = person.name.take(1),
contentDescription = "${person.name}'s photo",
painter =
rememberUriPainter(
person.photoUrl,
crossfade = 300.milliseconds,
contentScale = ContentScale.Crop,
),
modifier = Modifier.size(76.dp),
)
Spacer(Modifier.height(8.dp))
Text(person.name, style = Theme.typography.label, maxLines = 1)
}
}
package com.composables.pairel
data class Interest(val emoji: String, val label: String)
internal val interestGroups =
listOf(
"Outdoors" to
listOf(
Interest("๐ฅพ", "Hiking"),
Interest("๐", "Running"),
Interest("๐ฒ", "Cycling"),
Interest("๐ง", "Climbing"),
Interest("โบ", "Camping"),
Interest("๐ฑ", "Gardening"),
),
"Food and drink" to
listOf(
Interest("๐ต", "Tea"),
Interest("โ", "Coffee"),
Interest("๐ณ", "Cooking"),
Interest("๐ง", "Baking"),
Interest("๐ท", "Wine"),
Interest("๐ฅ", "Brunch"),
),
"Culture" to
listOf(
Interest("๐", "Bookshops"),
Interest("๐จ", "Museums"),
Interest("๐ถ", "Live music"),
Interest("๐ฌ", "Film"),
Interest("๐ญ", "Theatre"),
Interest("๐ท", "Photography"),
),
"At home" to
listOf(
Interest("๐ถ", "Dogs"),
Interest("๐ฑ", "Cats"),
Interest("๐ฒ", "Board games"),
Interest("๐ง", "Podcasts"),
Interest("๐งถ", "Crafts"),
Interest("๐ง", "Yoga"),
),
)
internal fun interests(vararg labels: String): List<Interest> {
val all = interestGroups.flatMap { it.second }
return labels.map { label -> all.first { it.label == label } }
}
package com.composables.pairel
internal const val YourPhotoUrl =
"https://images.unsplash.com/photo-1492562080023-ab3db95bfbce?fit=crop&w=700&h=900&q=85"
data class Person(
val name: String,
val age: Int,
val photoUrl: String,
val bio: String,
val interests: List<Interest>,
)
internal val people =
listOf(
Person(
"Nina",
29,
"https://images.unsplash.com/photo-1487412720507-e7ab37603c6f?fit=crop&w=900&h=1350&q=85",
"Here for real conversation, little adventures, and excellent noodles.",
interests("Climbing", "Board games", "Cooking"),
),
Person(
"Chloe",
30,
"https://images.unsplash.com/photo-1567532939604-b6b5b0db2604?fit=crop&w=900&h=1350&q=85",
"A curious cook who likes slow mornings and last-minute plans.",
interests("Cooking", "Brunch", "Coffee"),
),
Person(
"Sofia",
31,
"https://images.unsplash.com/photo-1772989665252-7cb41ae759cc?fit=crop&w=900&h=1350&q=85",
"Usually looking for a new place to walk, a good bookshop, or both.",
interests("Bookshops", "Cooking", "Coffee"),
),
Person(
"Ivy",
27,
"https://images.unsplash.com/photo-1489424731084-a5d8b219a5bb?fit=crop&w=900&h=1350&q=85",
"Big on live music, kind people, and getting outside.",
interests("Live music", "Hiking", "Cycling"),
),
Person(
"Amara",
33,
"https://images.unsplash.com/photo-1531123897727-8f129e1688ce?fit=crop&w=900&h=1350&q=85",
"Designer by day, enthusiastic home cook by night.",
interests("Cooking", "Photography", "Museums"),
),
)
package com.composables.pairel.components
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import com.composables.pairel.theme.Theme
import com.composeunstyled.UnstyledAvatar
@Composable
fun Avatar(
initials: String,
contentDescription: String,
modifier: Modifier = Modifier,
painter: Painter? = null,
) {
UnstyledAvatar(
painter = painter,
contentDescription = contentDescription,
contentScale = ContentScale.Crop,
modifier =
modifier
.size(48.dp)
.clip(CircleShape)
.background(Theme.colors.surfaceVariant)
.border(1.dp, Theme.colors.border, CircleShape),
underlay = {
Text(initials, color = Theme.colors.onSurfaceVariant, style = Theme.typography.label)
},
)
}
package com.composables.pairel.components
import androidx.compose.foundation.text.BasicText
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.isSpecified
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextOverflow
import com.composables.pairel.theme.LocalContentColor
import com.composables.pairel.theme.LocalTextStyle
@Composable
fun Text(
text: String,
modifier: Modifier = Modifier,
color: Color = Color.Unspecified,
style: TextStyle = LocalTextStyle.current,
overflow: TextOverflow = TextOverflow.Clip,
softWrap: Boolean = true,
maxLines: Int = Int.MAX_VALUE,
) {
val resolvedColor =
when {
color.isSpecified -> color
style.color.isSpecified -> style.color
else -> LocalContentColor.current
}
BasicText(
text = text,
modifier = modifier,
style = LocalTextStyle.current.merge(style).copy(color = resolvedColor),
overflow = overflow,
softWrap = softWrap,
maxLines = maxLines,
)
}
package com.composables.pairel.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.selection.LocalTextSelectionColors
import androidx.compose.foundation.text.selection.TextSelectionColors
import androidx.compose.runtime.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.staticCompositionLocalOf
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.*
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.*
import androidx.compose.ui.unit.*
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.composables.googlefonts.rememberGoogleFontFamily
@Immutable
data class ThemeColors(
val `background`: Color = Color(0xFFF8F5EF),
val `onBackground`: Color = Color(0xFF173D3A),
val `surface`: Color = Color(0xFFFFFFFF),
val `onSurface`: Color = Color(0xFF173D3A),
val `surfaceVariant`: Color = Color(0xFFECE8DD),
val `onSurfaceVariant`: Color = Color(0xFF173D3A),
val `primary`: Color = Color(0xFFF58C76),
val `onPrimary`: Color = Color(0xFF173D3A),
val `accent`: Color = Color(0xFF24675D),
val `onAccent`: Color = Color(0xFFF8F5EF),
val `textSelection`: Color = Color(0x6624675D),
val `onBackgroundMuted`: Color = Color(0xFF5A6B67),
val `onSurfaceInactive`: Color = Color(0xFF5A6B67),
val `border`: Color = Color(0xFFDCDDD4),
val `destructive`: Color = Color(0xFFB42318),
)
@Immutable
data class ThemeTypography(
val `display`: TextStyle =
TextStyle(
fontFamily = FontFamily.SansSerif,
fontSize = 46.sp,
lineHeight = 52.sp,
fontWeight = FontWeight(700),
),
val `headline`: TextStyle =
TextStyle(
fontFamily = FontFamily.SansSerif,
fontSize = 32.sp,
lineHeight = 38.sp,
fontWeight = FontWeight(700),
),
val `title`: TextStyle =
TextStyle(
fontFamily = FontFamily.SansSerif,
fontSize = 22.sp,
lineHeight = 28.sp,
fontWeight = FontWeight(700),
),
val `quote`: TextStyle =
TextStyle(
fontFamily = FontFamily.SansSerif,
fontSize = 23.sp,
lineHeight = 31.sp,
fontWeight = FontWeight(400),
),
val `body`: TextStyle =
TextStyle(
fontFamily = FontFamily.SansSerif,
fontSize = 16.sp,
lineHeight = 24.sp,
fontWeight = FontWeight(400),
),
val `label`: TextStyle =
TextStyle(
fontFamily = FontFamily.SansSerif,
fontSize = 16.sp,
lineHeight = 20.sp,
fontWeight = FontWeight(700),
),
val `caption`: TextStyle =
TextStyle(
fontFamily = FontFamily.SansSerif,
fontSize = 12.sp,
lineHeight = 16.sp,
fontWeight = FontWeight(700),
),
)
@Immutable
data class ThemeShapes(
val `small`: Shape = RoundedCornerShape(12.dp),
val `large`: Shape = RoundedCornerShape(24.dp),
val `fullyRounded`: Shape = RoundedCornerShape(50),
)
fun themeColors(dark: Boolean = false): ThemeColors = ThemeColors()
@Composable
fun resolveThemeTypography(typography: ThemeTypography = ThemeTypography()): ThemeTypography {
val font0 = rememberGoogleFontFamily("Fraunces", weights = intArrayOf(400, 700))
val font1 = rememberGoogleFontFamily("DM Sans", weights = intArrayOf(400, 700))
return typography.copy(
`display` = typography.`display`.copy(fontFamily = font0),
`headline` = typography.`headline`.copy(fontFamily = font1),
`title` = typography.`title`.copy(fontFamily = font1),
`quote` = typography.`quote`.copy(fontFamily = font0),
`body` = typography.`body`.copy(fontFamily = font1),
`label` = typography.`label`.copy(fontFamily = font1),
`caption` = typography.`caption`.copy(fontFamily = font1),
)
}
private val LocalColors = staticCompositionLocalOf<ThemeColors> { error("Theme is missing") }
private val LocalTypography =
staticCompositionLocalOf<ThemeTypography> { error("Theme is missing") }
private val LocalSpace = staticCompositionLocalOf { 4.dp }
private val LocalShapes = staticCompositionLocalOf<ThemeShapes> { error("Theme is missing") }
val LocalContentColor = compositionLocalOf<Color> { error("Theme is missing") }
val LocalTextStyle = compositionLocalOf<TextStyle> { error("Theme is missing") }
object Theme {
val colors: ThemeColors
@Composable get() = LocalColors.current
val typography: ThemeTypography
@Composable get() = LocalTypography.current
val shapes: ThemeShapes
@Composable get() = LocalShapes.current
}
val Space: Dp
@Composable get() = LocalSpace.current
@Composable
fun PairelTheme(
colors: ThemeColors = themeColors(isSystemInDarkTheme()),
typography: ThemeTypography = resolveThemeTypography(),
space: Dp = 4.dp,
shapes: ThemeShapes = ThemeShapes(),
contentColor: Color = colors.onBackground,
textStyle: TextStyle = typography.body,
content: @Composable () -> Unit,
) {
CompositionLocalProvider(
LocalColors provides colors,
LocalTypography provides typography,
LocalSpace provides space,
LocalShapes provides shapes,
LocalContentColor provides contentColor,
LocalTextStyle provides textStyle,
LocalTextSelectionColors provides
TextSelectionColors(
handleColor = colors.accent,
backgroundColor = colors.textSelection,
),
content = content,
)
}
@Composable
fun ProvideContentColor(color: Color, content: @Composable () -> Unit) {
CompositionLocalProvider(LocalContentColor provides color, content = content)
}
@Composable
fun ProvideTextStyle(style: TextStyle, content: @Composable () -> Unit) {
CompositionLocalProvider(
LocalTextStyle provides LocalTextStyle.current.merge(style),
content = content,
)
}


























