Jetpack Compose LargeTopAppBar Displays Title Twice - android

I'm trying to build a Jetpack Compose app with Scaffold and a LargeTopAppBar. I currently have a very simple UI with only the LargeTopAppBar in a Scaffold, but when I run my app I see two small titles at the top of the screen.
Any ideas why this is happening or how to fix it? My activity code is as follows
#OptIn(ExperimentalMaterial3Api::class)
class MainActivity : MonetCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycleScope.launchWhenCreated {
monet.awaitMonetReady()
setContent {
TVTimeTheme(monetCompat = monet) {
val decayAnimationSpec = rememberSplineBasedDecay<Float>()
val topAppBarScrollState = rememberTopAppBarScrollState()
val scrollBehavior = remember(decayAnimationSpec) {
TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
decayAnimationSpec, topAppBarScrollState
)
}
Scaffold (
topBar = {
LargeTopAppBar(
title = { Text(text = "movies") },
scrollBehavior = scrollBehavior
)
}
) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding))
}
}
}
}
}
}

These helped me:
Use only material3 components (androidx.compose.material3.*) in TabBar, not material (androidx.compose.material.*) components
Remove defaultTextColor of titleLarge and bodyLarge in your typography

I ran across this issue when I set the color within the Text composable, instead of the titleContentColor associated with the TopAppBar composable.
I was doing this:
MediumTopAppBar(
title = {
Text(
text = "Example",
color = MaterialTheme.colorScheme.secondary
)
},
...
)
Instead, use the titleContentColor field on the MediumTopAppBar composable itself. That looks like this:
MediumTopAppBar(
title = {
Text(
text = "Example",
)
},
colors = TopAppBarDefaults.largeTopAppBarColors(
titleContentColor = MaterialTheme.colorScheme.secondary
)
)
Easy mistake to make but stumped me for a good 10 minutes or so!

I think it's because you have not put your navigation icon yet. Try this:
Scaffold(
topBar = {
LargeTopAppBar(
title = { Text("movies") },
navigationIcon = {
IconButton(onClick = { }) {
Icon(
imageVector = Icons.Filled.Menu,
contentDescription = "menu icon"
)
}
},
actions = {
IconButton(onClick = { }) {
Icon(
imageVector = Icons.Filled.Favorite,
contentDescription = "favorite icon"
)
}
},
)
},
content = {innerPadding ->
Box(modifier = Modifier.padding(innerPadding))
}
)
Source

Just make sure that whether you use import androidx.compose.material3.Text instead of import androidx.compose.material.Text

Related

Why can't I use Modifier.padding(top = 20.dp) to increase space in AlertDialog when I use JetPack Compose?

The Code A displays a dialog box based AlertDialog, and I get Image A when I run Code A.
I find the space between title = { Text(text = dialogTitle) } and text = {...} is too closer in Image A.
So I set Modifier.padding(top = 100.dp) to wish to increase the space between the two controls, but I only get Image B, it seems that Modifier.padding(top = 100.dp) doesn't work as expected, how can I fix it?
Code A
#Composable
fun EditTextDialog(
isShow: Boolean,
onDismiss: () -> Unit,
onConfirm: (String) -> Unit,
saveTitle: String = stringResource(R.string.dialog_save_title),
cancelTitle:String = stringResource(R.string.dialog_cancel_title),
dialogTitle:String ="Edit",
editFieldContent:String ="",
) {
var mText by remember(editFieldContent){ mutableStateOf(editFieldContent) }
val cleanAndDismiss = {
mText = editFieldContent
onDismiss()
}
if (isShow) {
AlertDialog(
title = { Text(text = dialogTitle) },
text = {
Column(
Modifier.padding(top = 20.dp)
//Modifier.padding(top = 100.dp)
//Modifier.height(100.dp), //The same result as Image A
//verticalArrangement = Arrangement.Center
) {
TextField(
value = mText,
onValueChange = { mText = it }
)
}
},
confirmButton = {
TextButton(onClick = { onConfirm(mText) }) {
Text(text = saveTitle)
}
},
dismissButton = {
TextButton(onClick = cleanAndDismiss) {
Text(text = cancelTitle)
}
},
onDismissRequest = cleanAndDismiss
)
}
}
Image A
Image B
With M3 AlertDialog (androidx.compose.material3.AlertDialog) it works.
With M2 AlertDialog, one solution is to remove the title attribute and use the text attribute for the whole layout.
AlertDialog(
onDismissRequest = {},
text = {
Column(){
Text(text = "Title")
Spacer(Modifier.height(30.dp))
TextField(
value = "mText",
onValueChange = { },
)
}
},
//buttons..
)
I don't understand what you're trying to do. If you want more space between the TextField and the dialog buttons, then you don't want a top padding. You want padding below the TextField, so it would be bottom padding on the column.
Also, there's a chance that it won't work properly inside a Column, and you might have to switch it out for Box. And if that doesn't work for some reason, just add a spacer below the TextField:
Spacer(Modifier.height(20.dp).fillMaxWidth())
I assume you are using Material AlertDialog? If yes try using the Material3 variant. It should work then.
Just implement following library:
implementation "androidx.compose.material3:material3:1.0.0-beta02"
And make sure to use the Material3 AlertDialog Composable which is imported with the library.

TopAppBar and padding (Jetpack Compose)

I have a slightly different expectation for padding to work. How can this problem be solved? I see this only at TopAppBar.
CenterAlignedTopAppBar(
title = { Text(title) },
modifier = Modifier.statusBarsPadding(), // or padding(top = 24.dp)
actions = {
if (isSync) {
IconButton(
onClick = { },
enabled = false,
content = {
Icon(
painter = painterResource(R.drawable.ic_outline_cloud_download_24),
contentDescription = title,
tint = Color.DarkGray.copy(alpha = alpha)
)
}
)
}
},
scrollBehavior = scrollBehavior
)
UPD
For example, here it works as it should -> link
I made it myself based on the source code. Not quite universal, but just right for me. I will not give the full code, it is in the source code. I will give only the place where it is necessary to add.
#Composable
private fun TopAppBarLayout(...) {
Layout(
...,
modifier = modifier.statusBarsPadding()
) {
...
}
}
The rest of the code is identical to the original.
UPDATE 21
Now everything works out of the box.

Android Jetpack compose: BackdropScaffold background with GoogleMap corner issue

I'm using the new BackdropScaffold composable to make a similar looking screen like Google Map with a Map on the back and a list on the front. (See the image)
As you can see there is a problem with the corner around the front layer. Currently is displayed the surface under (pale blue). What I would like to achieve is having the Google Map shown in those corners. I tried to play with the size and padding of GoogleMap composable or the front panel but no luck.
UPDATE
The following example code shows the issue I'm facing. As you can see the BackdropScaffold background is correctly applied (RED). The corners of the front layer are transparent. The issue comes out when you have a different color in your background layer (BLUE). If the background layer contains a map you have the same issue.
BackdropScaffold is dividing the space but not overlaying any layer. The front layer should overlay a bit the back layer to fix this problem.
#OptIn(ExperimentalMaterialApi::class)
#Composable
internal fun test() {
val scope = rememberCoroutineScope()
val selection = remember { mutableStateOf(1) }
val scaffoldState = rememberBackdropScaffoldState(BackdropValue.Concealed)
val frontLayerHeightDp = LocalConfiguration.current.screenHeightDp / 3
LaunchedEffect(scaffoldState) {
scaffoldState.conceal()
}
BackdropScaffold(
scaffoldState = scaffoldState,
appBar = {
TopAppBar(
title = { Text("Backdrop scaffold") },
navigationIcon = {
if (scaffoldState.isConcealed) {
IconButton(onClick = { scope.launch { scaffoldState.reveal() } }) {
Icon(Icons.Default.Menu, contentDescription = "Localized description")
}
} else {
IconButton(onClick = { scope.launch { scaffoldState.conceal() } }) {
Icon(Icons.Default.Close, contentDescription = "Localized description")
}
}
},
actions = {
var clickCount by remember { mutableStateOf(0) }
IconButton(
onClick = {
// show snackbar as a suspend function
scope.launch {
scaffoldState.snackbarHostState
.showSnackbar("Snackbar #${++clickCount}")
}
}
) {
Icon(Icons.Default.Favorite, contentDescription = "Localized description")
}
},
elevation = 0.dp,
backgroundColor = Color.Transparent
)
},
backLayerContent = {
LazyColumn(modifier = Modifier.background(Color.Blue)) {
items(if (selection.value >= 3) 3 else 5) {
ListItem(
Modifier.clickable {
selection.value = it
scope.launch { scaffoldState.conceal() }
},
text = { Text("Select $it", color = Color.White) }
)
}
}
},
backLayerBackgroundColor = Color.Red,
frontLayerShape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp),
headerHeight = frontLayerHeightDp.dp,
frontLayerBackgroundColor = Color.White,
frontLayerContent = {
LazyColumn {
items(50) {
ListItem(
text = { Text("Item $it") },
icon = {
Icon(
Icons.Default.Favorite,
contentDescription = "Localized description"
)
}
)
}
}
}
)
}
BackdropScaffold is creating a Surface for from layer under the hood, when you create your own inside frontLayerContent it's displayed on top of built-in one.
Instead use frontLayerShape and frontLayerBackgroundColor parameters:
frontLayerShape = BottomSheetShape,
frontLayerBackgroundColor = Color.White,
frontLayerContent = {
LazyColumn(
modifier = Modifier.padding(16.dp)
) {
items(
items = moorings,
itemContent = { mooring ->
...
}
)
}
}
p.s. some comments about your code:
When you have modifier parameter, you should only apply it for the topmost container in your view - here you've applied it for content of frontLayerContent, which may cause unexpected behaviour.
You don't need to wrap LazyColumn in a Column - it has no effect when Column has only one child, and if you need to apply a modifier you can do it directly for LazyColumn

How to reduce horizontal space between navigation icon and title in a jetpack compose `TopAppBar`?

I'm making an app bar in jetpack compose but I'm having spacing issues between the navigation icon and the title.
This is my compose function:
#Composable
fun DetailsAppBar(coin: Coin, backAction: () -> Unit) {
TopAppBar(
navigationIcon = {
IconButton(onClick = { backAction() }) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = null
)
}
},
title = { Text(text = "${coin.rank}. ${coin.name} (${coin.symbol})") }
)
}
This is my preview function:
#Composable
#Preview
fun DetailsAppBarPreview() {
val bitcoin = Coin(
id = "",
isActive = true,
name = "Bitcoin",
rank = 1,
symbol = "BTC"
)
DetailsAppBar(coin = bitcoin, backAction = {})
}
This is the visual preview of my compose function:
This is the space I want to reduce:
Entering the code of the TopAppBar compose function I can't see any parameters that allow me to do this.
You are right. With the variant of TopAppBar you are using, this is not possible. This is because the width of the NavigationIcon is set to the default (72.dp - 4.dp). You can check the implementation of TopAppBar and see that it uses the below:
private val AppBarHorizontalPadding = 4.dp
// Start inset for the title when there is a navigation icon provided
private val TitleIconModifier = Modifier.fillMaxHeight()
.width(72.dp - AppBarHorizontalPadding)
What you could do is to use the other variant of the TopAppBar that gives you much more control in placing the title and icon. It could be something like:
#Composable
fun Toolbar(
#StringRes title: Int,
onNavigationUp: (() -> Unit)? = null,
) {
TopAppBar(backgroundColor = MaterialTheme.colors.primary) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
) {
// Title
Text(...)
// Navigation Icon
if (onNavigationUp != null) {
Icon(
painter = painterResource(id = R.drawable.ic_back),
contentDescription = stringResource(
id = R.string.back
),
tint = MaterialTheme.colors.onPrimary,
modifier = Modifier
.clip(MaterialTheme.shapes.small)
.clickable { onNavigationUp() }
.padding(16.dp)
...... ,
)
}
}
}
}
Actually it is possible to reduce space between icon and and title but it's a little bit tricky. Just add negative offset to modifier of text like that
#Composable
fun DetailsAppBar(coin: Coin, backAction: () -> Unit) {
TopAppBar(
navigationIcon = {
IconButton(onClick = { backAction() }) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = null
)
}
},
title = {
Text(
text = "${coin.rank}. ${coin.name} (${coin.symbol})",
modifier = Modifier.offset(x = (-16).dp)
)
}
)
}

How to use Jetpack compose app bar backbutton

getActionBar().setDisplayHomeAsUpEnabled(true) this i was using for normal android appCp,pact activity to switch between two or more activity.can any one tell me how to do this in jetpack Compose ?
The other answer is correct for showing the back button. This is a slight alternative that uses TopAppBar composable instead.
I also ran into a similar issue. The main thing I wanted to solve was hiding the back button when you are at the root or if there is nothing left in backstack, since setDisplayHomeAsUpEnabled took care of that as long as you specified your parent.
Assuming you are using the nav controller with compose, you can do something like this
val navController = rememberNavController()
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = "app bar title") },
navigationIcon = if (navController.previousBackStackEntry != null) {
{
IconButton(onClick = { navController.navigateUp() }) {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = "Back"
)
}
}
} else {
null
}
)
},
content = {
// Body content
}
)
The key here is to set navigationIcon argument of TopAppBar to null when there is nothing in the back stack. This way the back arrow will be hidden when you are at the root and shown otherwise.
You can wrap your main content inside an Scaffold composable and use the topBar to add the back button and handle back action like this:
import androidx.compose.material.Scaffold
.
.
Scaffold(
topBar = {
Row {
Icon(
imageVector = Icons.Filled.ArrowBack,
contentDescription = "Back",
modifier = Modifier
.padding(16.dp)
.clickable {
// Implement back action here
}
)
}
}
) {
BodyContent()
}
Both other solutions are fine and helped me refine my own variation that I have to extract because of usage on many screens.
#Composable
fun MyScaffold(#StringRes titleId: Int, upAvailable: Boolean, onUpClicked: () -> Unit, content: #Composable (PaddingValues) -> Unit) {
Scaffold(
topBar = {
TopAppBar(title = { MyText(textId = titleId) }, backgroundColor = Color.Black, navigationIcon = {
if (upAvailable) {
IconButton(onClick = { onUpClicked() }) {
Icon(imageVector = Icons.Filled.ArrowBack, contentDescription = "Back", tint = Color.White)
}
}
})
},
backgroundColor = Color.Transparent,
content = content
)
}
Where MyText is just my variant that accepts string res and has white text color.
Usage:
val isUpAvailable by viewModel.upAvailable.collectAsState()
MyScaffold(titleId = R.string.title, upAvailable = isUpAvailable, onUpClicked = { viewModel.navigateUp() }) {
// Content
}
Then my BaseViewModel provides upAvailable and navigateUp() via navigationManager dependency which handles navigation via navigationController:
if (destination.route == NAVIGATE_UP) { navController.navigateUp() }
...
// set on every navigation
navigationManager.setUpAvailable(navController.previousBackStackEntry != null)
Here a full Example of using compose app bar back button. https://github.com/pgatti86/toolbar-demo
val navController: NavHostController = rememberNavController()
val isBackButtonVisible by remember {
derivedStateOf {
navController.previousBackStackEntry != null
}
}
use derived state to show or hide your back button icon

Categories

Resources