I want the AnnotatedString in the Box Composable below to display in the middle of the screen and centered, but the text is aligned to the left, although it displays in the middle of the screen. How can I fix this?
Box(
modifier = Modifier.fillMaxSize(),
) {
val emptyMessage = AnnotatedString(
text = "You don't have any items in this list. Click the ",
spanStyle = SpanStyle(Color.Gray)
).plus(
AnnotatedString(
text = "+ ",
spanStyle = SpanStyle(Color.Gray, fontSize = 18.sp),
)
).plus(
AnnotatedString(
text = "button below to add an item.",
spanStyle = SpanStyle(Color.Gray)
)
)
Text(
modifier = Modifier.align(Alignment.Center),
text = emptyMessage,
)
}
The Following property has to be used:
TextAlign = TextAlign.Center
Box(
modifier = Modifier.fillMaxSize(),
) {
val emptyMessage = AnnotatedString(
text = "You don't have any items in this list. Click the ",
spanStyle = SpanStyle(Color.Gray, fontSize = 18.sp)
).plus(
AnnotatedString(
text = "+ ",
spanStyle = SpanStyle(Color.Gray, fontSize = 23.sp),
)
).plus(
AnnotatedString(
text = "button to add an item.",
spanStyle = SpanStyle(Color.Gray, fontSize = 18.sp)
)
)
Text(
modifier = Modifier.align(Alignment.Center).padding(10.dp),
textAlign = TextAlign.Center,
text = emptyMessage,
)
}
Related
I have vertical pager and text composable that expands by click and scrollable modifier when expanded, text is located at bottom of screen and sometimes conflicts with pager scroll
How can i fix that? I tried to enable scroll only when text overlaps but this didn't work in my case
So question is how to enable scroll only when text is overflow ?
this is my description widget
#Composable
private fun ExpandableDescription(
modifier: Modifier = Modifier,
isExpanded: Boolean,
description: String,
prices: AnnotatedString,
onClick: () -> Unit,
) {
val isScrollEnabled = remember(isExpanded) { mutableStateOf(false) }
val scrollModifier =
Modifier.verticalScroll(rememberScrollState(), enabled = isScrollEnabled.value)
Box(
modifier = Modifier
.animateContentSize()
.then(modifier)
.then(scrollModifier)
.clickable { onClick() }
) {
Column {
Text(
text = description + description + description + description + description,
color = CryptoTheme.colors.primaryText,
fontSize = 14.sp,
onTextLayout = {
Timber.v("hasVisual=${it.hasVisualOverflow}")
isScrollEnabled.value = it.hasVisualOverflow
}
)
Text(modifier = Modifier.padding(top = 10.dp),
text = prices,
lineHeight = 28.sp,
onTextLayout = {
Timber.v("hasVisual2=${it.hasVisualOverflow}")
isScrollEnabled.value = it.hasVisualOverflow
}
)
}
TransparentField(
Modifier
.align(Alignment.BottomCenter)
.alpha(if (isExpanded) 0f else 1f)
)
}
}
I want to design a layout like this in Compose. In my case both the components have a variable width. But I still want the title to be centered and the icon to be right aligned.
I tried using Row but if the title or the button changes, alignment becomes unstable.
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
){
Spacer(modifier = Modifier.width(1.dp))
Text("Title Comes here")
TextButton(onClick = { }) { Text(text = "Dynamic Button") }
}
You can use a Box instead of a Row
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center,
){
Text(
text = "Title Comes here",
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center
)
TextButton(
onClick = { },
modifier = Modifier.align(CenterEnd)
){
Text(text = "Button")
}
}
If you have a more complex logic you can use the Layout composable:
Layout(
content = {
//Title
Text(text = "Title Comes here")
//TextButton or IconButton
TextButton(onClick = { } ){
Text(text = "Button")
}
IconButton(onClick = { } ){
Icon(Icons.Filled.Add,"contentButton")
}
},
measurePolicy = { measurables, constraints ->
val title = measurables[0].measure(constraints)
val textButton = measurables[1].measure(constraints)
val iconButton = measurables[2].measure(constraints)
val endPadding = 8.dp.toPx().toInt()
//Use your custom logic
//If the title + text button are overlapping the TextButton is replaced by a IconButton
var isOverlapping = false
if (title.width/2 + textButton.width > constraints.maxWidth/2)
isOverlapping = true
val height = maxOf(
if (isOverlapping) iconButton.height else textButton.height,
title.height,
constraints.minHeight)
layout(
width = constraints.maxWidth,
height = height
) {
title.placeRelative(
constraints.maxWidth/2 - title.width/2,
height/2-title.height/2)
if (isOverlapping)
iconButton.placeRelative(
constraints.maxWidth - iconButton.width - endPadding,
height/2-iconButton.height/2)
else
textButton.placeRelative(
constraints.maxWidth - textButton.width - endPadding,
height/2-textButton.height/2)
}
}
)
I want two behavior.
[Working] If middle text is short, then heart icon should be next to middle text and cart should be at end,
see image.
[Not working] If middle text is large, then heart icon should stick beside carts left and middle text be ellipsis.
Note: I have tried Modifier.weight(1f,fill = false) for second behaviour but then first broke.
code
Row(
modifier = Modifier.fillMaxSize()
) {
Row(
modifier = Modifier.wrapContentWidth()
) {
Icon(Icons.Filled.Search,"")
Spacer(modifier = Modifier.width(18.dp))
Icon(Icons.Filled.Add,"")
Spacer(modifier = Modifier.width(12.dp))
Text(
text = "If text is long, then cart icon show at end with ellipsis text",
maxLines = 1,
modifier = Modifier
.weight(1f,fill = false)
)
Spacer(modifier = Modifier.width(12.dp))
Icon(Icons.Filled.Favorite,"")
}
Row(Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End) {
Icon(Icons.Filled.ShoppingCart,"")
}
}
You can wrap the Text and the Favorite Icon with a Row and apply to it the weight modifier to fill the available space.
Then assign weight(1f, fill = false) to the Text:
Row(
modifier = Modifier.fillMaxWidth().background(Color.Yellow),
) {
Icon(Icons.Filled.Search,"")
Spacer(modifier = Modifier.width(18.dp))
Icon(Icons.Filled.Add,"")
Row(Modifier.weight(1f)) {
Text(
text = "If text is",
maxLines = 1,
modifier = Modifier
.weight(1f, fill = false)
.padding(8.dp),
overflow = TextOverflow . Ellipsis
)
Icon(
Icons.Filled.Favorite, "",
)
}
Icon(Icons.Filled.ShoppingCart,"")
}
You center component should use the "left space" with weight attribute
modifier = Modifier
.weight(1f)
.padding(8.dp),
overflow = TextOverflow.Ellipsis
I'm trying to put a icon after the word skip
put it always appears in the top left corner.
Example: Skip>
Text(text = buildAnnotatedString {
append("")
val skipText = stringResource(id = R.string.skip)
withStyle(style = SpanStyle(color = MaterialTheme.colors.onBackground)
) {
append(skipText)
}
Icon(Icons.Filled.ChevronRight, "chevron right")
},
style = MaterialTheme.typography.body1,
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 50.dp)
)
You can use the inlineContent attribute to insert a Composable into text layout.
val inlineContent = mapOf(
Pair(
"icon",
InlineTextContent(
Placeholder(
width = 12.sp,
height = 12.sp,
placeholderVerticalAlign = PlaceholderVerticalAlign.AboveBaseline
)
) {
Icon(Icons.Filled.CheckCircle,"",tint = Color.Red)
}
)
)
Text(
text = buildAnnotatedString {
append("")
val skipText = "Skip text"
withStyle(style = SpanStyle(color = MaterialTheme.colors.onBackground)){
append(skipText)
}
appendInlineContent("icon", "[icon]")
},
style = MaterialTheme.typography.body1,
modifier = Modifier
.padding(bottom = 50.dp),
inlineContent = inlineContent
)
Is there a way to expand the BottomSheetScaffold to a specific height?
The content of my BottomSheetScaffold is a big list so it expands to fullscreen.
I did not find a way to always expand to a specific height, regardless of the content.
You can use heightIn(min = 100.dp, max = 500.dp) on sheetContent as
val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(
bottomSheetState = BottomSheetState(BottomSheetValue.Collapsed)
)
BottomSheetScaffold(
scaffoldState = bottomSheetScaffoldState,
sheetElevation = 8.dp,
sheetShape = RoundedCornerShape(
bottomStart = 0.dp,
bottomEnd = 0.dp,
topStart = 12.dp,
topEnd = 12.dp
),
sheetContent = {
SheetContent()
},
// This is the height in collapsed state
sheetPeekHeight = 70.dp
) {
MainContent(bottomSheetScaffoldState.bottomSheetState)
}
#Composable
private fun SheetContent() {
Column(modifier = Modifier.heightIn(min = 100.dp, max = 500.dp)) {
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "Places to Visit",
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold,
color = Color(0xffFDD835),
fontSize = 24.sp,
modifier = Modifier.padding(8.dp)
)
LazyColumn(
contentPadding = PaddingValues(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(places) { place ->
PlacesToBookVerticalComponent(place = place)
}
}
}
}
And if interested to see states and scroll positions you can check out them with
#ExperimentalMaterialApi
#Composable
private fun MainContent(bottomSheetState: BottomSheetState) {
val direction = bottomSheetState.direction
val currentValue: BottomSheetValue = bottomSheetState.currentValue
val targetValue: BottomSheetValue = bottomSheetState.targetValue
val overflow = bottomSheetState.overflow.value
val offset = bottomSheetState.offset.value
val progress = bottomSheetState.progress
val fraction = progress.fraction
val from = progress.from.name
val to = progress.to.name
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(0xff6D4C41))
.padding(top = 30.dp)
) {
Text(
color = Color.White,
text = "direction:$direction\n" +
"isExpanded: ${bottomSheetState.isExpanded}\n" +
"isCollapsed: ${bottomSheetState.isCollapsed}\n" +
"isAnimationRunning: ${bottomSheetState.isAnimationRunning}"
)
Text(
color = Color.White,
text = "currentValue: ${currentValue}\n" +
"targetValue: ${targetValue}\n" +
"overflow: ${overflow}\n" +
"offset: $offset"
)
Text(
color = Color.White,
text = "progress: $progress\n" +
"fraction: ${fraction}\n" +
"from: ${from}\n" +
"to: $to"
)
}
}
Bottom sheet wouldn't expand more than the content's height.
To constraint the content's maximum height you can just use some height modifier (e.g. heightIn(max = 300.dp) or height(300.dp)) on a root item of sheetContent in BottomSheetScaffold.
Of course you can use some predefined value or compute it based on the scaffold size.