How to align title at layout center in TopAppBar? - android

TopAppBar(
backgroundColor = Color.Transparent,
elevation = 0.dp,
modifier= Modifier.fillMaxWidth(),
navigationIcon = {
IconButton(
onClick = { TODO },
enabled = true,
) {
Icon(
painter = painterResource(id = R.drawable.icon_back_arrow),
contentDescription = "Back",
)
}
}
},
title = {
Text(
modifier = if (action == null) Modifier.fillMaxWidth() else Modifier,
textAlign = if (action == null) TextAlign.Center else TextAlign.Start,
maxLines = 1,
text = "Hello"
)
},
actions = {
action?.run {
Text(
modifier = Modifier
.padding(horizontal = 16.dp)
.clickable(onClick = TODO),
color = Color.Green,
text ="Cancel",
)
}
}
I'm new in jetpack and want to align title of TopAppBar at center if action is null. Title is not align center of layout. when there is no navigationIcon it work but adding navigationIcon it show slightly right. How can I do it to make title text at center of layout.

With Material2 you have to use the other constructor of TopAppBar that has no pre-defined slots for content, allowing you to customize the layout of content inside.
You can do something like:
val appBarHorizontalPadding = 4.dp
val titleIconModifier = Modifier.fillMaxHeight()
.width(72.dp - appBarHorizontalPadding)
TopAppBar(
backgroundColor = Color.Transparent,
elevation = 0.dp,
modifier= Modifier.fillMaxWidth()) {
//TopAppBar Content
Box(Modifier.height(32.dp)) {
//Navigation Icon
Row(titleIconModifier, verticalAlignment = Alignment.CenterVertically) {
CompositionLocalProvider(
LocalContentAlpha provides ContentAlpha.high,
) {
IconButton(
onClick = { },
enabled = true,
) {
Icon(
painter = painterResource(id = R.drawable.ic_add_24px),
contentDescription = "Back",
)
}
}
}
//Title
Row(Modifier.fillMaxSize(),
verticalAlignment = Alignment.CenterVertically) {
ProvideTextStyle(value = MaterialTheme.typography.h6) {
CompositionLocalProvider(
LocalContentAlpha provides ContentAlpha.high,
){
Text(
modifier = Modifier.fillMaxWidth(),
textAlign = TextAlign.Center,
maxLines = 1,
text = "Hello"
)
}
}
}
//actions
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Row(
Modifier.fillMaxHeight(),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically,
content = actions
)
}
}
}
With Material3 you can simply use the CenterAlignedTopAppBar:
CenterAlignedTopAppBar(
title = { Text("Centered TopAppBar") },
navigationIcon = {
IconButton(onClick = { /* doSomething() */ }) {
Icon(
imageVector = Icons.Filled.Menu,
contentDescription = "Localized description"
)
}
}
)

If you're using Material3, you can use the CenterAlignedTopAppBar as well.
fun CenterAlignedTopAppBar(
title: #Composable () -> Unit,
modifier: Modifier = Modifier,
navigationIcon: #Composable () -> Unit = {},
actions: #Composable RowScope.() -> Unit = {},
colors: TopAppBarColors = TopAppBarDefaults.centerAlignedTopAppBarColors(),
scrollBehavior: TopAppBarScrollBehavior? = null
) {
SingleRowTopAppBar(
modifier = modifier,
title = title,
titleTextStyle =
MaterialTheme.typography.fromToken(TopAppBarSmallTokens.HeadlineFont),
centeredTitle = true,
navigationIcon = navigationIcon,
actions = actions,
colors = colors,
scrollBehavior = scrollBehavior
)
}

EDIT: the old answer is out of date, please use CenterAlignedTopAppBar
CenterAlignedTopAppBar(
title = { Text(text = stringResource(id = titleRes)) },
actions = {
IconButton(onClick = onActionClick) {
Icon(
imageVector = actionIcon,
contentDescription = actionIconContentDescription,
tint = MaterialTheme.colorScheme.onSurface
)
}
},
colors = colors,
modifier = modifier,
)
OLD ANSWER:
I redid the native implementation a bit.
Need to do just two things:
1.Add this file to your project. This is a slightly modified implementation of the TopAppBar class.
https://gist.github.com/evansgelist/aadcd633e9b160f9f634c16e99ffe163
2.Replace in your code TopAppBar to CenterTopAppBar. And it's all!
Scaffold(
topBar = {
CenterTopAppBar(
title = {
Text(
textAlign = TextAlign.Center,
text = text,
)
},
EDIT
extension's code
val Number.toPx
get() = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
this.toFloat(),
Resources.getSystem().displayMetrics
)
Result

The core of the title centering is that the space of the left and right occupying slots is the same. You only need to adjust the default size of the slots. We give the left and right slots the default space occupying slots, which can solve this problem well, and the code is simple.
#Composable
fun TopBar(title: Int, actions: #Composable (() -> Unit)? = null, popOnClick: () -> Unit) {
val modifier = Modifier.size(width = 70.dp, height = 50.dp).background(Color.Red)
TopAppBar(
title = {
Text(text = stringResource(id = title), fontSize = 16.sp,
textAlign = TextAlign.Center, modifier = Modifier.fillMaxWidth()) },
navigationIcon = {
Box(modifier = modifier, contentAlignment = Alignment.Center) {
IconButton(onClick = { popOnClick() }) {
Icon(Icons.Filled.ArrowBack, contentDescription = "", tint = MaterialTheme.colors.primary)
}
}
},
actions = {
Box(modifier = modifier, contentAlignment = Alignment.Center) {
if (actions != null) {
actions()
}
}
},
backgroundColor = MaterialTheme.colors.surface,
elevation = 1.dp
)
}

It depends what's in the appbar.
If you only have the title, then you could do the following:
topBar = {
TopAppBar(content = {
Text(
modifier = Modifier.fillMaxWidth(),
text = "Title Text",
textAlign = TextAlign.Center,
style = MaterialTheme.typography.h6,
)
})
},
If you have an icon either side you should be able to do the same, may have to adjust things if have two icons one side and one the other, then may want to use add a same sized icon to even things out and put enabled to false to it's not clickable and color to transparent so it's not seen, otherwise you could try and figure out the size and add padding end to the text, it seems you also need to add 16.dp padding to the transparent icon, as the navigation icon is given extra padding before the title but not between title and actions
Here's what I did for the arrowIcon and title
topBar = {
TopAppBar(
navigationIcon = {
IconButton(
onClick = {
navController.popBackStack()
}
) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = "back arrow icon"
)
}
},
title = {
Text(
modifier = Modifier
.fillMaxWidth(),
text = "Basic Navigation",
textAlign = TextAlign.Center,
)
},
actions = {
IconButton(
modifier = Modifier
.padding(start = 16.dp),
enabled = false,
onClick = {}
) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = "back arrow icon",
tint = Color.Transparent
)
}
}
)
}

I'm just a begginer with Jetpack Compose and before I searched for a solution of that problem I tried to figure my own, maybe it will be enough for someone. I needed centered title for TopAppBar with navigation icon on the left side only or with two icons on the left and right side, this solution was OK for me. Later I configurated that for including passed icon on the right side or no.
TopAppBar(
backgroundColor = Color.Green,
elevation = 5.dp,
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
// size of an icon and placeholder box on the right side
val iconSize = 32.dp
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = "Arrow Back",
modifier = Modifier
.clickable {
navController.popBackStack()
}
.size(iconSize)
)
Text(text = "Centered Title", fontSize = 32.sp)
// placeholder on the right side in order to have centered title - might be replaced with icon
Box(
modifier = Modifier
.size(iconSize)
) { }
}
I can't attach screenshot, preview is here: https://i.stack.imgur.com/UNQTF.png

I found a good workaround if you're using Material 2.
Create a Row, that contains:
Column with weight 1, containing an Icon
Column with weight 1, containing Header
Spacer with weight 1
The device's screen will automatically divide into 3 equal sections.
Here is a code sample:
Scaffold(
topBar = {
TopAppBar(
backgroundColor = Transparent,
elevation = 0.dp,
modifier = Modifier.fillMaxWidth()
) {
/** TopAppBar Content */
Row(
modifier = Modifier.height(51.dp).fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
/** Icon*/
Column(modifier = Modifier.weight(1f).padding(start = 9.dp)) {
IconButton(
onClick = { /*TODO*/ },
modifier = Modifier
.width(22.dp)
.height(22.dp),
enabled = true,
) {
Icon(
painter = painterResource(id = R.drawable.ic_baseline_arrow_back),
contentDescription = "Back",
tint = ThemeBlue
)
}
}
/** Header */
Column(modifier = Modifier.weight(1f)) {
Text(
modifier = Modifier.fillMaxWidth(),
style = MaterialTheme.typography.h1,
textAlign = TextAlign.Center,
maxLines = 1,
text = "Header",
)
}
/*TODO
* When the stable version of Material 3 comes out
* we should replace the whole TopAppBar with CenterAlignedTopAppBar.
* For now, this Spacer keeps the Header component in the center of the topBar
*/
Spacer(modifier = Modifier.weight(1f))
}
}
},
content = {
Divider(color = ThemeBlue, thickness = 0.5.dp)
}
)
Here is a screenshot of the result this code produces:

Use fillMaxWidth() to let the Text View occupy AppBar width
Use wrapContentWidth(align = Alignment.CenterHorizontally) to align the Text Title.
TopAppBar(
title = {
Text(
text = screenname,
modifier = Modifier
.fillMaxWidth()
.wrapContentWidth(align= Alignment.CenterHorizontally)
)
},
modifier = modifier
)

title = {
Row(
modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically
) {
Text(
stringResource(R.string.app_title).uppercase(),
style = MaterialTheme.typography.h1
)
Spacer(modifier.width(1.dp))
}
},

#Composable
fun TopAppBarCompose(){
TopAppBar(
title = {
Box(modifier = Modifier.fillMaxWidth()) {
Text(
text = "Hello",
fontSize = 30.sp,
modifier = Modifier.align(Alignment.Center)
)
}
},
)
}

The previous solutions are too complex. It is actually quite simple:
title = {
Text(
text = "title",
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}

Related

Aligning multiple texts center if one is null

I want to align two texts to be centered vertically to the start of the TaskCard. It works as intended if there are two texts present. However, if there is only one and the other one is null the null one will completely disappear, but will still leave a blank space in its place. How do I get rid of it so I can properly align the text?
#Composable
fun TaskCard(
modifier: Modifier = Modifier,
task: Task,
viewModel: TaskListViewModel = hiltViewModel()
) {
Card(
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant
), modifier = modifier.padding()
) {
Surface(
color = MaterialTheme.colorScheme.surfaceVariant, modifier = modifier
) {
Row(
verticalAlignment = Alignment.CenterVertically, modifier = modifier.padding(12.dp)
) {
Column(
modifier = modifier
.weight(1f)
.padding(12.dp)
) {
task.categoryName?.let {
Text(
text = it,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.primary
)
}
Text(
text = task.taskName,
style = MaterialTheme.typography.headlineSmall,
color = MaterialTheme.colorScheme.onBackground,
)
}
IconButton(
onClick = { viewModel.onEvent(TaskListEvent.OnEditTask(task)) },
modifier = modifier.size(36.dp)
) {
Icon(
imageVector = Icons.Rounded.Edit,
contentDescription = "Edit",
modifier = modifier.padding(horizontal = 2.dp),
tint = MaterialTheme.colorScheme.onBackground
)
}
IconButton(
onClick = { }, modifier = modifier.size(36.dp)
) {
Icon(
imageVector = Icons.Rounded.Notifications,
contentDescription = "Notifications",
modifier = modifier.padding(horizontal = 2.dp),
tint = MaterialTheme.colorScheme.onBackground
)
}
}
}
}
}
As Gabriele Mariotti suggested I've changed the condition under which the code is executed.
from:
task.categoryName?.let
to:
if (!task.categoryName.isNullOrEmpty())

How to change TopAppBar height when using Scaffold under Material 3

I am using the Material 3 Scaffold with the material 3 TopAppBar. I need to change the height of TopAppBar but cannot use MediumTopAppBar as it's too heightened. Is there any way to do the same?
Here is the code
val scrollBehaviour = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
Scaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
TopAppBar(
title = {
Text(
text = "About",
modifier = Modifier
.padding(start = 30.dp, bottom = 12.dp)
.wrapContentSize(),
color = colorResource(id = R.color.black)
)
},
colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = Color.Red,
titleContentColor = Color.Black,
),
modifier = Modifier.fillMaxWidth(),
navigationIcon = {
Button(onClick = { }) {
Text(text = "B1")
}
}, actions = {
Button(onClick = {}) {
Text(text = "B2")
}
},
scrollBehavior = scrollBehaviour
)
},
content = {
}
)

In jetpack compose how to add numbers(counts) near icons(like,comment)

how to add numbers near the icon in android jetapck compose, i have attached snap below like that.please help me to solve the problem.
You can use this code below:
#Composable
fun IconsRow() {
// Update the texts according to your logic
var favoriteText by remember { mutableStateOf("34") }
var repeatText by remember { mutableStateOf("12") }
var chatText by remember { mutableStateOf("12") }
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceEvenly
) {
IconWithNearbyText(
text = chatText,
icon = Icons.Outlined.ChatBubbleOutline
)
IconWithNearbyText(
text = repeatText,
icon = Icons.Outlined.Repeat
)
IconWithNearbyText(
text = favoriteText,
icon = Icons.Outlined.FavoriteBorder
)
}
}
}
#Composable
fun IconWithNearbyText(
text: String,
icon: ImageVector
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(2.dp)
) {
Icon(imageVector = icon, contentDescription = null)
Text(text = text)
}
}
You can use the BadgedBox.
Use the offset modifier to change the position of the badge, and the containerColor to change the background color.
In M2 the background color is defined by the backgroundColor attribute.
BadgedBox(
badge = {
Badge(
modifier = Modifier.offset(y=10.dp),
containerColor=LightGray
){
val badgeNumber = "8"
Text(
badgeNumber,
modifier = Modifier.semantics {
contentDescription = "$badgeNumber new notifications"
}
)
}
}) {
Icon(
Icons.Filled.Star,
contentDescription = "Favorite"
)
}

Clickable function of composable does not work anymore

So these are my Project Versions:
val compose_version by extra("1.0.0-beta07")
val kotlin_version by extra("1.4.32")
val hilt_version by extra("2.36")
So I have a LazyColum which displays a custom composable card list:
package com.veloce.montageservice.ui.composables
// Here are imports but its too much code for stackoverflow
#Composable
fun SimpleCard(
imageVector: ImageVector,
title: String,
description: String,
#DrawableRes trailingButton: Int? = null,
clickable: () -> Unit
) {
Card(
modifier = Modifier
.padding(8.dp)
.fillMaxWidth()
.clickable {
clickable()
},
border = BorderStroke(
(0.5).dp,
brush = Brush.horizontalGradient(
colors = listOf(
MaterialTheme.colors.primary, MaterialTheme.colors.primaryVariant
)
)
),
elevation = 5.dp
// backgroundColor = Color.Gray
) {
Row(
modifier = Modifier.padding(8.dp),
horizontalArrangement = if (trailingButton != null) Arrangement.SpaceEvenly else Arrangement.Start
) {
Surface(
shape = CircleShape,
modifier = Modifier.size(50.dp),
color = MaterialTheme.colors.primaryVariant
) {
Icon(
modifier = Modifier.padding(4.dp),
imageVector = imageVector,
contentDescription = "Icon of a building",
tint = Color.White
)
}
Column(
modifier = Modifier.padding(8.dp),
verticalArrangement = Arrangement.SpaceEvenly
) {
Text(
title,
fontWeight = FontWeight.Bold
)
Text(description)
}
trailingButton?.let {
Surface(
shape = CircleShape,
modifier = Modifier.size(35.dp),
color = MaterialTheme.colors.primaryVariant
) {
Icon(
modifier = Modifier.padding(4.dp),
imageVector = ImageVector.vectorResource(id = it),
contentDescription = "Icon of a building",
tint = Color.White
)
}
}
}
}
}
And this should be clickable. It was, but now it's not anymore and I didn't have changed anything in this composable.
Has anyone an idea what went wrong? The scroll function of LazyColumnworks perfectly fine, it's just that I can't click it anymore from one day to the other.
In compose 1.0.0-beta08 there was a breaking API Change (https://developer.android.com/jetpack/androidx/releases/compose-material) which causes the clickable modifier to be ignored. You have to use the onClickparameter of Card instead:
Card(onClick = { count++ }) {
Text("Clickable card content with count: $count")
}

How to get Image with text on top/infront? (Google Classroom Home Image Format)

I want to make an Image with Text on top, just like Google CLassroom. But first I want to test Image and then Text. Instead, I got the image overlapping the text. Image Overlapping text
Then I move the Image code after the text. How to get simple G classroom format
Text then Image
#Composable
fun ClassImage(
// icon: VectorAsset,
// label: String,
// modifier: Modifier = Modifier
) {
val imageAlpha = 1f
Surface(
modifier = Modifier
.padding(start = 8.dp, top = 8.dp, end = 8.dp)
.fillMaxWidth(),
color = colors.primary.copy(alpha = 0.12f)
) {
TextButton(
onClick = {},
modifier = Modifier.fillMaxWidth()
) {
Row(
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth())
{
Image(
imageResource(id = R.drawable.class1),
alpha = imageAlpha
)
Column {
Text("Alfred Sisley", fontWeight = FontWeight.Bold)
ProvideEmphasis(emphasis = EmphasisAmbient.current.medium) {
Text("3 minutes ago", style = MaterialTheme.typography.body2)
}
}
}
}
}
}
To put Text on top of the Image you can use Box, which is similar to old FrameLayout.
I'm not sure want you wanted to achieve, but if smth like this:
Then you can do it this way:
Surface(
shape = RoundedCornerShape(8.dp),
modifier = Modifier
.preferredHeight(128.dp)
.clickable(onClick = {})
) {
Box {
Image(
vectorResource(id = R.drawable.ic_launcher_background),
alpha = imageAlpha,
contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize()
)
Column(modifier = Modifier.padding(16.dp)) {
Text(
"Alfred Sisley",
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.h6)
ProvideEmphasis(emphasis = EmphasisAmbient.current.medium) {
Text("3 minutes ago", style = MaterialTheme.typography.body2)
}
Spacer(modifier = Modifier.weight(1f))
Text(text = "Footer", style = MaterialTheme.typography.body1)
}
}
}
With 1.0.0-beta02 you can use a Box as parent container.
Something like:
Box(modifier = Modifier.height(IntrinsicSize.Max))
{
Image(
painterResource(id = R.drawable.xx),
"contentDescription",
alpha = 0.8f,
modifier = Modifier.requiredHeight(100.dp)
)
Column(
modifier = Modifier.fillMaxHeight(),
verticalArrangement = Arrangement.Bottom) {
Text("Alfred Sisley",
fontWeight = FontWeight.Bold)
CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.medium) {
Text("3 minutes ago", style = MaterialTheme.typography.body2)
}
}
}

Categories

Resources