Hello I can't figure out how to make a cut corners menu in jetpack compose 1.0.0-beta02. I tried wrapping the while menu with a surface but It didn't work.
TopAppBar(
modifier = Modifier
.statusBarsPadding(),
title = {
Text(text = "Title")
},
actions = {
var menuExpanded by remember { mutableStateOf(false) }
IconButton(onClick = { menuExpanded = true }) {
Icon(Icons.Default.MoreVert, contentDescription = null)
}
DropdownMenu(
expanded = menuExpanded,
onDismissRequest = {
menuExpanded = false
},
) {
DropdownMenuItem(onClick = {}) {
Text("Item 2")
}
}
},
)
Which gives me
But I need something like this, which is rounded.
Using a M2 MaterialTheme theme, the default shape used by the DropdownMenu is defined by the
medium attribute in the shapes used in the MaterialTheme (check your theme).
val Shapes = Shapes(
small = RoundedCornerShape(4.dp),
medium = RoundedCornerShape(4.dp), //<- used by `DropdownMenu`
large = RoundedCornerShape(0.dp)
)
You can change this value in your theme or you can override the medium shape only in your DropdownMenu.
Something like:
MaterialTheme(shapes = MaterialTheme.shapes.copy(medium = RoundedCornerShape(16.dp))) {
DropdownMenu(
expanded = menuExpanded,
onDismissRequest = {
menuExpanded = false
}
) {
DropdownMenuItem(onClick = {}) {
Text("Item 2")
}
DropdownMenuItem(onClick = {}) {
Text("Item 3")
}
}
}
Using a M3 MaterialTheme the default shape used by the DropdownMenu is defined by the extraSmall attribute in the shapes:
MaterialTheme(
shapes = MaterialTheme.shapes.copy(extraSmall = RoundedCornerShape(16.dp))){
//... DropdownMenu()
}
Related
I need remove the elevation/shadow from dropdown menu, like right image:
My menu:
DropdownMenu(
expanded = mExpanded,
onDismissRequest = { mExpanded = false },
modifier = Modifier
.width(with(LocalDensity.current){mTextFieldSize.width.toDp()})
) {
mCities.forEach { label ->
DropdownMenuItem(onClick = {
mSelectedText = label
mExpanded = false
}) {
Text(text = label)
}
}
}
Based on answer: How to add elevation just in the bottom of an element? Jetpack Compose you can wrap your dropdown menu in a Surface and provide the elevation as 0.
Modified code:
Surface(
shadowElevation = 0.dp
){
DropdownMenu(
expanded = mExpanded,
onDismissRequest = { mExpanded = false },
modifier = Modifier
.width(with(LocalDensity.current){mTextFieldSize.width.toDp()})
) {
mCities.forEach { label ->
DropdownMenuItem(onClick = {
mSelectedText = label
mExpanded = false
}) {
Text(text = label)
}
}
}
}
I'm trying to have a white top app bar using Jetpack Compose but the color is grey instead of white.
Here is my code:
#Composable
fun AppTheme(content: #Composable () -> Unit) {
MaterialTheme(
colors = Colors(
primary = colorResource(R.color.colorPrimary),
primaryVariant = colorResource(R.color.colorPrimaryVariant),
secondary = colorResource(R.color.colorSecondary),
secondaryVariant = colorResource(R.color.colorSecondaryVariant),
background = colorResource(R.color.white),
surface = colorResource(R.color.white),
error = colorResource(R.color.failure),
onPrimary = colorResource(R.color.colorOnPrimary),
onSecondary = colorResource(R.color.colorOnSecondary),
onBackground = colorResource(R.color.colorOnBackground),
onSurface = colorResource(R.color.colorOnSurface),
onError = colorResource(R.color.white),
isLight = false
),
typography = Typography(),
shapes = Shapes(),
content = content
)
}
#Composable
#Preview
fun TestTopBar() {
AppTheme {
TopBar(title = R.string.common_country, actions = actions())
}
}
#Composable
fun actions(): #Composable RowScope.() -> Unit = {
IconButton(onClick = {}) { Icon(Icons.Filled.Search, "Search") }
}
#Composable
fun TopBar(title: Int, actions: #Composable (RowScope.() -> Unit) = {}) {
TopAppBar(backgroundColor = Color.White) {
//CompositionLocalProvider(LocalContentAlpha provides ContentAlpha.high) {
Box {
// navigation icon
Row(Modifier.align(Alignment.CenterStart), verticalAlignment = Alignment.CenterVertically) {
IconButton(onClick = { }) {
Icon(Icons.Filled.ArrowBack, "Back")
}
}
// title
Row(Modifier.fillMaxSize(), verticalAlignment = Alignment.CenterVertically) {
Text(
modifier = Modifier
.fillMaxWidth()
.alpha(1f),
textAlign = TextAlign.Center,
maxLines = 1,
text = stringResource(title),
style = MaterialTheme.typography.h6
)
}
// actions
Row(
Modifier.align(Alignment.CenterEnd),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically,
content = actions
)
}
//}
}
}
I've set the surface as white in my theme. I've also set the background color of the top app bar as white. Still, the preview display a light grey instead of white.
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
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)
)
}
)
}
I have a row with a text align at the start and a image align at the end. When I press the image I'm showing a DropdownMenu, but this appear in the start of the row and I want that appear at the end of the row.
I'm trying to use Alignment.centerEnd in Modifier component but is not working.
How can I do that the popup appear at the end of the row?
#Composable
fun DropdownDemo(currentItem: CartListItems) {
var expanded by remember { mutableStateOf(false) }
Box(modifier = Modifier
.fillMaxWidth()) {
Text(modifier = Modifier.align(Alignment.TopStart),
text = currentItem.type,
color = colorResource(id = R.color.app_grey_dark),
fontSize = 12.sp)
Image(painter = painterResource(R.drawable.three_dots),
contentDescription = "more options button",
Modifier
.padding(top = 5.dp, bottom = 5.dp, start = 5.dp)
.align(Alignment.CenterEnd)
.width(24.dp)
.height(6.75.dp)
.clickable(indication = null,
interactionSource = remember { MutableInteractionSource() },
onClick = {
expanded = true
}))
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
modifier = Modifier
.background(
Color.LightGray
).align(Alignment.CenterEnd),
) {
DropdownMenuItem(onClick = { expanded = false }) {
Text("Delete")
}
DropdownMenuItem(onClick = { expanded = false }) {
Text("Save")
}
}
}
}
As documentation says:
A DropdownMenu behaves similarly to a Popup, and will use the position of the parent layout to position itself on screen.
You need to put DropdownMenu together with the caller view in a Box. In this case DropdownMenu will appear under the caller view.
var expanded by remember { mutableStateOf(false) }
Column {
Text("Some text")
Box {
Image(
painter = painterResource(R.drawable.test),
contentDescription = "more options button",
modifier = Modifier
.clickable {
expanded = true
}
)
DropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false },
) {
DropdownMenuItem(onClick = { expanded = false }) {
Text("Delete")
}
DropdownMenuItem(onClick = { expanded = false }) {
Text("Save")
}
}
}
}
Use the offset parameter of the DropdownMenu().
DropdownMenu(
offset = DpOffset(x = (-66).dp, y = (-10).dp)
)
Change the x and y values. They accept both positive and negative values.