transparent navigation bar leaves a dark box - android

#Composable
fun BottomNavigationBar(items: List<BottomNavItem>, navController: NavController, onItemClick: (BottomNavItem) -> Unit){
val selectedScreen = navController.currentBackStackEntryAsState()
BottomNavigation(
modifier = Modifier.fillMaxWidth(),
backgroundColor = Color.Transparent,
) {
items.forEach { item ->
val selected = item.route == selectedScreen.value?.destination?.route
BottomNavigationItem(
selected = selected,
onClick = { onItemClick(item) },
selectedContentColor = colorResource(id = R.color.button_background_light_blue),
unselectedContentColor = Color.Gray,
icon = {
Column(horizontalAlignment = CenterHorizontally) {
Icon(imageVector = item.icon, contentDescription = item.name)
if(selected) {
Text(text = item.name, textAlign = TextAlign.Center, fontSize = 10.sp)
}
}
}
)
}
}
}
tbh I haven't found anything about (that's just what I assume) a box that contains icons or anything close to that, what could I add to remove that "box"?

Just set the elevation to 0.dp. I don't know the exact method for BottomNavigation, but if there is a parameter named elevation, set it to 0.dp. If there is no parameter value with that name, apply the elevation Modifier.
Modifier.elevation(0.dp)

Related

Jetpack Compose: How to create multi-item FAB with animation?

I'm trying to create a multi-item floating action button with the following animation:
I created a multi-item floating action button but I could not implement the intended animation.
I have FilterFabMenuButton composable that I show as a menu item :
FilterFabMenuButton
#Composable
fun FilterFabMenuButton(
item: FilterFabMenuItem,
onClick: (FilterFabMenuItem) -> Unit,
modifier: Modifier = Modifier
) {
FloatingActionButton(
modifier = modifier,
onClick = {
onClick(item)
},
backgroundColor = colorResource(
id = R.color.primary_color
)
) {
Icon(
painter = painterResource(item.icon), contentDescription = null, tint = colorResource(
id = R.color.white
)
)
}
}
I have FilterFabMenuLabel composable which is a label for FilterFabMenuButton:
FilterFabMenuLabel
#Composable
fun FilterFabMenuLabel(
label: String,
modifier: Modifier = Modifier
) {
Surface(
modifier = modifier,
shape = RoundedCornerShape(6.dp),
color = Color.Black.copy(alpha = 0.8f)
) {
Text(
text = label, color = Color.White,
modifier = Modifier.padding(horizontal = 20.dp, vertical = 2.dp),
fontSize = 14.sp,
maxLines = 1
)
}
}
I have FilterFabMenuItem composable which is a Row that contains FilterFabMenuLabel and FilterFabMenuButton composables:
FilterFabMenuItem
#Composable
fun FilterFabMenuItem(
menuItem: FilterFabMenuItem,
onMenuItemClick: (FilterFabMenuItem) -> Unit,
modifier: Modifier = Modifier
) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalAlignment = Alignment.CenterVertically
) {
//label
FilterFabMenuLabel(label = menuItem.label)
//fab
FilterFabMenuButton(item = menuItem, onClick = onMenuItemClick)
}
}
I have FilterFabMenu composable which is a Column that shows menu items:
FilterFabMenu
#Composable
fun FilterFabMenu(
visible: Boolean,
items: List<FilterFabMenuItem>,
modifier: Modifier = Modifier
) {
val enterTransition = remember {
expandVertically(
expandFrom = Alignment.Bottom,
animationSpec = tween(150, easing = FastOutSlowInEasing)
) + fadeIn(
initialAlpha = 0.3f,
animationSpec = tween(150, easing = FastOutSlowInEasing)
)
}
val exitTransition = remember {
shrinkVertically(
shrinkTowards = Alignment.Bottom,
animationSpec = tween(150, easing = FastOutSlowInEasing)
) + fadeOut(
animationSpec = tween(150, easing = FastOutSlowInEasing)
)
}
AnimatedVisibility(visible = visible, enter = enterTransition, exit = exitTransition) {
Column(
modifier = modifier.fillMaxWidth(), horizontalAlignment = Alignment.End,
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
items.forEach { menuItem ->
FilterFabMenuItem(
menuItem = menuItem,
onMenuItemClick = {}
)
}
}
}
}
I have FilterFab composable that expands/collapses FilterMenu:
FilterFab
#Composable
fun FilterFab(
state: FilterFabState,
rotation:Float,
onClick: (FilterFabState) -> Unit,
modifier: Modifier = Modifier
) {
FloatingActionButton(
modifier = modifier
.rotate(rotation),
elevation = FloatingActionButtonDefaults.elevation(defaultElevation = 0.dp),
onClick = {
onClick(
if (state == FilterFabState.EXPANDED) {
FilterFabState.COLLAPSED
} else {
FilterFabState.EXPANDED
}
)
},
backgroundColor = colorResource(
R.color.primary_color
),
shape = CircleShape
) {
Icon(
painter = painterResource(R.drawable.fab_add),
contentDescription = null,
tint = Color.White
)
}
}
Last but not least, I have a FilterView composable which is a Column that contains FilterFabMenu and FilterFab composables:
FilterView
#SuppressLint("UnusedTransitionTargetStateParameter")
#Composable
fun FilterView(
items: List<FilterFabMenuItem>,
modifier: Modifier = Modifier
) {
var filterFabState by rememberSaveable() {
mutableStateOf(FilterFabState.COLLAPSED)
}
val transitionState = remember {
MutableTransitionState(filterFabState).apply {
targetState = FilterFabState.COLLAPSED
}
}
val transition = updateTransition(targetState = transitionState, label = "transition")
val iconRotationDegree by transition.animateFloat({
tween(durationMillis = 150, easing = FastOutSlowInEasing)
}, label = "rotation") {
if (filterFabState == FilterFabState.EXPANDED) 230f else 0f
}
Column(
modifier = modifier.fillMaxSize().padding(16.dp), horizontalAlignment = Alignment.End,
verticalArrangement = Arrangement.spacedBy(16.dp,Alignment.Bottom)
) {
FilterFabMenu(items = items, visible = filterFabState == FilterFabState.EXPANDED)
FilterFab(
state = filterFabState,
rotation = iconRotationDegree, onClick = { state ->
filterFabState = state
})
}
}
This produces the following result:
expandVertically in your enterTransition is not the correct approach for this kind of animation. Per documentation, it animates a clip revealing the content of the animated item from top to bottom, or vice versa. You apply this animation to the entire column (so all items at once), resulting in the gif you have shown us.
Instead, you should use a different enter/exit animation type, maybe a custom animation where you work with the item scaling to emulate the "pop in" effect like such:
scaleFactor.animateTo(2f, tween(easing = FastOutSlowInEasing, durationMillis = 50))
scaleFactor.animateTo(1f, tween(easing = FastOutSlowInEasing, durationMillis = 70))
(the scaleFactor is an animatabale of type Animatable<Float, AnimationVector1D> in this instance).
Then you create such an animatable for each of the column items, i.e. your menu items. After that, just run the animations in a for loop for each menu item inside a coroutine scope (since compose animations are suspend by default, they will run in sequence, use async/awaitAll if you want to do it in parallel).
Also, don't forget to put your animatabales in remember {}, then just use the values you are animating like scaleFactor inside modifiers of your column items, and trigger them inside a LaunchedEffect when you click to expand/close the menu.

Cannot calculate specific dimensions for composable TextField

I am trying to create multiple items to encapsulate the specific behavior of every component but I cannot specify the dimensions for every view.
I want a Textfield with an X icon on its right
setContent {
Surface(
modifier = Modifier
.fillMaxSize()
.background(color = white)
.padding(horizontal = 15.dp)
) {
Row(horizontalArrangement = Arrangement.spacedBy(15.dp)) {
Searcher(
modifier = Modifier.weight(1f),
onTextChanged = { },
onSearchAction = { }
)
Image(
painter = painterResource(id = R.drawable.ic_close),
contentDescription = null,
colorFilter = ColorFilter.tint(blue)
)
}
}
}
The component is the following
#Composable
fun Searcher(
modifier: Modifier = Modifier,
onTextChanged: (String) -> Unit,
onSearchAction: () -> Unit
) {
Row {
SearcherField(
onTextChanged = onTextChanged,
onSearchAction = onSearchAction,
modifier = Modifier.weight(1f)
)
CircularSearch(
modifier = Modifier
.padding(horizontal = 10.dp)
.align(CenterVertically)
)
}
}
and the SearcherField:
#Composable
fun SearcherField(
modifier: Modifier = Modifier,
onTextChanged: (String) -> Unit,
onSearchAction: () -> Unit
) {
var fieldText by remember { mutableStateOf(emptyText) }
TextField(
value = fieldText,
onValueChange = { value ->
fieldText = value
if (value.length > 2)
onTextChanged(value)
},
singleLine = true,
textStyle = Typography.h5.copy(color = White),
colors = TextFieldDefaults.textFieldColors(
cursorColor = White,
focusedIndicatorColor = Transparent,
unfocusedIndicatorColor = Transparent,
backgroundColor = Transparent
),
trailingIcon = {
if (fieldText.isNotEmpty()) {
IconButton(onClick = {
fieldText = emptyText
}) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = emptyText
)
}
}
},
placeholder = {
Text(
text = stringResource(id = R.string.dondebuscas),
style = Typography.h5.copy(color = White)
)
},
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search),
keyboardActions = KeyboardActions(
onSearch = {
onSearchAction()
}
),
modifier = modifier.fillMaxWidth()
)
}
But I don´t know why, but the component Searcher with the placeholder is rendered in two lines.
It´s all about the placeholder that seems to be resized for not having enough space because if I remove the placeholder, the component looks perfect.
Everything is in one line, not having a placeholder of two lines. I m trying to modify the size of every item but I am not able to get the expected result and I don´t know if the problem is just about the placeholder.
How can I solve it? UPDATE -> I found the error
Thanks in advance!
Add maxlines = 1 to the placeholder Text's parameters.
Your field is single -lune but your text is multi-line. I think it creates conflict in implementation.
Okay... I find that the problem is about the trailing icon. Is not visible when there is no text in the TextField but is still occupying some space in the view, that´s why the placeholder cannot occupy the entire space. The solution is the following.
val trailingIconView = #Composable {
IconButton(onClick = {
fieldText = emptyText
}) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = emptyText
)
}
}
Create a variable with the icon and set it to the TextField only when is required
trailingIcon = if (fieldText.isNotEmpty()) trailingIconView else null,
With that, the trailing icon will be "gone" instead of "invisible" (the old way).
Still have a lot to learn.

Jetpack Compose TopAppBar wrong color

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.

jetpack compose can't set search bar background color to white

I have a search bar with textfield when I try to set background color to white it comes with gray but I can make it to other colors only white not working if I change Textfiled to BasicTexfield it works fine but can't set the Icon top start
#Composable
fun DoctorListScreen(
navController: NavController,
viewModel: DoctorListViewModel = hiltViewModel()
) {
Surface(
color = Color.White,
modifier = Modifier.fillMaxSize(1f)
) {
Column {
Spacer(modifier = Modifier.padding(top = 15.dp))
SearchBar(
hint = "Klinik ara..", modifier = Modifier
.fillMaxWidth()
.padding(15.dp)
) {
}
CheckGender(modifier = Modifier.padding(15.dp))
}
}
}
#Composable
fun SearchBar(
modifier: Modifier = Modifier,
hint: String = "",
onSearch: (String) -> Unit = {},
) {
var text by remember {
mutableStateOf("")
}
var isHintDisplayed by remember {
mutableStateOf(hint != "")
}
Box(modifier = modifier) {
TextField(value = text, onValueChange = {
text = it
onSearch(it)
}, leadingIcon = {
Icon(painter = painterResource(id = R.drawable.search), contentDescription = null)
}, maxLines = 1,
singleLine = true,
modifier = Modifier
.fillMaxWidth()
.shadow(5.dp, shape = RoundedCornerShape(10.dp))
.background(Color.White, shape = RoundedCornerShape(10.dp))
.onFocusChanged {
isHintDisplayed = it.isFocused != true && text.isEmpty()
})
if (isHintDisplayed) {
Text(
text = hint,
color = Color.LightGray,
modifier = Modifier.padding(horizontal = 50.dp, vertical = 16.dp)
)
}
}
}
how it looks like :
both background and bar color is white but seems different
Remove the background modifier:
//background(Color.White, shape = RoundedCornerShape(10.dp))
and use:
TextField(
/* .... */
shape = RoundedCornerShape(10.dp),
colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.White),
)
Add this line in your Textfield:
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.White)

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)
)
}
)
}

Categories

Resources