I wrote an AutoCompletet TextField in Jetpack Compose. I used this view in a Row and between two others' views. The side views move down when autocomplete dropdown is opened. How can I fix this issue?
The Link of GIF file is end of page. in the GIF you can see side views of Autocomplete move to bottom when the suggest items is shows.
My AutoComplete codes:
#Composable
fun AutoCompleteTextField(
modifier: Modifier = Modifier,
list: List<Product>,
searchTerm: TextFieldValue,
updateSearchTerm: (TextFieldValue) -> Unit,
) {
var product by remember {
mutableStateOf("")
}
var heightTextFields by remember {
mutableStateOf(55.dp)
}
var textFieldSize by remember {
mutableStateOf(Size.Zero)
}
var expanded by remember {
mutableStateOf(false)
}
val interactionSource = remember {
MutableInteractionSource()
}
Column(
modifier = modifier
.width(200.dp)
// .padding(30.dp)
.clickable(
interactionSource = interactionSource,
indication = null,
onClick = {
expanded = false
}
)
) {
Column(modifier = modifier.fillMaxWidth()) {
Row(
modifier = modifier
.border(BorderStroke(width = 1.dp, color = Color.LightGray))
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
IconButton(onClick = {product = "" }) {
Icon(
imageVector = Icons.Filled.Add,
contentDescription = "Add",
modifier = modifier
.padding(4.dp)
.align(Alignment.CenterVertically)
)
}
TextField(
modifier = Modifier
.fillMaxWidth()
.height(heightTextFields)
.onGloballyPositioned { coordinates ->
textFieldSize = coordinates.size.toSize()
},
value = product,
onValueChange = {
product = it
expanded = true
},
colors = TextFieldDefaults.textFieldColors(
backgroundColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent,
cursorColor = Color.Black
),
textStyle = TextStyle(
fontFamily = shabnamFontFamily,
color = MaterialTheme.colorScheme.secondary,
fontWeight = FontWeight.Bold,
fontSize = 14.sp,
),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Text,
imeAction = ImeAction.Done
),
singleLine = true,
)
}
AnimatedVisibility(visible = expanded) {
Card(
modifier = modifier
.padding(horizontal = 4.dp)
.width(textFieldSize.width.dp),
elevation = 10.dp,
shape = RoundedCornerShape(bottomStart = 4.dp, bottomEnd = 4.dp)
) {
LazyColumn(
modifier = Modifier.heightIn(max = 150.dp),
) {
if (product.isNotEmpty()) {
items(
list.filter {
it.productName.lowercase()
.contains(product.lowercase())
}
) { item ->
ProductItems(title = item.productName) { title ->
product = title
expanded = false
}
}
} else {
items(
items =
list
) { item ->
ProductItems(title = item.productName) { title ->
product = title
expanded = false
}
}
}
}
}
}
}
}
}
}
Screen GIF file
I don't understand why the FloatingButton is going up with the BottomSheet.
I tried to change the sheetElevation which is higher than the elevation of the FloatingButton, but the issue remains. That's because the code inside BottomSheetScaffoldStack says to move the FloatingButton up along with the BottomSheet. Is there any way to avoid that?
Here is the code of the BottomSheetScaffold:
BottomSheetScaffold(
scaffoldState = bottomSheetScaffoldState,
topBar = { TopBar(
areButtonShowed = true,
title = topBarTitle,
onBackPressed = { BendRouter.navigateTo(onBackDestination) }
) },
floatingActionButtonPosition = FabPosition.Center,
floatingActionButton = {
ExtendedFloatingButton(
text = context.getString(R.string.start),
onClick = {}, // TODO
modifier = Modifier
.fillMaxWidth()
.height(45.dp)
.padding(
start = 24.dp,
end = 24.dp
),
backgroundColor = PureWhite
)
Spacer(modifier = Modifier.height(150.dp))
},
sheetBackgroundColor = PureWhite,
sheetPeekHeight = 0.dp,
sheetElevation = 70.dp,
sheetShape = RoundedCornerShape(50.dp),
sheetContent = {
openStretchDetails?.let { stretch ->
BottomSheetView(stretch = stretch)
}
},
content = { RoutinePageView(viewModel) }
)
And ExtendedFloatingButton:
#Composable
fun ExtendedFloatingButton(
text: String,
#DrawableRes icon: Int? = null,
onClick: () -> Unit,
modifier: Modifier = Modifier,
elevation: Dp = 12.dp,
backgroundColor: Color
) {
ExtendedFloatingActionButton(
text = {
Text(
text = text.uppercase(),
color = Gray,
fontSize = 18.sp,
maxLines = 1,
fontWeight = FontWeight.Bold,
letterSpacing = .5.sp
)
},
onClick = onClick,
icon = {
icon?.let {
Icon(
painter = painterResource(id = it),
contentDescription = ""
)
}
},
modifier = modifier,
elevation = FloatingActionButtonDefaults.elevation(elevation),
backgroundColor = backgroundColor
)
}
The BottomSheetScaffold has a default behavior that you can see by clicking on it while pressing CTRL. Looking at BottomSheetScaffoldStack inside the BottomSheetScaffold.kt class you'll see how the FloatingButton is moved along with the BottomSheet.
To solve this problem I used ModalBottomSheetLayout instead of BottomSheetScaffold.
ModalBottomSheetLayout(
sheetState = bottomState,
sheetContent = {
Box(Modifier.defaultMinSize(minHeight = 1.dp)) {
openStretchDetails?.let { stretch ->
BottomSheetView(stretch = stretch)
coroutineScope.launch {
bottomState.animateTo(ModalBottomSheetValue.Expanded)
}
}
}
},
sheetBackgroundColor = PureWhite,
sheetElevation = 16.dp,
sheetShape = RoundedCornerShape(50.dp),
) {
Scaffold(
topBar = { TopBar(
areButtonShowed = true,
title = topBarTitle,
onBackPressed = { BendRouter.navigateTo(onBackDestination) }
) },
floatingActionButtonPosition = FabPosition.Center,
floatingActionButton = {
ExtendedFloatingButton(
text = context.getString(R.string.start),
onClick = {}, // TODO
modifier = Modifier
.fillMaxWidth()
.height(45.dp)
.padding(
start = 24.dp,
end = 24.dp
),
backgroundColor = PureWhite
)
Spacer(modifier = Modifier.height(150.dp))
},
content = { RoutinePageView(viewModel) }
)
}
Add this on the top:
val bottomState = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Hidden,
confirmStateChange = { it != ModalBottomSheetValue.HalfExpanded }
)
val coroutineScope = rememberCoroutineScope()
Can anyone please point me out how to change this white background color of the app? Setting a color or background color on the Surface is having no impact. I've set the cyan background for the content inside the Scaffold just to debug the issue.
class MainActivity : ComponentActivity() {
...
setContent {
ChakkarTheme {
Surface(
color = Color.Red, modifier = Modifier
.fillMaxSize()
.background(Color.DarkGray)
) {
ChakkarApp()
}
...
#Composable
fun ChakkarApp() {
Scaffold(
topBar = { TopAppBar(title = { Text(appTitle) }, navigationIcon = navigationIcon) },
floatingActionButtonPosition = FabPosition.End,
floatingActionButton = {
if (showFab) {
FloatingActionButton(onClick = { /*TODO*/ }) {
Icon(imageVector = Icons.Filled.Add, contentDescription = null)
}
}
},
bottomBar = {
BottomNavigation() {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentDestination = navBackStackEntry?.destination
bottomNavItems.forEach { screen ->
BottomNavigationItem(
icon = { Icon(imageVector = screen.icon, contentDescription = null) },
label = { Text(stringResource(id = screen.resourceId)) },
selected = currentDestination?.hierarchy?.any { it.route == screen.route } == true,
onClick = {
navController.navigate(screen.route) {
popUpTo(navController.graph.findStartDestination().id) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
)
}
}
}
) { paddingValues ->
Column(
modifier = Modifier
.verticalScroll(rememberScrollState())
.background(Color.Cyan)
.padding(paddingValues)
.padding(16.dp)
) {
NavHost(
navController = navController,
startDestination = Screen.Running.route
) {
...
Thanks for your help!
The default background color of Scaffold is MaterialTheme.colors.background.
You can specify a custom value:
Scaffold(
...
backgroundColor = Color.DarkGray,
)
Could you try add modifier = Modifier.fillMaxSize().background(color = Color.Black) or backgroundColor = Color.Black in your Scaffold?
+ Edit:
add Modifier is not working. Try to add backgroundColor in your Scaffold.
Like this:
MaterialTheme {
Scaffold(
content = {
Box(modifier = Modifier.size(500.dp).background(color = Color.Cyan))
},
backgroundColor = Color.Black
)
}
From the below code snippet set full background color
Modifier.fillMaxSize() It gives a full height and width.
Modifier.background(Color.Grey) By usging background can set a background color.
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Grey)
) {
Column(
modifier = Modifier
.verticalScroll(
state = rememberScrollState()
)
) {
Text(
text = profileViewModel.loginUserDetail.lastName,
fontSize = 18.sp,
color = Color.White,
fontFamily = myFontFamily,
fontWeight = FontWeight.SemiBold,
modifier = Modifier.padding(5.dp, 5.dp, 5.dp, 5.dp),
)
}
}
I am trying to develop a custom Topbar like below:
I have tried developing TopAppBar using jetpack compose but now wanted to have collapsed and expanded effect on Topbar and because of that, I wanted to develop a custom top bar. Please let me know if the above screenshot can be achieved from the below code.
Apologies as I am new to jetpack.
#Composable
fun TopNavBar(
navController: NavController,
title: String,
showActions: Boolean = false,
showAvatar: Boolean = true
) {
val toolbarHeight = 52.dp
val toolbarOffsetHeightPx = remember { mutableStateOf(0f) }
TopAppBar(
modifier = Modifier
.height(toolbarHeight)
.offset { IntOffset(x = 0, y = toolbarOffsetHeightPx.value.roundToInt()) },
// title = { Text("toolbar offset is ${toolbarOffsetHeightPx.value}") }
backgroundColor = MaterialTheme.colors.background,
title = {
Text(
text = title,
color = MaterialTheme.colors.primaryLabel,
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.body1
)
},
navigationIcon = {
IconButton(onClick = { navController.popBackStack() }) {
Icon(
painter = painterResource(id = R.drawable.ic_back),
contentDescription = stringResource(id = R.string.back),
tint = Grey8
)
}
},
actions = {
// RowScope here, so these icons will be placed horizontally
if (showActions) {
IconButton(onClick = { }) {
Icon(
painter = painterResource(id = R.drawable.ic_menu_search),
contentDescription = stringResource(id = R.string.search),
tint = Grey8
)
}
IconButton(onClick = { }) {
Icon(
painter = painterResource(id = R.drawable.ic_create),
contentDescription = stringResource(id = R.string.create),
tint = Grey8
)
}
}
if (showAvatar) {
IconButton(onClick = { }) {
Image(
painter = painterResource(id = R.drawable.ic_avatar),
contentDescription = stringResource(id = R.string.profile),
modifier = Modifier
.size(32.dp)
.clip(CircleShape)
)
}
}
}
)
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()
)
}