I am wanting to build the following layout in Compose:
I have written the following code for this:
#Composable
fun MainScreenContent(){
Surface(
color = MaterialTheme.colors.primary,
modifier = Modifier
.wrapContentWidth(Alignment.CenterHorizontally)
.wrapContentHeight(Alignment.CenterVertically)
) {
Row() {
Column(Modifier.weight(0.3F)) {
Greeting("Android")
Spacer(Modifier.padding(16.dp))
Greeting("User")
}
Surface(modifier = Modifier.weight(0.4F),
color = MaterialTheme.colors.primaryVariant,
){
Spacer(Modifier.padding(16.dp))
}
Column(Modifier.weight(0.3F)) {
Greeting("Android")
Spacer(Modifier.padding(16.dp))
Greeting("User")
}
}
}
}
#Composable
fun Greeting(name: String) {
Text(text = "Hello $name!",
Modifier
.padding(16.dp), fontSize = 30.sp)
}
But with this, I am getting the following output:
How can I achieve the objective?
Set the parent Surface's height to IntrinsicSize.Max, and add fillMaxHeight() to the inner Surface's modifier.
#Composable
fun MainScreenContent() {
Surface(
...,
modifier = Modifier
.wrapContentWidth(Alignment.CenterHorizontally)
.height(IntrinsicSize.Max),
) {
Row {
...
Surface(
modifier = Modifier
.weight(0.4F)
.fillMaxHeight(),
color = MaterialTheme.colors.primaryVariant,
) {
Spacer(Modifier.padding(16.dp))
}
...
}
}
}
Related
A few days ago I bumped on a problem where a part of my view is overlaped by keyboard.
Let's say we have 3 different dialogs (could be any content), which looks like this:
When I want to write in anything, last dialog is covered by keyboard:
And there's no way to see what user wrote. Here's my code:
#Composable
fun BuildWordsView(navController: NavController, sharedViewModel: SharedViewModel) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(PrimaryLight)
.fillMaxSize()
) {
BuildWordsScreenContents()
}
}
#Composable
fun BuildWordsScreenContents() {
Column(
Modifier
.fillMaxSize()
.padding(all = 16.dp)
) {
val inputBoxModifier = Modifier
.clip(RoundedCornerShape(10.dp))
.background(Primary)
.weight(12f)
.wrapContentHeight()
InputBlock("Dialog1", inputBoxModifier)
Spacer(Modifier.weight(1f))
InputBlock("Dialog2", inputBoxModifier)
Spacer(Modifier.weight(1f))
InputBlock("Dialog3", inputBoxModifier)
}
}
#Composable
fun InputBlock(dialogText: String, inputBlockModifier: Modifier) {
Column(modifier = inputBlockModifier) {
Text(
dialogText,
fontSize = 30.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
)
var text by remember { mutableStateOf("") }
TextField(
value = text,
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center),
onValueChange = { text = it },
label = { Text("Label") }
)
}
}
This question seems to be similar to mine but answers modificate the content of view which I want to avoid:
Software keyboard overlaps content of jetpack compose view
By now I figured out how to solve this problem and I share my approach as an answer
My approach to deal with this problem is using Insets for Jetpack Compose:
https://google.github.io/accompanist/insets/
In order to start dealing with problem you need to add depency to gradle (current version is 0.22.0-rc).
dependencies {
implementation "com.google.accompanist:accompanist-insets:0.22.0-rc"
}
Then you need to wrap your content in your activity with ProvideWindowInsets
setContent {
ProvideWindowInsets {
YourTheme {
//YOUR CONTENT HERE
}
}
}
Additionaly you need to add following line in your activity onCreate() function:
WindowCompat.setDecorFitsSystemWindows(window, false)
Update: Despite this function is recommended, to my experience it may make this approach not work. If you face any problem, you may need to delete this line.
Now your project is set up to use Insets
In the next steps I'm gonna use code I provided in question
First of all you need to wrap your main Column with
ProvideWindowInsets(windowInsetsAnimationsEnabled = true)
Then let's modificate a modifier a bit by adding:
.statusBarsPadding()
.navigationBarsWithImePadding()
.verticalScroll(rememberScrollState())
As you can see the trick in my approach is to use verticalScroll(). Final code of main column should look like this:
#Composable
fun BuildWordsView(navController: NavController, sharedViewModel: SharedViewModel) {
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(PrimaryLight)
.statusBarsPadding()
.navigationBarsWithImePadding()
.verticalScroll(rememberScrollState())
.fillMaxSize()
) {
BuildWordsScreenContents()
}
}
}
Now let's modificate the modifier of Column in fun BuildWordsScreenContents()
The main modification is that we provide a height of our screen by:
.height(LocalConfiguration.current.screenHeightDp.dp)
This means that height of our Column would fit our screen perfectly. So when keyboard is not opened the Column will not be scrollable
There is the full code:
#Composable
fun BuildWordsView(navController: NavController, sharedViewModel: SharedViewModel) {
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(PrimaryLight)
.statusBarsPadding()
.navigationBarsWithImePadding()
.verticalScroll(rememberScrollState())
.fillMaxSize()
) {
BuildWordsScreenContents()
}
}
}
#Composable
fun BuildWordsScreenContents() {
Column(
Modifier
.height(LocalConfiguration.current.screenHeightDp.dp)
.padding(all = 16.dp)
) {
val inputBoxModifier = Modifier
.clip(RoundedCornerShape(10.dp))
.background(Primary)
.weight(12f)
.wrapContentHeight()
InputBlock("Dialog1", inputBoxModifier)
Spacer(Modifier.weight(1f))
InputBlock("Dialog2", inputBoxModifier)
Spacer(Modifier.weight(1f))
InputBlock("Dialog3", inputBoxModifier)
}
}
#Composable
fun InputBlock(dialogText: String, inputBlockModifier: Modifier) {
Column(modifier = inputBlockModifier) {
Text(
dialogText,
fontSize = 30.sp,
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center)
)
var text by remember { mutableStateOf("") }
TextField(
value = text,
modifier = Modifier
.fillMaxWidth()
.wrapContentSize(Alignment.Center),
onValueChange = { text = it },
label = { Text("Label") }
)
}
}
The final code allows us to scroll down the view:
Important Note For APIs 30-
For APIs lower then 30 you need to modificate the AndroidManifest.xml file
In <activity you need to add android:windowSoftInputMode="adjustResize" in order to make it work. It do not resize your components but it is obligatory to make this approach work
Manifest should look like this:
<activity
android:name=".MainActivity"
android:windowSoftInputMode="adjustResize"
Feel free to give me any tips how can I improve my question. AFAIK this problem is as old as android and I wanted to create a quick tutorial how to manage that. Happy coding!
Here's my solution, using the experimental features in Compose 1.2.0
In build.gradle (:project)
...
ext {
compose_version = '1.2.0-beta03'
}
...
In build.gradle (:app)
...
dependencies {
implementation 'androidx.core:core-ktx:1.8.0'
implementation "androidx.compose.ui:ui:$compose_version"
implementation "androidx.compose.material:material:$compose_version"
implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
implementation "androidx.compose.foundation:foundation-layout:$compose_version"
...
}
In AndroidManifest.xml
<activity
...
android:windowSoftInputMode="adjustResize" >
In AuthScreen.kt
#OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
#Composable
fun AuthScreen(
val focusManager = LocalFocusManager.current
val coroutineScope = rememberCoroutineScope()
// Setup the handles to items to scroll to.
val bringIntoViewRequesters = mutableListOf(remember { BringIntoViewRequester() })
repeat(6) {
bringIntoViewRequesters += remember { BringIntoViewRequester() }
}
val buttonViewRequester = remember { BringIntoViewRequester() }
fun requestBringIntoView(focusState: FocusState, viewItem: Int) {
if (focusState.isFocused) {
coroutineScope.launch {
delay(200) // needed to allow keyboard to come up first.
if (viewItem >= 2) { // force to scroll to button for lower fields
buttonViewRequester.bringIntoView()
} else {
bringIntoViewRequesters[viewItem].bringIntoView()
}
}
}
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Top,
modifier = Modifier
.fillMaxSize()
.statusBarsPadding()
.navigationBarsPadding()
.imePadding()
.padding(10.dp)
.verticalScroll(rememberScrollState())
) {
repeat(6) { viewItem ->
Row(
modifier = Modifier
.bringIntoViewRequester(bringIntoViewRequesters[viewItem]),
) {
TextField(
value = "",
onValueChange = {},
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Next),
keyboardActions = KeyboardActions(
onNext = { focusManager.moveFocus(FocusDirection.Down) }),
modifier = Modifier
.onFocusEvent { focusState ->
requestBringIntoView(focusState, viewItem)
},
)
}
}
Button(
onClick = {},
modifier = Modifier
.bringIntoViewRequester(buttonViewRequester)
) {
Text(text = "I'm Visible")
}
}
}
Try to google into such keywords: Modifier.statusBarsPadding(), systemBarsPadding(), navigationBarsPadding().
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
makeStatusBarTransparent()
//WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
Box(
Modifier
.background(Color.Blue)
.fillMaxSize()
.padding(top = 10.dp, bottom = 10.dp)
.statusBarsPadding() //systemBarsPadding
) {
//Box(Modifier.background(Color.Green).navigationBarsPadding()) {
Greeting("TopStart", Alignment.TopStart)
Greeting("BottomStart", Alignment.BottomStart)
Greeting("TopEnd", Alignment.TopEnd)
Greeting("BottomEnd", Alignment.BottomEnd)
//}
}
}
/* setContent {
MyComposeApp1Theme {
// A surface container using the 'background' color from the theme
Surface(modifier = Modifier.fillMaxSize(), color = Color.Red) {
Box(Modifier
.fillMaxSize()
.padding(top = 34.dp)
) {
Greeting("Android")
}
}
}
}*/
}
}
#Composable
fun Greeting(name: String, contentAlignment: Alignment) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = contentAlignment
) {
Text(
text = "Hello $name!",
Modifier
.background(color = Color.Cyan)
)
}
}
#Preview(showBackground = true)
#Composable
fun DefaultPreview() {
MyComposeApp1Theme {
Greeting("Android", Alignment.TopStart)
}
}
#Suppress("DEPRECATION")
fun Activity.makeStatusBarTransparent() {
window.apply {
clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
decorView.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
statusBarColor = android.graphics.Color.GREEN//android.graphics.Color.TRANSPARENT
}
}
val Int.dp
get() = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
toFloat(),
Resources.getSystem().displayMetrics
)
I am practicing JetPack compose, but my text won't align in the center for some reason within the 2 surfaces. Any tips why?
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
SimpleLayoutsTheme {
// A surface container using the 'background' color from the theme
MainScreen("Hello World!")
}
}
}
}
#Composable
fun MainScreen(message: String) {
Surface {
Column {
Surface(modifier = Modifier
.width(500.dp)
.height(250.dp)) {
Text(text = message, Modifier.align(Alignment.CenterHorizontally))
}
Surface(modifier = Modifier
.width(500.dp)
.height(250.dp)) {
Text(text = message, Modifier.align(Alignment.CenterHorizontally))
}
}
}
}
I tried adding .FillMaxWidth() as a modifier to text, but it didn't help.
You can not align directly on a surface. You have to wrap your content with Column, Row, Box, etc.
You can change your code like the following
#Composable
fun MainScreen(message: String) {
Surface {
Column {
Surface(
modifier = Modifier
.width(500.dp)
.height(250.dp),
color = Color.Green
) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(text = message)
}
}
Surface(
modifier = Modifier
.width(500.dp)
.height(250.dp),
color = Color.Blue
) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
text = message
)
}
}
}
}
}
Using compose, I want to create something like this :
Problem is using compose AlertDialog I only achieve to get this :
There is a padding between the AlertDialog border and the title that can not be removed. How to remove it using Compose ?
Using Modifier.padding(all = 0.dp) doesn't work. I tried it at every level.
Properties "title" and "text" are wrapped with AlertDialogBaselineLayout that is adding padding. You can't remove it.
I have made a custom Composable that is acting as an AlertDialog and doesn't use AlertDialogBaselineLayout.
You can copy/paste it and edit it according to your needs :
NoPaddingAlertDialog :
#Composable
fun NoPaddingAlertDialog(
onDismissRequest: () -> Unit,
modifier: Modifier = Modifier,
title: #Composable (() -> Unit)? = null,
text: #Composable (() -> Unit)? = null,
confirmButton: #Composable () -> Unit,
dismissButton: #Composable (() -> Unit)? = null,
shape: Shape = MaterialTheme.shapes.medium,
backgroundColor: Color = MaterialTheme.colors.surface,
contentColor: Color = contentColorFor(backgroundColor),
properties: DialogProperties = DialogProperties()
) {
Dialog(
onDismissRequest = onDismissRequest,
properties = properties
) {
Surface(
modifier = modifier,
shape = shape,
color = backgroundColor,
contentColor = contentColor
) {
Column(
modifier = Modifier
.fillMaxWidth()
) {
title?.let {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) {
val textStyle = MaterialTheme.typography.subtitle1
ProvideTextStyle(textStyle, it)
}
}
text?.let {
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) {
val textStyle = MaterialTheme.typography.subtitle1
ProvideTextStyle(textStyle, it)
}
}
Box(
Modifier
.fillMaxWidth()
.padding(all = 8.dp)
) {
Row(
horizontalArrangement = Arrangement.End,
modifier = Modifier.fillMaxWidth()
) {
dismissButton?.invoke()
confirmButton()
}
}
}
}
}
}
You can then easily use it this way :
#Composable
fun MyCustomAlertDialog(openDialog: MutableState<Boolean> = mutableStateOf(true)) {
if (openDialog.value) {
NoPaddingAlertDialog(
title = {
Surface(
color = Color.Blue,
contentColor = Color.White,
modifier = Modifier
.fillMaxWidth()
) {
Text(
text = " Popup Titleď¸",
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 8.dp, vertical = 16.dp),
)
}
},
text = {
Column(Modifier.fillMaxWidth()) {
OutlinedTextField(
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(horizontal = 8.dp, vertical = 16.dp)
.fillMaxWidth(),
value = "Alert Dialog content ...",
onValueChange = { },
label = { Text(text = "Content") },
)
}
},
onDismissRequest = { /*TODO*/ },
confirmButton = {
PopupButton(title = "Ok", setShow = {
openDialog.value = false
})
},
dismissButton = {
PopupButton(
title = "Cancel",
setShow = {
openDialog.value = false
}
)
}
)
}
}
And get this :
You can use a Dialog() instead of AlertDialog(). It allows you to pass in a composable as the content parameter, so you can set whatever padding you want. Just don't forget to set the background color in the root composable of the content, since it is transparent by default.
I am trying to put a TextField and a FAB inside a bottomBar using Jetpack Compose.
I wrapped the two with a box, which has the modifier "fillMaxWidth".
But the two controls dont use the full width.
Does anyone know, how to fix this issue?
Here is my Code:
#Composable
fun ChatView() {
Scaffold(
topBar= { ChannelButton() },
bottomBar = { ChatBox() },
modifier = Modifier
.padding(10.dp)
) {
ChatList()
}
}
#Composable
fun ChatBox() {
Box(modifier = Modifier
.background(DiscordDarkGray)
.fillMaxWidth()
){
Column(modifier = Modifier
.padding(10.dp)
.fillMaxWidth()) {
HorizontalCenteredRow(modifier = Modifier
.fillMaxWidth()) {
val textState = remember { mutableStateOf(TextFieldValue()) }
TextField(
value = textState.value,
onValueChange = { textState.value = it }
)
Spacer(modifier = Modifier.width(10.dp))
FloatingIconActionButton (
icon = Icons.Default.Send,
onClick = { /*TODO*/ },
backgroundColor = DiscordBlue
)
}
Spacer(modifier = Modifier.height(60.dp))
}
}
}
Here is the Code of the HorizontalCenteredRow:
#Composable
fun HorizontalCenteredRow(
modifier: Modifier = Modifier,
content: #Composable RowScope.() -> Unit
) {
Row(
modifier = modifier
.wrapContentSize(Alignment.Center),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center,
content = content
)
}
Here is the code of the FAB:
#Composable
fun FloatingIconActionButton(
onClick: () -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
shape: Shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)),
backgroundColor: Color = MaterialTheme.colors.secondary,
contentColor: Color = contentColorFor(backgroundColor),
elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation(),
icon: ImageVector = Icons.Default.QuestionAnswer,
iconContentDescription: String = "",
) {
Surface(
modifier = modifier.let {
if (enabled) {
it.clickable(
onClick = onClick,
role = Role.Button,
interactionSource = interactionSource,
indication = null
)
} else it
},
shape = shape,
color = backgroundColor,
contentColor = contentColor,
elevation = elevation.elevation(interactionSource).value
) {
CompositionLocalProvider(LocalContentAlpha provides contentColor.alpha) {
ProvideTextStyle(MaterialTheme.typography.button) {
Box(
modifier = Modifier
.defaultMinSize(minWidth = FabSize, minHeight = FabSize)
.indication(interactionSource, rememberRipple()),
contentAlignment = Alignment.Center
) {
Icon(
icon,
iconContentDescription,
tint = if (enabled) {
colors().onPrimary
} else {
colors().onPrimary.transparentize(.6f)
}
)
}
}
}
}
}
Using
HorizontalCenteredRow(
modifier = Modifier.fillMaxWidth()
the Row fills all the space, but it doesn't mean that the children occupy the entire space.
If you want to fill all the space with the children you have to change the default dimensions of them.
For example you can apply modifier = Modifier.weight(1f) to the TextField.
Something like:
HorizontalCenteredRow(modifier = Modifier
.fillMaxWidth()) {
val textState = remember { mutableStateOf(TextFieldValue()) }
TextField(
value = textState.value,
onValueChange = { textState.value = it },
modifier = Modifier.weight(1f)
)
As can be seen in gif
when Column that contains of Text, Spacer, and LazyRowForIndexed is touched ripple is not propagating with circular motion. And it gets touched effect even when horizontal list is touched.
#Composable
fun Chip(modifier: Modifier = Modifier, text: String) {
Card(
modifier = modifier,
border = BorderStroke(color = Color.Black, width = Dp.Hairline),
shape = RoundedCornerShape(8.dp)
) {
Row(
modifier = Modifier.padding(start = 8.dp, top = 4.dp, end = 8.dp, bottom = 4.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier.preferredSize(16.dp, 16.dp)
.background(color = MaterialTheme.colors.secondary)
)
Spacer(Modifier.preferredWidth(4.dp))
Text(text = text)
}
}
}
#Composable
fun TutorialSectionCard(model: TutorialSectionModel) {
Column(
modifier = Modifier
.padding(top = 8.dp)
.clickable(onClick = { /* Ignoring onClick */ })
.padding(16.dp)
) {
Text(text = model.title, fontWeight = FontWeight.Bold, style = MaterialTheme.typography.h6)
Spacer(Modifier.preferredHeight(8.dp))
Providers(AmbientContentAlpha provides ContentAlpha.medium) {
Text(model.description, style = MaterialTheme.typography.body2)
}
Spacer(Modifier.preferredHeight(16.dp))
LazyRowForIndexed(items = model.tags) { _: Int, item: String ->
Chip(text = item)
Spacer(Modifier.preferredWidth(4.dp))
}
}
}
#Preview
#Composable
fun TutorialSectionCardPreview() {
val model = TutorialSectionModel(
clazz = MainActivity::class.java,
title = "1-1 Column/Row Basics",
description = "Create Rows and Columns that adds elements in vertical order",
tags = listOf("Jetpack", "Compose", "Rows", "Columns", "Layouts", "Text", "Modifier")
)
Column {
TutorialSectionCard(model)
TutorialSectionCard(model)
TutorialSectionCard(model)
}
}
What should be done to have circular effect, but not when list itself or an item from list is touched, or scrolled?
You have to apply a Theme to your composable, which in turn provides a default ripple factory, or you have to set the ripple explicitly:
#Preview
#Composable
fun TutorialSectionCardPreview() {
MaterialTheme() {
Column {
TutorialSectionCard
...
}
}
}
or
Column(
modifier = Modifier
.padding(top = 8.dp)
.clickable(
onClick = { /* Ignoring onClick */ },
indication = rememberRipple(bounded = true)
)
.padding(16.dp)
) {
// content
}
(As of compose version 1.0.0-alpha09 there seems to be no way to prevent the ripple from showing when content is scrolled)
I'm using this approach:
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(
color = Color.Black
),
onClick = {
}
)
I also figured out how to keep ripple only for the card not the scrollable list that contains tags. To prevent ripple only move through cards use a Box which places it's children as a stack, and add clickable to section that contains header and text.
#Composable
fun TutorialSectionCard(
model: TutorialSectionModel,
onClick: ((TutorialSectionModel) -> Unit)? = null
) {
Card(
modifier = Modifier.padding(vertical = 3.dp, horizontal = 8.dp),
elevation = 1.dp,
shape = RoundedCornerShape(8.dp)
) {
Box(
contentAlignment = Alignment.BottomStart
) {
TutorialContentComponent(onClick, model)
TutorialTagsComponent(model)
}
}
}
#Composable
private fun TutorialContentComponent(
onClick: ((TutorialSectionModel) -> Unit)?,
model: TutorialSectionModel
) {
Column(Modifier
.clickable(
onClick = { onClick?.invoke(model) }
)
.padding(16.dp)
) {
Text(
text = model.title,
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.h6
)
// Vertical spacing
Spacer(Modifier.height(8.dp))
// Description text
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text(model.description, style = MaterialTheme.typography.body2)
}
// Vertical spacing
Spacer(Modifier.height(36.dp))
}
}
#Composable
private fun TutorialTagsComponent(model: TutorialSectionModel) {
Column(Modifier.padding(12.dp)) {
// Horizontal list for tags
LazyRow(content = {
items(model.tags) { tag ->
TutorialChip(text = tag)
Spacer(Modifier.width(8.dp))
}
})
}
}