High Quality UI Blocks built with Kotlin & Compose
Ready-to-use components for Jetpack Compose and Compose Multiplatform. Copy paste into your apps.
Sign In Screen
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.widthIn
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.OutlinedTextFieldDefaults
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
@Composable
fun ScreenSignIn() {
var phoneNumber by remember { mutableStateOf("5554311394") }
val countryCode by remember { mutableStateOf("+44") }
val onSubmit: () -> Unit = {
/*TODO*/
}
val focus = LocalFocusManager.current
val keyboardController = LocalSoftwareKeyboardController.current
Scaffold { padding ->
Box(modifier = Modifier.fillMaxSize().padding(padding), contentAlignment = Alignment.TopCenter) {
Column(
modifier = Modifier
.widthIn(max = 400.dp)
.fillMaxSize()
.padding(horizontal = 16.dp)
.padding(vertical = 32.dp),
) {
Text("What's your number?", style = MaterialTheme.typography.headlineLarge)
Spacer(Modifier.height(12.dp))
Text("We verify the phone number of all our members", style = MaterialTheme.typography.bodyMedium)
Spacer(Modifier.height(48.dp))
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
OutlinedButton(
onClick = { /*TODO*/ },
shape = OutlinedTextFieldDefaults.shape,
colors = ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.onSurface),
modifier = Modifier
// top spacing to match OutlinedTextField's
.padding(top = 8.dp)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(vertical = 8.dp)
) {
Text(countryCode)
Icon(Icons.Filled.ArrowDropDown, contentDescription = "Select")
}
}
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = phoneNumber,
label = { Text("Phone number") },
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Phone,
imeAction = ImeAction.Send
),
keyboardActions = KeyboardActions(
onSend = {
focus.clearFocus(force = true)
keyboardController?.hide()
if (phoneNumber.isNotBlank()) {
onSubmit()
}
}
),
onValueChange = {
phoneNumber = it
},
maxLines = 1
)
}
Spacer(Modifier.height(24.dp))
Text(
text = "We will send you an SMS verification. Data charges might apply.",
style = MaterialTheme.typography.bodyMedium
)
Spacer(Modifier.height(8.dp))
Spacer(Modifier.weight(1f))
Column(Modifier.padding(horizontal = 16.dp)) {
Button(enabled = phoneNumber.isNotBlank(), onClick = onSubmit, modifier = Modifier.fillMaxWidth()) {
Text("Continue")
}
}
}
}
}
}
Sign Up Screen
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.widthIn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.ClickableText
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Checkbox
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.minimumInteractiveComponentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.unit.dp
@Composable
fun ScreenSignUp() {
var fullname by remember { mutableStateOf("Matias Duarte") }
var username by remember { mutableStateOf("[email protected]") }
var password by remember { mutableStateOf("duartesStrongPassword") }
var errorMessage by remember { mutableStateOf("") }
var acceptedTerms by remember { mutableStateOf(true) }
val focus = LocalFocusManager.current
val keyboardController = LocalSoftwareKeyboardController.current
val onSubmit: () -> Unit = {
TODO("Handle onSubmit")
}
@Composable
fun TermsAndConditions(onClick: () -> Unit) {
val fullText = "I accept the Terms & Conditions"
val clickableText = "Terms & Conditions"
val tag = "terms-and-conditions"
val annotatedString = buildAnnotatedString {
append(fullText)
val start = fullText.indexOf(clickableText)
val end = start + clickableText.length
addStyle(
style = SpanStyle(color = MaterialTheme.colorScheme.primary, fontWeight = FontWeight.SemiBold),
start = start,
end = end
)
addStringAnnotation(tag = tag, annotation = "https://www.composables.com", start = start, end = end)
}
val uriHandler = LocalUriHandler.current
ClickableText(
style = MaterialTheme.typography.bodyMedium.copy(color = LocalContentColor.current),
text = annotatedString,
onClick = { offset ->
val string = annotatedString.getStringAnnotations(tag, offset, offset).firstOrNull()
if (string != null) {
uriHandler.openUri(string.item)
} else {
onClick()
}
})
}
Scaffold { padding ->
BoxWithConstraints(modifier = Modifier.fillMaxSize().padding(padding)) {
val centered = maxWidth >= 840.dp
Box(
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState(0)),
contentAlignment = Alignment.TopCenter
) {
Column(
modifier = Modifier.widthIn(max = 600.dp).padding(horizontal = 16.dp).padding(vertical = 32.dp),
horizontalAlignment = if (centered) Alignment.CenterHorizontally else Alignment.Start
) {
Text("Create Free Account", style = MaterialTheme.typography.headlineLarge)
Spacer(Modifier.height(12.dp))
Text(
text = "Gain access to the full Composables catalog",
style = MaterialTheme.typography.bodyMedium
)
Spacer(Modifier.height(24.dp))
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = fullname,
label = { Text("Full name") },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text, imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(onNext = {
focus.moveFocus(FocusDirection.Next)
}),
onValueChange = { fullname = it },
singleLine = true
)
Spacer(Modifier.height(8.dp))
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
value = username,
label = { Text("E-mail") },
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Email,
imeAction = ImeAction.Next
),
keyboardActions = KeyboardActions(onNext = {
focus.moveFocus(FocusDirection.Next)
}),
onValueChange = { username = it },
singleLine = true
)
Spacer(Modifier.height(8.dp))
OutlinedTextField(
modifier = Modifier.fillMaxWidth(),
label = { Text("Password") },
isError = errorMessage.isNotBlank(),
supportingText = {
Text(errorMessage)
},
value = password,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions(onNext = {
focus.clearFocus()
keyboardController?.hide()
onSubmit()
}),
visualTransformation = PasswordVisualTransformation(),
onValueChange = {
password = it
},
singleLine = true
)
Spacer(Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth().clip(MaterialTheme.shapes.small)
.clickable { acceptedTerms = acceptedTerms.not() }.minimumInteractiveComponentSize()
.padding(horizontal = 2.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
Checkbox(
checked = acceptedTerms,
onCheckedChange = null,
)
TermsAndConditions(onClick = { acceptedTerms = acceptedTerms.not() })
}
Spacer(Modifier.height(16.dp))
Column(Modifier.padding(horizontal = 16.dp)) {
Button(
enabled = acceptedTerms && fullname.isNotBlank() && username.isNotBlank() && password.isNotBlank(),
onClick = onSubmit,
modifier = Modifier.fillMaxWidth()
) {
Text("Sign Up")
}
Spacer(Modifier.height(8.dp))
TextButton(
modifier = Modifier.align(Alignment.CenterHorizontally),
onClick = { /*TODO*/ },
colors = ButtonDefaults.textButtonColors(
contentColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.66f)
)
) {
Text("Already have an account? Sign in")
}
}
}
}
}
}
}
Expandable Card
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material.icons.rounded.Star
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import com.composables.uripainter.rememberUriPainter
@Composable
fun CardExpandable() {
data class Review(val stars: Float, val reviewer: String, val timestamp: String, val summary: String)
val reviews = listOf(
Review(
stars = 5f,
reviewer = "John",
timestamp = "2 hours ago",
summary = "Best coffee in town. I love the atmosphere and the staff is super friendly."
),
Review(
stars = 4.5f,
reviewer = "Cassidy",
timestamp = "1 day ago",
summary = "Great place to unwind. The cappuccino was excellent, and the pastries were delicious."
),
Review(
stars = 3.5f,
reviewer = "James",
timestamp = "3 days ago",
summary = "Decent coffee, but a bit crowded during peak hours. Could use more seating space."
),
Review(
stars = 4f,
reviewer = "Cassidy",
timestamp = "5 days ago",
summary = "Nice little coffee spot. I enjoyed the latte and the artsy decor."
),
Review(
stars = 5f,
reviewer = "Rony",
timestamp = "1 week ago",
summary = "Charming café with a Parisian vibe. The espresso here is top-notch."
),
Review(
stars = 3.5f,
reviewer = "Fraklin",
timestamp = "2 weeks ago",
summary = "Average coffee, but the location is convenient for a quick stop."
)
)
@Composable
fun RatingBar(
rating: Float,
maxRating: Int,
modifier: Modifier = Modifier,
fillColor: Color = Color(0xFFFFC107),
emptyColor: Color = Color(0xFFEEEEEE)
) {
val FirstHalf = object : Shape {
override fun createOutline(size: Size, layoutDirection: LayoutDirection, density: Density): Outline {
return Outline.Rectangle(Rect(0f, 0f, size.width / 2, size.height))
}
}
// offset matches the padding of the Star Icon
Row(modifier.offset(x = (-2).dp)) {
repeat(maxRating) { i ->
Box(Modifier.size(24.dp)) {
val lastFullIndex = (rating - 1).toInt()
Icon(
imageVector = Icons.Rounded.Star,
contentDescription = null,
tint = emptyColor,
modifier = Modifier.matchParentSize()
)
when {
i <= lastFullIndex -> {
Icon(
imageVector = Icons.Rounded.Star,
contentDescription = null,
tint = fillColor,
modifier = Modifier.matchParentSize()
)
}
i == lastFullIndex + 1 -> {
Icon(
imageVector = Icons.Rounded.Star,
contentDescription = null,
tint = fillColor,
modifier = Modifier.matchParentSize().clip(FirstHalf)
)
}
}
}
}
}
}
OutlinedCard {
var expanded by remember { mutableStateOf(false) }
val degrees by animateFloatAsState(if (expanded) -90f else 90f)
Column(Modifier.width(380.dp)) {
Image(
rememberUriPainter("https://images.unsplash.com/photo-1491147334573-44cbb4602074?w=320"),
modifier = Modifier.clip(CardDefaults.outlinedShape).aspectRatio(16 / 9f),
contentScale = ContentScale.Crop,
contentDescription = null
)
Column(Modifier.padding(start = 16.dp, end = 16.dp)) {
Row(verticalAlignment = Alignment.CenterVertically) {
Text("Cafe de Paris", style = MaterialTheme.typography.titleLarge)
Spacer(Modifier.weight(1f))
IconButton(onClick = { expanded = expanded.not() }) {
Icon(
imageVector = Icons.Filled.ChevronRight,
contentDescription = if (expanded) "Hide details" else "Show more details",
modifier = Modifier.rotate(degrees)
)
}
}
Row(verticalAlignment = Alignment.CenterVertically) {
RatingBar(rating = 4.5f, maxRating = 5)
Spacer(Modifier.width(8.dp))
Text("4.5", style = MaterialTheme.typography.bodyMedium)
Spacer(Modifier.width(4.dp))
Text("(${1280})", style = MaterialTheme.typography.bodyMedium)
}
Spacer(Modifier.height(16.dp))
Box {
this@Column.AnimatedVisibility(visible = expanded) {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.height(190.dp),
contentPadding = PaddingValues(vertical = 8.dp)
) {
item { Text("Reviews (${reviews.size})") }
reviews.forEach { review ->
val rating = review.stars
val reviewer = review.reviewer
val timestamp = review.timestamp
val summary = review.summary
item {
Column {
Text(text = reviewer, style = MaterialTheme.typography.bodyLarge)
Spacer(Modifier.height(8.dp))
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
RatingBar(rating = rating, maxRating = 5)
Text(text = timestamp, style = MaterialTheme.typography.bodyMedium)
}
Spacer(Modifier.height(16.dp))
Text(text = summary, style = MaterialTheme.typography.bodyMedium)
}
}
}
}
}
this@Column.AnimatedVisibility(visible = expanded) {
HorizontalDivider()
}
}
}
}
}
}
Friends Profile Screen
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
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.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.rounded.Home
import androidx.compose.material.icons.rounded.LocationOn
import androidx.compose.material.icons.rounded.Star
import androidx.compose.material3.Button
import androidx.compose.material3.Card
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
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.draw.shadow
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.composables.uripainter.rememberUriPainter
@Composable
fun ScreenFriendsProfile() {
@Composable
fun ProfileLabel(header: String, value: String, icon: ImageVector) {
Row(horizontalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier.padding(vertical = 8.dp)) {
Icon(icon, contentDescription = null)
Text(buildAnnotatedString {
append(header)
append(value)
addStyle(MaterialTheme.typography.bodyLarge.toSpanStyle(), header.length, header.length + value.length)
})
}
}
@Composable
fun FriendCard(onClick: () -> Unit, name: String, image: String) {
Card(
modifier = Modifier.width(132.dp).aspectRatio(3 / 4f)
.border(1.dp, MaterialTheme.colorScheme.onSurface.copy(0.12f), MaterialTheme.shapes.medium),
shape = MaterialTheme.shapes.medium,
onClick = onClick
) {
Box {
Image(
painter = rememberUriPainter(image),
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop,
contentDescription = "Profile photo",
)
Box(
Modifier.align(Alignment.BottomCenter).fillMaxWidth().height(64.dp).background(
Brush.verticalGradient(
listOf(
Color.Transparent,
Color.Black.copy(alpha = 0.33f),
)
)
)
)
Text(
name,
style = MaterialTheme.typography.bodyLarge,
overflow = TextOverflow.Ellipsis,
maxLines = 2,
color = MaterialTheme.colorScheme.onPrimary,
modifier = Modifier.padding(16.dp).align(Alignment.BottomStart)
)
}
}
}
val listState = rememberLazyListState()
val hasScrolled by remember {
derivedStateOf {
listState.firstVisibleItemScrollOffset > 0
}
}
val appBarElevation by animateDpAsState(targetValue = if (hasScrolled) 4.dp else 0.dp)
Scaffold(
topBar = {
TopAppBar(modifier = Modifier.shadow(appBarElevation), navigationIcon = {
IconButton(onClick = { /*TODO*/ }) {
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Go back")
}
}, title = { }, actions = {
IconButton(onClick = { /*TODO*/ }) {
Icon(Icons.Default.MoreVert, contentDescription = "Menu")
}
})
}
) { padding ->
Box(modifier = Modifier.fillMaxWidth().padding(padding), contentAlignment = Alignment.TopCenter) {
LazyColumn(
modifier = Modifier.widthIn(max = 600.dp),
state = listState,
) {
item {
Column(Modifier.padding(horizontal = 16.dp)) {
Box {
Image(
painter = rememberUriPainter("https://images.unsplash.com/photo-1622763853951-ded5a33cb724"),
modifier = Modifier.fillMaxWidth().aspectRatio(16 / 9f).clip(
RoundedCornerShape(
topStart = MaterialTheme.shapes.medium.topStart,
topEnd = MaterialTheme.shapes.medium.topStart,
bottomStart = CornerSize(0.dp),
bottomEnd = CornerSize(0.dp)
)
),
contentScale = ContentScale.Crop,
contentDescription = "Profile photo",
)
Image(
painter = rememberUriPainter("https://images.unsplash.com/photo-1618189063538-57c32befeb38?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=512&q=80"),
modifier = Modifier.size(120.dp).offset(y = (60).dp).clip(CircleShape).shadow(10.dp)
.background(MaterialTheme.colorScheme.surface)
.border(4.dp, MaterialTheme.colorScheme.surface, CircleShape)
.align(Alignment.BottomCenter),
contentScale = ContentScale.Crop,
contentDescription = "Profile photo",
)
}
Spacer(Modifier.height(60.dp))
Text(
"Clawdia Purrington",
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
Spacer(Modifier.height(12.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
Button(
onClick = { /*TODO*/ },
modifier = Modifier.weight(1f),
shape = MaterialTheme.shapes.extraSmall
) {
Text("Add friend")
}
OutlinedButton(
onClick = { /*TODO*/ },
modifier = Modifier.weight(1f),
shape = MaterialTheme.shapes.extraSmall
) {
Text("Message")
}
}
Spacer(Modifier.height(12.dp))
}
}
item {
Column(Modifier.padding(horizontal = 16.dp)) {
CompositionLocalProvider(
LocalContentColor provides MaterialTheme.colorScheme.onSurface.copy(
0.66f
)
) {
ProfileLabel("Works at ", "Composables", Icons.Rounded.Star)
ProfileLabel("Lives in ", "London", Icons.Rounded.LocationOn)
ProfileLabel("From ", "Indonesia", Icons.Rounded.Home)
}
}
}
item {
HorizontalDivider(modifier = Modifier.padding(vertical = 16.dp))
}
item {
Column {
Column(Modifier.padding(horizontal = 16.dp)) {
Text("Friends", style = MaterialTheme.typography.headlineSmall)
Spacer(Modifier.height(4.dp))
Text("1,230 friends")
Spacer(Modifier.height(12.dp))
}
LazyRow(
horizontalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(horizontal = 16.dp)
) {
item {
FriendCard(
name = "Purrcy Jackson",
image = "https://images.unsplash.com/photo-1618189063538-57c32befeb38?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=512&q=80",
onClick = { /* TODO */ })
}
item {
FriendCard(
name = "Furrball Fawcett",
image = "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=256&q=80",
onClick = { /* TODO */ })
}
item {
FriendCard(
name = "Mittens Mewington",
image = "https://images.unsplash.com/photo-1492370284958-c20b15c692d2",
onClick = { /* TODO */ })
}
item {
FriendCard(
name = "Boris",
image = "https://images.unsplash.com/photo-1618189063538-57c32befeb38?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=512&q=80",
onClick = { /* TODO */ })
}
}
}
}
item {
Spacer(Modifier.height(40.dp))
}
}
}
}
}
MusicPlayer with Album Cover and Seekbar
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.spring
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Image
import androidx.compose.foundation.MarqueeSpacing
import androidx.compose.foundation.background
import androidx.compose.foundation.basicMarquee
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsDraggedAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
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.layout.widthIn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Pause
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.SkipNext
import androidx.compose.material.icons.filled.SkipPrevious
import androidx.compose.material3.FilledIconButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
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.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.composables.uripainter.rememberUriPainter
import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.minutes
@Composable
fun MusicPlayerWithAlbumCoverAndSeekbar() {
var isPlaying by remember { mutableStateOf(false) }
var currentTimestamp by remember { mutableLongStateOf(0L) }
val totalDuration = 3.2.minutes.inWholeMilliseconds
Column(
modifier = Modifier.widthIn(max = 300.dp).padding(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = rememberUriPainter("https://images.unsplash.com/photo-1668605335684-c97ce92cbd76?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=512&q=80"),
modifier = Modifier.fillMaxWidth().aspectRatio(1f).clip(MaterialTheme.shapes.large)
.background(MaterialTheme.colorScheme.secondaryContainer),
contentScale = ContentScale.Crop,
contentDescription = null
)
Column(Modifier.padding(vertical = 12.dp)) {
Box(contentAlignment = Alignment.Center) {
Text(
text = "\tUnraveling the Human Psyche: Exploring the Depths of Consciousness",
modifier = Modifier.basicMarquee(iterations = Int.MAX_VALUE, spacing = MarqueeSpacing(58.dp))
.align(Alignment.Center),
style = MaterialTheme.typography.titleLarge,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Box(
Modifier.align(Alignment.CenterStart)
.height(MaterialTheme.typography.titleLarge.fontSize.value.dp + 8.dp).background(
Brush.horizontalGradient(
listOf(
MaterialTheme.colorScheme.background,
Color.Transparent
)
)
).width(24.dp)
)
Box(
Modifier.align(Alignment.CenterEnd)
.height(MaterialTheme.typography.titleLarge.fontSize.value.dp + 8.dp).background(
Brush.horizontalGradient(
listOf(
Color.Transparent,
MaterialTheme.colorScheme.background
)
)
).width(24.dp)
)
}
Text(
text = "Mind Matters",
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier.padding(top = 8.dp),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
val scrubberInteraction = remember { MutableInteractionSource() }
val isScrubbing by scrubberInteraction.collectIsDraggedAsState()
val progress by animateFloatAsState(
targetValue = currentTimestamp.toFloat() / totalDuration.toFloat(),
animationSpec = if (isScrubbing) spring() else tween(200),
)
LaunchedEffect(Unit) {
while (true) {
delay(1000)
if (isPlaying && isScrubbing.not()) {
currentTimestamp = (currentTimestamp + 1000)
}
}
}
Slider(
interactionSource = scrubberInteraction,
value = progress,
onValueChangeFinished = {},
onValueChange = { // convert % to currentTimestamp
currentTimestamp = (it * totalDuration).toLong()
})
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(24.dp),
modifier = Modifier.padding(16.dp)
) {
IconButton(onClick = {/*TODO Skip to previous media item*/ }) {
Icon(
imageVector = Icons.Filled.SkipPrevious,
contentDescription = "Previous",
modifier = Modifier.size(36.dp),
tint = MaterialTheme.colorScheme.primary
)
}
FilledIconButton(onClick = {/*TODO toggle playback*/
isPlaying = !isPlaying
}, modifier = Modifier.size(64.dp), shape = CircleShape) {
if (isPlaying) {
Icon(
imageVector = Icons.Filled.Pause,
contentDescription = "Pause",
modifier = Modifier.size(48.dp)
)
} else {
Icon(
imageVector = Icons.Filled.PlayArrow,
contentDescription = "Play",
modifier = Modifier.size(48.dp)
)
}
}
IconButton(onClick = {/*TODO Skip to previous media item*/ }) {
Icon(
imageVector = Icons.Filled.SkipNext,
contentDescription = "Next",
modifier = Modifier.size(36.dp),
tint = MaterialTheme.colorScheme.primary
)
}
}
}
}
Settings Screen
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
import androidx.compose.material.icons.outlined.AccountCircle
import androidx.compose.material.icons.outlined.AutoAwesome
import androidx.compose.material.icons.outlined.CreditCard
import androidx.compose.material.icons.outlined.Description
import androidx.compose.material.icons.outlined.Email
import androidx.compose.material.icons.outlined.Handshake
import androidx.compose.material.icons.outlined.Lock
import androidx.compose.material.icons.outlined.Notifications
import androidx.compose.material.icons.outlined.QuestionMark
import androidx.compose.material.icons.outlined.Style
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp
@Composable
fun SettingsScreen() {
@Composable
fun CategoryItem(title: String, icon: ImageVector, onClick: () -> Unit) {
Surface(
onClick = onClick,
shape = MaterialTheme.shapes.medium,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 16.dp),
horizontalArrangement = Arrangement.spacedBy(30.dp)
) {
Icon(
icon,
contentDescription = null,
modifier = Modifier.size(28.dp),
tint = MaterialTheme.colorScheme.onSurface
)
Text(title, style = MaterialTheme.typography.bodyLarge)
}
}
}
@Composable
fun AppVersion(versionText: String, copyrights: String, onClick: () -> Unit) {
Surface(onClick = onClick) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp, vertical = 12.dp),
horizontalArrangement = Arrangement.spacedBy(30.dp)
) {
Box(
modifier = Modifier.size(30.dp),
)
Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
Text(
versionText,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurface.copy(0.44f)
)
Text(
copyrights,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurface.copy(0.44f)
)
}
}
}
}
val listState = rememberLazyListState()
val hasScrolled by remember {
derivedStateOf {
listState.firstVisibleItemScrollOffset > 0
}
}
val appBarElevation by animateDpAsState(targetValue = if (hasScrolled) 4.dp else 0.dp)
Scaffold(
containerColor = MaterialTheme.colorScheme.surface,
contentColor = MaterialTheme.colorScheme.onSurface,
topBar = {
CenterAlignedTopAppBar(
colors = TopAppBarDefaults.topAppBarColors(
containerColor = if (isSystemInDarkTheme()) {
MaterialTheme.colorScheme.surfaceVariant.copy(alpha = if (hasScrolled) 1f else 0f)
} else {
MaterialTheme.colorScheme.surface
},
),
modifier = Modifier.shadow(appBarElevation),
title = { Text(text = "Settings") },
navigationIcon = {
IconButton(onClick = { /*TODO*/ }) {
Icon(Icons.AutoMirrored.Rounded.ArrowBack, contentDescription = "Go back")
}
},
actions = { },
)
},
) { padding ->
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.TopCenter) {
LazyColumn(contentPadding = padding, modifier = Modifier.widthIn(max = 600.dp), state = listState) {
item { CategoryItem(title = "Account", icon = Icons.Outlined.AccountCircle, onClick = { /*TODO*/ }) }
item {
CategoryItem(
title = "Payment methods",
icon = Icons.Outlined.CreditCard,
onClick = { /*TODO*/ })
}
item { CategoryItem(title = "Privacy", icon = Icons.Outlined.Lock, onClick = { /*TODO*/ }) }
item {
CategoryItem(
title = "Notifications",
icon = Icons.Outlined.Notifications,
onClick = { /*TODO*/ })
}
item { CategoryItem(title = "Look & Feel", icon = Icons.Outlined.Style, onClick = { /*TODO*/ }) }
item { HorizontalDivider(modifier = Modifier.padding(vertical = 12.dp)) }
item { CategoryItem(title = "FAQ", icon = Icons.Outlined.QuestionMark, onClick = { /*TODO*/ }) }
item { CategoryItem(title = "Send Feedback", icon = Icons.Outlined.Email, onClick = { /*TODO*/ }) }
item {
CategoryItem(
title = "See what's new",
icon = Icons.Outlined.AutoAwesome,
onClick = { /*TODO*/ })
}
item { HorizontalDivider(modifier = Modifier.padding(vertical = 12.dp)) }
item { CategoryItem(title = "Legal", icon = Icons.Outlined.Description, onClick = { /*TODO*/ }) }
item { CategoryItem(title = "Licenses", icon = Icons.Outlined.Handshake, onClick = { /*TODO*/ }) }
item { HorizontalDivider(modifier = Modifier.padding(vertical = 12.dp)) }
item {
AppVersion(
versionText = "Version 1.0.0",
copyrights = "© 2025 Your Company",
onClick = { /* TODO Add easter egg after 8 times is clicked */ })
}
}
}
}
}
List with swipe to dismiss
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
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.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Archive
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.SwipeToDismissBox
import androidx.compose.material3.SwipeToDismissBoxValue
import androidx.compose.material3.Text
import androidx.compose.material3.rememberSwipeToDismissBoxState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.composables.uripainter.rememberUriPainter
import kotlin.random.Random
@Composable
fun ListViewWithSwipeToDismiss() {
data class Message(
val id: Int = Random.nextInt(),
val displayName: String,
val content: String,
val avatar: String
)
val allMessages = listOf(
Message(
displayName = "Ava Johnson",
content = "Hey! Just wanted to let you know that I got the tickets for the concert. Can't wait to go together!",
avatar = "https://images.unsplash.com/photo-1544005313-94ddf0286df2?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1920&q=80"
),
Message(
displayName = "Adrian Thompson",
content = "Happy birthday! Wishing you an amazing day filled with joy, laughter, and lots of cake!",
avatar = "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1920&q=80"
),
Message(
displayName = "Amelia Rodriguez",
content = "Meeting at 3 pm sounds good. Let's discuss the agenda and prepare some ideas beforehand.",
avatar = "https://images.unsplash.com/photo-1580489944761-15a19d654956?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1920&q=80"
),
Message(
displayName = "Aaron Smith",
content = "Congratulations on your new job! You've worked hard for this opportunity. Proud of you!",
avatar = "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1920&q=80"
),
Message(
displayName = "Alice Bennett",
content = "I miss you! It's been too long since we last caught up. Let's plan a coffee date soon!",
avatar = "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1920&q=80"
)
)
val items = remember { mutableStateListOf(*allMessages.toTypedArray()) }
@Composable
fun ListItem(
onDismiss: () -> Unit,
photoUrl: String,
title: String,
content: String,
modifier: Modifier = Modifier
) {
val dismissState = rememberSwipeToDismissBoxState()
val backgroundAlpha by animateFloatAsState(
targetValue = if (dismissState.currentValue != SwipeToDismissBoxValue.Settled) 0f else 1f,
finishedListener = {
onDismiss()
})
val cornerRadius by animateDpAsState(targetValue = if (dismissState.dismissDirection != SwipeToDismissBoxValue.Settled) 12.dp else 0.dp)
SwipeToDismissBox(modifier = modifier, state = dismissState, backgroundContent = {
val alignment = when (dismissState.dismissDirection) {
SwipeToDismissBoxValue.StartToEnd -> Alignment.CenterStart
SwipeToDismissBoxValue.EndToStart -> Alignment.CenterEnd
SwipeToDismissBoxValue.Settled -> Alignment.Center
}
Box(
modifier = Modifier.alpha(backgroundAlpha).fillMaxSize()
.background(MaterialTheme.colorScheme.errorContainer).padding(16.dp), contentAlignment = alignment
) {
Icon(
imageVector = Icons.Outlined.Archive,
contentDescription = "Archive",
modifier = Modifier.size(24.dp)
)
}
}) {
Surface(onClick = { /* TODO */ }, shape = RoundedCornerShape(cornerRadius)) {
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.fillMaxWidth().padding(16.dp)
) {
Image(
rememberUriPainter(photoUrl),
modifier = Modifier.size(58.dp).clip(CircleShape),
contentScale = ContentScale.Crop,
contentDescription = null
)
Column(Modifier.weight(1f)) {
Row(
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = title,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
fontWeight = FontWeight.SemiBold
)
Text(text = "1d ago", style = MaterialTheme.typography.labelLarge)
}
Spacer(Modifier.height(4.dp))
Text(
text = content,
style = MaterialTheme.typography.bodyLarge,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
}
}
}
}
Box(
modifier = Modifier.padding(24.dp).border(1.dp, MaterialTheme.colorScheme.outline, MaterialTheme.shapes.large)
.clip(MaterialTheme.shapes.large)
) {
AnimatedVisibility(
visible = items.isEmpty(),
modifier = Modifier.align(Alignment.Center).fillMaxSize().padding(top = 20.dp),
enter = fadeIn(),
exit = fadeOut()
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text("(°-° ;)", style = MaterialTheme.typography.titleLarge, fontSize = 58.sp)
Spacer(Modifier.height(4.dp))
Text("No messages left to swipe away", style = MaterialTheme.typography.titleMedium)
Spacer(Modifier.height(8.dp))
Button(onClick = {
val newMessages = allMessages.map { message ->
message.copy(id = Random.nextInt())
}
items.addAll(newMessages)
}) {
Text("Bring them back")
}
}
}
AnimatedVisibility(items.isNotEmpty(), enter = fadeIn(), exit = fadeOut()) {
LazyColumn(
modifier = Modifier.widthIn(max = 600.dp).fillMaxHeight()
.border(1.dp, MaterialTheme.colorScheme.outline, MaterialTheme.shapes.large)
.clip(MaterialTheme.shapes.large).background(MaterialTheme.colorScheme.background)
) {
items(items, key = { item -> item.id }) { item ->
ListItem(
photoUrl = item.avatar,
title = item.displayName,
content = item.content,
onDismiss = {
items.remove(item)
})
}
}
}
}
}
More UI Blocks are coming...
Want to know when we add new UI Blocks? Sign up to our newsletter and we will email you when we release new ones