Try a free component
Get familiar with Composables UI by adding a free component in your project
🔎
Step 1
Find a component
{}
Step 2
Copy the code
🎨
Step 3
Customize away
In Avatars
Loading live preview powered by Compose Web
Avatar stack
@Composable
fun AvatarPhoto(imageUrl: String, modifier: Modifier = Modifier) {
AsyncImage(
model = imageUrl,
modifier = modifier.size(40.dp).clip(CircleShape).background(Color.White).border(2.dp, Color.White, CircleShape),
contentScale = ContentScale.Crop,
contentDescription = "Avatar",
)
}
Column(Modifier.padding(24.dp), verticalArrangement = Arrangement.spacedBy(24.dp), horizontalAlignment = Alignment.CenterHorizontally) {
Row(horizontalArrangement = Arrangement.spacedBy((-12).dp), verticalAlignment = Alignment.CenterVertically) {
AvatarPhoto(modifier = Modifier.size(32.dp), imageUrl = "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?fit=crop&w=512&q=80")
AvatarPhoto(modifier = Modifier.zIndex(25f).size(40.dp), imageUrl = "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?fit=crop&w=512&q=80")
AvatarPhoto(modifier = Modifier.zIndex(50f).size(48.dp), imageUrl = "https://images.unsplash.com/photo-1567532939604-b6b5b0db2604?fit=crop&w=512&q=80")
AvatarPhoto(modifier = Modifier.zIndex(25f).size(40.dp), imageUrl = "https://images.unsplash.com/photo-1540569014015-19a7be504e3a?fit=crop&w=512&q=80")
AvatarPhoto(modifier = Modifier.size(32.dp), imageUrl = "https://images.unsplash.com/photo-1506863530036-1efeddceb993?fit=crop&w=512&q=80")
}
Row(horizontalArrangement = Arrangement.spacedBy((-12).dp), verticalAlignment = Alignment.CenterVertically) {
AvatarPhoto(imageUrl = "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?fit=crop&w=512&q=80")
AvatarPhoto(imageUrl = "https://images.unsplash.com/photo-1500648767791-00dcc994a43e?fit=crop&w=512&q=80")
AvatarPhoto(imageUrl = "https://images.unsplash.com/photo-1567532939604-b6b5b0db2604?fit=crop&w=512&q=80")
AvatarPhoto(imageUrl = "https://images.unsplash.com/photo-1540569014015-19a7be504e3a?fit=crop&w=512&q=80")
AvatarPhoto(imageUrl = "https://images.unsplash.com/photo-1506863530036-1efeddceb993?fit=crop&w=512&q=80")
}
}
In Lists
Loading live preview powered by Compose Web
Simple List
data class Person(val name: String, val email: String, val photoUrl: String)
val items = listOf(
Person(name = "Jenny", email = "[email protected]", photoUrl = "https://images.unsplash.com/photo-1517841905240-472988babdf9?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=256&q=80"),
Person(name = "James", email = "[email protected]", photoUrl = "https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=256&q=80"),
Person(name = "Cassidy", email = "[email protected]", photoUrl = "https://images.unsplash.com/photo-1438761681033-6461ffad8d80?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=256&q=80"),
Person(name = "Kim", email = "[email protected]", photoUrl = "https://images.unsplash.com/photo-1494790108377-be9c29b29330?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=256&q=80"),
Person(name = "Samantha", email = "[email protected]", photoUrl = "https://images.unsplash.com/photo-1580489944761-15a19d654956?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=256&q=80"),
Person(name = "John", email = "[email protected]", photoUrl = "https://images.unsplash.com/photo-1580518380430-2f84c0a7fb85?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=256&q=80"),
)
LazyColumn(contentPadding = PaddingValues(vertical = 20.dp)) {
items(items) { item ->
Surface(onClick = { /* TODO */ }, shape = MaterialTheme.shapes.large) {
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(16.dp), modifier = Modifier.fillMaxWidth().padding(16.dp)) {
AsyncImage(model = item.photoUrl, modifier = Modifier.size(58.dp).clip(CircleShape), contentScale = ContentScale.Crop, contentDescription = null)
Column {
Text(text = item.name, maxLines = 1, overflow = TextOverflow.Ellipsis)
Spacer(Modifier.height(4.dp))
Text(text = item.email, style = MaterialTheme.typography.bodyMedium, maxLines = 1, overflow = TextOverflow.Ellipsis)
}
}
}
}
}
In Dialogs
Loading live preview powered by Compose Web
Start aligned when large
/**
* Add the following dependency to your build.gradle.kts:
*
* dependencies {
* implementation("com.composables:core")
* }
*/
val state = rememberDialogState(visible = true)
LaunchedEffect(state.visible) {
if (state.visible.not()) {
delay(1000)
state.visible = true
}
}
BoxWithConstraints {
val isCompact = maxWidth <= 600.dp
Dialog(state) {
Scrim(enter = fadeIn(), exit = fadeOut(), scrimColor = Color.Black.copy(0.3f))
DialogPanel(
modifier = Modifier.systemBarsPadding()
.padding(16.dp)
.shadow(8.dp, ComposeTheme.shapes.roundL)
.background(Color.White, ComposeTheme.shapes.round)
.padding(24.dp),
enter = scaleIn(initialScale = 0.8f) + fadeIn(tween(durationMillis = 250)),
exit = scaleOut(targetScale = 0.6f) + fadeOut(tween(durationMillis = 150))
) {
Column(
modifier = Modifier.let {
if (isCompact) it.fillMaxWidth() else it.widthIn(min = 280.dp, max = 520.dp)
},
horizontalAlignment = if (isCompact) Alignment.CenterHorizontally else Alignment.Start
) {
BasicText(
text = "Are you sure?",
style = ComposeTheme.textStyles.base.copy(
fontWeight = FontWeight.Medium,
color = ComposeTheme.colors.gray900,
textAlign = if (isCompact) TextAlign.Center else TextAlign.Start
)
)
Spacer(Modifier.height(16.dp))
BasicText(
text = "This action cannot be undone. Choose wisely.",
style = ComposeTheme.textStyles.base.copy(
fontWeight = FontWeight.Normal,
color = ComposeTheme.colors.gray600,
textAlign = if (isCompact) TextAlign.Center else TextAlign.Start
)
)
Spacer(Modifier.height(24.dp))
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp, alignment = Alignment.End),
modifier = Modifier.fillMaxWidth()
) {
Box(Modifier.clip(ComposeTheme.shapes.roundL)
.clickable(role = Role.Button) { state.visible = false }
.border(1.dp, Color(0xFFBDBDBD), ComposeTheme.shapes.roundL)
.padding(horizontal = 14.dp, vertical = 10.dp)
.let { if (isCompact) it.weight(1f) else it },
contentAlignment = Alignment.Center
) {
BasicText(
text = "Cancel", style = ComposeTheme.textStyles.base.copy(
color = Color(0xFF424242), fontWeight = FontWeight.Medium
)
)
}
Box(
Modifier.clip(ComposeTheme.shapes.roundL)
.clickable(role = Role.Button) { state.visible = false }.background(Color(0xFF212121))
.padding(horizontal = 14.dp, vertical = 10.dp)
.let { if (isCompact) it.weight(1f) else it },
contentAlignment = Alignment.Center
) {
BasicText(
text = "Continue", style = ComposeTheme.textStyles.base.copy(
color = Color.White, fontWeight = FontWeight.Medium
)
)
}
}
}
}
}
}
In Cards
Loading live preview powered by Compose Web
VisualCardWithAction
Box(Modifier.clip(ComposeTheme.shapes.roundXL).width(400.dp)) {
AsyncImage(model = "https://images.unsplash.com/photo-1521676129211-b7a9e7592e65?ixlib=rb-4.0.3&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=512&q=80", contentDescription = "Autumn Collection photo", contentScale = ContentScale.Crop, modifier = Modifier.aspectRatio(1f).fillMaxWidth().background(ComposeTheme.colors.gray50))
Box(Modifier.align(Alignment.TopCenter).fillMaxWidth().height(64.dp).background(Brush.verticalGradient(listOf(Color.Black.copy(alpha = 0.33f), Color.Transparent))))
Box(Modifier.align(Alignment.BottomCenter).fillMaxWidth().height(64.dp).background(Brush.verticalGradient(listOf(Color.Transparent, Color.Black.copy(alpha = 0.33f)))))
BasicText("Autumn Collection", style = ComposeTheme.textStyles.xl3.copy(color = Color.White), modifier = Modifier.padding(16.dp).align(Alignment.BottomStart))
Box(Modifier.align(Alignment.TopEnd).padding(16.dp).clip(ComposeTheme.shapes.roundFull).clickable(role = Role.Button) { /* TODO */ }.background(Color.White).padding(16.dp), contentAlignment = Alignment.Center) {
Image(Icons.Outlined.Favorite, contentDescription = "Favorite", colorFilter = ColorFilter.tint(ComposeTheme.colors.gray900))
}
}
In Settings
Loading live preview powered by Compose Web
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 = "© 2024 Your Company", onClick = { /* TODO Add easter egg after 8 times is clicked */ }) }
}
}
}
In MusicPlayback
Loading live preview powered by Compose Web
MusicPlayerControls
var isPlaying by remember { mutableStateOf(false) }
val repeatModes = listOf("off", "once", "all")
var repeatMode by remember { mutableStateOf(repeatModes[0]) }
var shuffle by remember { mutableStateOf(false) }
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(24.dp),
) {
IconButton(
onClick = {/*TODO Enable shuffle mode*/
shuffle = shuffle.not()
},
) {
val icon = when (shuffle) {
true -> Icons.Outlined.ShuffleOn
false -> Icons.Rounded.Shuffle
}
Icon(imageVector = icon, contentDescription = "Forward 5 seconds", modifier = Modifier.size(24.dp), tint = MaterialTheme.colorScheme.primary)
}
IconButton(onClick = {/*TODO Skip to previous media item*/ }) {
Icon(imageVector = Icons.Filled.SkipPrevious, contentDescription = "Play 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.Rounded.Pause, contentDescription = "Pause", modifier = Modifier.size(48.dp))
} else {
Icon(imageVector = Icons.Rounded.PlayArrow, contentDescription = "Play", modifier = Modifier.size(48.dp))
}
}
IconButton(onClick = {/*TODO Skip to previous media item*/ }) {
Icon(imageVector = Icons.Filled.SkipNext, contentDescription = "Play Next", modifier = Modifier.size(36.dp), tint = MaterialTheme.colorScheme.primary)
}
IconButton(
onClick = {
repeatMode = repeatModes[(repeatModes.indexOf(repeatMode) + 1) % repeatModes.size]
},
) {
val icon = when (repeatMode) {
"once" -> Icons.Rounded.RepeatOne
"all" -> Icons.Rounded.RepeatOn
else -> Icons.Rounded.Repeat
}
Icon(imageVector = icon, contentDescription = "Forward 5 seconds", modifier = Modifier.size(24.dp), tint = MaterialTheme.colorScheme.primary)
}
}
In Dropdowns
Loading live preview powered by Compose Web
Simple dropdown
/**
* Add the following dependency to your build.gradle.kts:
*
* dependencies {
* implementation("com.composables:core")
* }
*/
@Composable
fun ChevronDown(): ImageVector {
return remember {
ImageVector.Builder(
name = "ChevronDown",
defaultWidth = 16.dp,
defaultHeight = 16.dp,
viewportWidth = 24f,
viewportHeight = 24f
).apply {
path(
fill = null,
fillAlpha = 1.0f,
stroke = SolidColor(Color(0xFF000000)),
strokeAlpha = 1.0f,
strokeLineWidth = 2f,
strokeLineCap = StrokeCap.Round,
strokeLineJoin = StrokeJoin.Round,
strokeLineMiter = 1.0f,
pathFillType = PathFillType.NonZero
) {
moveTo(6f, 9f)
lineToRelative(6f, 6f)
lineToRelative(6f, -6f)
}
}.build()
}
}
Box(Modifier.height(300.dp).fillMaxWidth()) {
Menu(modifier = Modifier.align(Alignment.TopCenter).width(240.dp), state = rememberMenuState(expanded = true)) {
MenuButton(
Modifier.clip(RoundedCornerShape(6.dp)).background(Color.White)
.border(1.dp, Color(0xFFBDBDBD), RoundedCornerShape(6.dp))
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(horizontal = 14.dp, vertical = 10.dp)
) {
BasicText("Options", style = TextStyle(fontWeight = FontWeight(500)))
Spacer(Modifier.width(4.dp))
Image(ChevronDown(), null)
}
}
MenuContent(
modifier = Modifier.padding(top = 4.dp).width(320.dp).clip(RoundedCornerShape(6.dp))
.border(1.dp, Color(0xFFE0E0E0), RoundedCornerShape(6.dp)).background(Color.White).padding(4.dp),
exit = fadeOut()
) {
MenuItem(modifier = Modifier.clip(RoundedCornerShape(6.dp)), onClick = { /* TODO handle click */ }) {
BasicText("Option 1", Modifier.fillMaxWidth().padding(vertical = 10.dp, horizontal = 10.dp))
}
MenuItem(modifier = Modifier.clip(RoundedCornerShape(6.dp)), onClick = { /* TODO handle click */ }) {
BasicText("Option 2", Modifier.fillMaxWidth().padding(vertical = 10.dp, horizontal = 10.dp))
}
MenuItem(modifier = Modifier.clip(RoundedCornerShape(6.dp)), onClick = { /* TODO handle click */ }) {
BasicText("Option 3", Modifier.fillMaxWidth().padding(vertical = 10.dp, horizontal = 10.dp))
}
}
}
}
In BottomSheets
Loading live preview powered by Compose Web
ModalBottomSheetWithVerticalActions
val state = rememberModalBottomSheetState(skipPartiallyExpanded = true)
val scope = rememberCoroutineScope()
LaunchedEffect(Unit) {
state.show()
}
if (state.isVisible) {
ModalBottomSheet(onDismissRequest = {
scope.launch {
state.hide()
delay(1500)
state.show()
}
}) {
val items = listOf(
Icons.Rounded.Share to "Share",
Icons.Rounded.Link to "Get Link",
Icons.Rounded.Edit to "Edit name",
Icons.Rounded.Delete to "Delete items",
)
Column(Modifier.navigationBarsPadding()) {
items.forEach { item ->
Row(
horizontalArrangement = Arrangement.spacedBy(32.dp),
modifier = Modifier.clickable { /* TODO */ }.clip(MaterialTheme.shapes.medium).fillMaxWidth()
.padding(horizontal = 32.dp, vertical = 18.dp),
) {
Icon(item.first, null)
Text(item.second)
}
}
}
}
}
In Accordions
Loading live preview powered by Compose Web
Accordion
@Composable
fun rememberChevronRight(): ImageVector {
return remember {
ImageVector.Builder(name = "ChevronRight", defaultWidth = 24.dp, defaultHeight = 24.dp, viewportWidth = 24f, viewportHeight = 24f).apply {
path(fill = null, fillAlpha = 1.0f, stroke = SolidColor(Color(0xFF000000)), strokeAlpha = 1.0f, strokeLineWidth = 2f, strokeLineCap = StrokeCap.Round, strokeLineJoin = StrokeJoin.Round, strokeLineMiter = 1.0f, pathFillType = PathFillType.NonZero) {
moveTo(9f, 18f)
lineToRelative(6f, -6f)
lineToRelative(-6f, -6f)
}
}.build()
}
}
LazyColumn(Modifier.widthIn(max = 600.dp)) {
item {
var expanded by remember { mutableStateOf(false) }
val degrees by animateFloatAsState(if (expanded) -90f else 90f)
Column {
Row(modifier = Modifier.clip(ComposeTheme.shapes.roundL).clickable { expanded = expanded.not() }.fillMaxWidth().padding(16.dp), horizontalArrangement = Arrangement.SpaceBetween) {
BasicText("How do I create a new account?", style = ComposeTheme.textStyles.base)
Image(imageVector = rememberChevronRight(), contentDescription = null, modifier = Modifier.rotate(degrees), colorFilter = ColorFilter.tint(ComposeTheme.colors.gray800))
}
AnimatedVisibility(visible = expanded, enter = expandVertically(spring(stiffness = Spring.StiffnessMediumLow, visibilityThreshold = IntSize.VisibilityThreshold)), exit = shrinkVertically()) {
Box(Modifier.fillMaxWidth().padding(16.dp)) {
BasicText("To create a new account, please follow these steps:\n\n" + "1. Open the app.\n" + "2. Tap on the 'Sign Up' button.\n" + "3. Fill in your details, including your name, email address, and password.\n" + "4. Click 'Create Account'.\n" + "5. You will receive a confirmation email to verify your account. Follow the instructions in the email to complete the registration process.", style = ComposeTheme.textStyles.sm)
}
}
Box(Modifier.fillMaxWidth().height(1.dp).background(ComposeTheme.colors.gray100))
}
}
item {
var expanded by remember { mutableStateOf(false) }
val degrees by animateFloatAsState(if (expanded) -90f else 90f)
Column {
Row(modifier = Modifier.clip(ComposeTheme.shapes.roundL).clickable { expanded = expanded.not() }.fillMaxWidth().padding(16.dp), horizontalArrangement = Arrangement.SpaceBetween) {
BasicText("How can I reset my password?", style = ComposeTheme.textStyles.base)
Image(imageVector = rememberChevronRight(), contentDescription = null, modifier = Modifier.rotate(degrees).size(24.dp))
}
AnimatedVisibility(visible = expanded, enter = expandVertically(spring(stiffness = Spring.StiffnessMediumLow, visibilityThreshold = IntSize.VisibilityThreshold)), exit = shrinkVertically()) {
Box(Modifier.fillMaxWidth().padding(16.dp)) {
BasicText("If you need to reset your password, here's what you should do:\n" + "1. On the app's login screen, tap on the 'Forgot Password?' link.\n" + "2. Enter your registered email address.\n" + "3. You will receive an email with a password reset link.\n" + "4. Click on the link and follow the instructions to reset your password.\n" + "5. Once your password is reset, you can log in with your new password.", style = ComposeTheme.textStyles.sm)
}
}
Box(Modifier.fillMaxWidth().height(1.dp).background(ComposeTheme.colors.gray100))
}
}
item {
var expanded by remember { mutableStateOf(false) }
val degrees by animateFloatAsState(if (expanded) -90f else 90f)
Column {
Row(modifier = Modifier.clip(ComposeTheme.shapes.roundL).clickable { expanded = expanded.not() }.fillMaxWidth().padding(16.dp), horizontalArrangement = Arrangement.SpaceBetween) {
BasicText("How do I update my profile information?", style = ComposeTheme.textStyles.base)
Image(imageVector = rememberChevronRight(), contentDescription = null, modifier = Modifier.rotate(degrees).size(24.dp))
}
AnimatedVisibility(visible = expanded, enter = expandVertically(spring(stiffness = Spring.StiffnessMediumLow, visibilityThreshold = IntSize.VisibilityThreshold)), exit = shrinkVertically()) {
Box(Modifier.fillMaxWidth().padding(16.dp)) {
BasicText("To update your profile information, please follow these steps:\n" + "1. Log in to your account.\n" + "2. Go to the 'Profile' section of the app.\n" + "3. Click on the 'Edit Profile' button.\n" + "4. Update the information you want to change, such as your name, profile picture, or contact details.\n" + "5. Click 'Save' to save your changes.\n" + "Your profile information will be updated accordingly.", style = ComposeTheme.textStyles.sm)
}
}
}
}
}
In Forms
Loading live preview powered by Compose Web
FormLayoutFilled
val focusManager = LocalFocusManager.current
LazyColumn(modifier = Modifier.widthIn(max = 480.dp), verticalArrangement = Arrangement.spacedBy(12.dp), contentPadding = PaddingValues(vertical = 24.dp)) {
item {
Column(modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(12.dp)) {
Surface(onClick = { /* TODO */ }, color = MaterialTheme.colorScheme.surfaceVariant, shape = CircleShape) {
Box(modifier = Modifier.size(96.dp), contentAlignment = Alignment.Center) {
Icon(imageVector = Icons.Outlined.CameraAlt, contentDescription = null, tint = MaterialTheme.colorScheme.onSurfaceVariant)
}
}
Text("Add photo")
}
}
item {
var imageUrl = remember { mutableStateOf<String?>(null) }
Box(Modifier.fillMaxWidth()) {
AsyncImage(model = imageUrl, contentDescription = null)
}
}
item {
var text by remember { mutableStateOf("") }
TextField(modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), label = { Text("First name") }, value = text, onValueChange = { text = it }, singleLine = true, trailingIcon = {
AnimatedVisibility(visible = text.isNotBlank(), enter = fadeIn(), exit = fadeOut()) {
IconButton(onClick = { text = "" }) {
Icon(Icons.Outlined.Cancel, "Clear")
}
}
}, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text, imeAction = ImeAction.Next, capitalization = KeyboardCapitalization.Words), keyboardActions = KeyboardActions {
focusManager.moveFocus(FocusDirection.Next)
})
}
item {
var text by remember { mutableStateOf("") }
TextField(modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp), label = { Text("Last name") }, value = text, onValueChange = { text = it }, singleLine = true, trailingIcon = {
AnimatedVisibility(visible = text.isNotBlank(), enter = fadeIn(), exit = fadeOut()) {
IconButton(onClick = { text = "" }) {
Icon(Icons.Outlined.Cancel, "Clear")
}
}
}, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text, imeAction = ImeAction.Next, capitalization = KeyboardCapitalization.Words), keyboardActions = KeyboardActions {
focusManager.moveFocus(FocusDirection.Next)
})
}
item { Spacer(Modifier.height(4.dp)) }
item {
var text by remember { mutableStateOf("") }
TextField(
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
label = { Text("Phone number") },
value = text,
onValueChange = { text = it },
trailingIcon = {
AnimatedVisibility(visible = text.isNotBlank(), enter = fadeIn(), exit = fadeOut()) {
IconButton(onClick = { text = "" }) {
Icon(Icons.Outlined.Cancel, "Clear")
}
}
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone, imeAction = ImeAction.Next),
keyboardActions = KeyboardActions {
focusManager.moveFocus(FocusDirection.Next)
},
singleLine = true,
)
}
item { Spacer(Modifier.height(4.dp)) }
item {
var text by remember { mutableStateOf("") }
TextField(
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
label = { Text("Email") },
value = text,
onValueChange = { text = it },
trailingIcon = {
AnimatedVisibility(visible = text.isNotBlank(), enter = fadeIn(), exit = fadeOut()) {
IconButton(onClick = { text = "" }) {
Icon(Icons.Outlined.Cancel, "Clear")
}
}
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions {
focusManager.clearFocus()
},
singleLine = true,
)
}
item { Spacer(Modifier.height(4.dp)) }
item {
var text by remember { mutableStateOf("") }
TextField(
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
value = text,
onValueChange = {
text = it
},
label = { Text("Country") },
trailingIcon = {
AnimatedVisibility(visible = text.isNotBlank(), enter = fadeIn(), exit = fadeOut()) {
IconButton(onClick = { text = "" }) {
Icon(Icons.Outlined.Cancel, "Clear")
}
}
},
)
}
item {
var text by remember { mutableStateOf("") }
TextField(
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
label = { Text("Street address") },
value = text,
onValueChange = { text = it },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text, imeAction = ImeAction.Next),
keyboardActions = KeyboardActions {
focusManager.moveFocus(FocusDirection.Next)
},
singleLine = true,
trailingIcon = {
AnimatedVisibility(visible = text.isNotBlank(), enter = fadeIn(), exit = fadeOut()) {
IconButton(onClick = { text = "" }) {
Icon(Icons.Outlined.Cancel, "Clear")
}
}
},
)
}
item {
var text by remember { mutableStateOf("") }
TextField(
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
label = { Text("City") },
value = text,
onValueChange = { text = it },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text, imeAction = ImeAction.Next),
keyboardActions = KeyboardActions {
focusManager.moveFocus(FocusDirection.Next)
},
singleLine = true,
trailingIcon = {
AnimatedVisibility(visible = text.isNotBlank(), enter = fadeIn(), exit = fadeOut()) {
IconButton(onClick = { text = "" }) {
Icon(Icons.Outlined.Cancel, "Clear")
}
}
},
)
}
item {
var text by remember { mutableStateOf("") }
TextField(
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
label = { Text("Zip/postal code") },
value = text,
onValueChange = { text = it },
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Text, imeAction = ImeAction.Done),
keyboardActions = KeyboardActions {
focusManager.clearFocus()
},
singleLine = true,
trailingIcon = {
AnimatedVisibility(visible = text.isNotBlank(), enter = fadeIn(), exit = fadeOut()) {
IconButton(onClick = { text = "" }) {
Icon(Icons.Outlined.Cancel, "Clear")
}
}
},
)
}
item {
Text(text = "Groups", style = MaterialTheme.typography.titleMedium, modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 24.dp, bottom = 8.dp))
}
item {
var selected by remember { mutableStateOf<Int?>(null) }
val options = listOf("Family", "Friends", "Work", "Other")
Column {
options.forEachIndexed { i, option ->
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(24.dp), modifier = Modifier.clickable { selected = i }.fillMaxWidth().padding(16.dp)) {
RadioButton(selected = selected == i, onClick = null)
Text(text = option)
}
}
}
}
item {
Text(text = "Notification Options", style = MaterialTheme.typography.titleMedium, modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 24.dp, bottom = 8.dp))
}
item {
var selected by remember { mutableStateOf(false) }
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(24.dp), modifier = Modifier.clickable { selected = selected.not() }.fillMaxWidth().padding(16.dp)) {
Checkbox(checked = selected, onCheckedChange = null)
Text(text = "Override 'Do not Disturb'")
}
}
item {
var selected by remember { mutableStateOf(false) }
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(24.dp), modifier = Modifier.clickable { selected = selected.not() }.fillMaxWidth().padding(16.dp)) {
Checkbox(checked = selected, onCheckedChange = null)
Text(text = "Block calls from this contact")
}
}
}
In ResponsiveLayouts
Loading live preview powered by Compose Web
SupportingPane
var navigationIndex by remember { mutableStateOf(0) }
val scope = rememberCoroutineScope()
val widthSizeClass = calculateWindowSizeClass().widthSizeClass
val navigationItems = listOf(
mapOf("icon" to Icons.AutoMirrored.Outlined.Message, "label" to "Messages"),
mapOf("icon" to Icons.Outlined.Photo, "label" to "Photos"),
mapOf("icon" to Icons.Outlined.VideoCall, "label" to "Videos"),
mapOf("icon" to Icons.Outlined.Games, "label" to "Games"),
)
@Composable
fun ContentPanel(modifier: Modifier = Modifier) { // TODO replace the contents of this composable with your own content
Box(modifier.padding(16.dp).border(1.dp, MaterialTheme.colorScheme.outline, RoundedCornerShape(18.dp)).clip(RoundedCornerShape(18.dp)).background(MaterialColors.Gray[100]))
}
@Composable
fun SupportPane(modifier: Modifier = Modifier) { // TODO replace the contents of this composable with your own content
Box(modifier.padding(16.dp).border(1.dp, MaterialTheme.colorScheme.outline, RoundedCornerShape(18.dp)).clip(RoundedCornerShape(18.dp)).background(MaterialColors.Gray[100]))
}
if (widthSizeClass in listOf(WindowWidthSizeClass.Compact, WindowWidthSizeClass.Medium)) {
val drawerState = rememberDrawerState(DrawerValue.Closed)
ModalNavigationDrawer(
modifier = Modifier.fillMaxHeight(),
drawerState = drawerState,
drawerContent = {
ModalDrawerSheet {
Column {
Icon(imageVector = Icons.Rounded.Token, contentDescription = null, modifier = Modifier.padding(16.dp).size(48.dp), tint = MaterialTheme.colorScheme.primary)
Spacer(Modifier.height(12.dp))
navigationItems.forEachIndexed { index, map ->
val icon = map.get("icon") as ImageVector
val label = map.get("label") as String
NavigationDrawerItem(icon = { Icon(icon, contentDescription = null) }, label = { Text(label) }, selected = index == navigationIndex, onClick = {
scope.launch { drawerState.close() }
navigationIndex = index
}, modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding))
}
}
}
},
) {
Scaffold(
topBar = {
TopAppBar(title = { Text(navigationItems[navigationIndex].get("label") as String) }, navigationIcon = {
IconButton(onClick = { scope.launch { drawerState.open() } }) {
Icon(Icons.Rounded.Menu, "Show menu")
}
})
},
) { contentPadding ->
Column(Modifier.padding(contentPadding).fillMaxSize()) {
ContentPanel(Modifier.weight(1f).fillMaxWidth())
HorizontalDivider()
SupportPane(Modifier.weight(1f).fillMaxWidth())
}
}
}
} else {
Scaffold {
PermanentNavigationDrawer(drawerContent = {
PermanentDrawerSheet(Modifier.width(200.dp)) {
Column(Modifier.padding(vertical = 24.dp)) {
ExtendedFloatingActionButton(
onClick = { /*TODO*/ },
modifier = Modifier.padding(horizontal = 12.dp),
) {
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start)) {
Icon(Icons.Outlined.Edit, null)
Text("Compose")
}
}
Spacer(Modifier.height(24.dp))
LazyColumn(verticalArrangement = Arrangement.spacedBy(4.dp)) {
itemsIndexed(navigationItems) { index, map ->
val item = navigationItems.get(index)
val icon = item.get("icon") as ImageVector
val label = item.get("label") as String
NavigationDrawerItem(modifier = Modifier.padding(horizontal = 16.dp), selected = index == navigationIndex, icon = { Icon(icon, null) }, label = { Text(label) }, onClick = { navigationIndex = index })
}
}
}
}
}, content = {
Row {
ContentPanel(Modifier.weight(1f).fillMaxHeight())
VerticalDivider()
SupportPane(Modifier.width(360.dp).fillMaxHeight())
}
})
}
}