Why Jetpack Compose AppBar overlaps its shadow - android

I want AppBar to display default shadow below bottom edge but appbar clips its shadow for some reason:
View hierarchy captured from LayoutInspector:
My code:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyAppTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = Color.Cyan,
) {
Box {
TopAppBar(
title = { Text("AppBar title") }
)
}
}
}
}
}
}
Why AppBar shadow behave like this? Am I using it incorrectly? How can I fix this?
UPD:
My bad - shadow works as expected. I just didn't check it properly. Default shadow is very hard to detect visually:

Try to add to Box this code modifier = Modifier.fillMaxSize(). Box cuts your shadow in your code
Example (I added there a white color to see the shadow better)
Surface(
modifier = Modifier.fillMaxSize(),
color = Color.White,
) {
Box(modifier = Modifier.fillMaxSize()) {
TopAppBar(
backgroundColor = Color.White,
title = { Text("AppBar title") }
)
}
}
Result

Related

Compose: How to have ime padding and Scaffold padding with edge-to-edge and windowSoftInputMode is adjustResize

The androidx.compose.material3.Scaffold padding wrongly adds the Navigation Bar padding even when soft keyboard is open the IME padding is added, resulting in a double amount of Navigation Bar padding (see screenshot below, the divider should be touching the top of the soft keyboard).
I'm trying to have the following thing to work together:
App is edge-to-edge
windowSoftInputMode is adjustResize
having my content inside a androidx.compose.material3.Scaffold
This is the code of the MainActivity:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
MyComposeApplicationTheme {
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = stringResource(id = R.string.app_name)) }
)
},
) { scaffoldPadding ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(scaffoldPadding),
contentAlignment = Alignment.BottomCenter
) {
OutlinedTextField(
value = "",
onValueChange = {},
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
)
Divider()
}
}
}
}
}
}
And this is how it looks:
But, if I open the keyboard, the screen does not resizes correctly, despite having the android:windowSoftInputMode="adjustResize" attribute inside the AndroidManifest set for the Activity:
If I use the Modifier.imePadding(), the situation is improving but now I have, beside the padding for the IME, also the inner padding of the Scaffold that is taking into account the padding for the Navigation Bar even when the keyboard is open:
What is the right way to keep the Scaffold bottom padding without it adding the Navigation Bar padding when the IME padding is added?
EDIT
I suspect this is a bug of the Scaffold so I've created an issue on the tracker: https://issuetracker.google.com/issues/249727298
Currently there is no clean solution but the following workaround seems to work fine: passing WindowInsets(0, 0, 0, 0) to Scaffold and then applying .padding(scaffoldPadding).consumedWindowInsets(scaffoldPadding).systemBarsPadding() internally, or applying imePadding() internally as well.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
MyComposeApplicationTheme {
Scaffold(
modifier = Modifier.imePadding(),
topBar = {
TopAppBar(
title = { Text(text = stringResource(id = R.string.app_name)) }
)
},
bottomBar = {
BottomAppBar() {
IconButton(onClick = { }) {
Icon(Icons.Default.Build, null)
}
}
},
contentWindowInsets = WindowInsets(0, 0, 0, 0)
) { scaffoldPadding ->
Box(
modifier = Modifier
.fillMaxSize()
.padding(scaffoldPadding)
.consumedWindowInsets(scaffoldPadding)
.systemBarsPadding(),
contentAlignment = Alignment.BottomCenter
) {
OutlinedTextField(
value = "",
onValueChange = {},
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
)
Divider()
}
}
}
}
}
}

How to disabled ripple effect in TabRow or Tab in Jetpack Compose?

I would like to use the TabRow, but when I click the background has a ripple effect that I do not want. Is there a way to change this? I have the Tab's selectedContectColor equal to the same background color of the page, but I still see a white ripple effect.
TabRow(
modifier = Modifier.height(20.dp),
selectedTabIndex = selectedIndex,
indicator = { tabPositions ->
TabRowDefaults.Indicator(
modifier = Modifier.customTabIndicatorOffset(
currentTabPosition = tabPositions[lazyListState.firstVisibleItemIndex]
tabWidths[lazyListState.firstVisibleItemIndex]
),
color = RED
)
},
backgroundColor = BLACK
) {
tabList.forEachIndexed{ index, tab ->
val selected = (selectedIndex == index)
Tab(
modifier = Modifier
// Have tried different solutions here where there is a .clickable
// and the indication = null, and set interactionSource = remember{
//MutableInteractionSource()}
selected = selected,
selectedContentColor = BLACK,
onClick = {
animateScrollToItem(selectedIndex)
},
text = {
Text("Text Code")
}
)
}
}
You can see in these docs that the selectedContentColor affects the ripple
The ripple is implemented in a selectable modifier defined inside the Tab.
You can't disable it but you can change the appearance of the ripple that is based on a RippleTheme. You can define a custom RippleTheme and apply to your composable with the LocalRippleTheme.
CompositionLocalProvider(LocalRippleTheme provides NoRippleTheme) {
//..TabRow()
}
private object NoRippleTheme : RippleTheme {
#Composable
override fun defaultColor() = Color.Unspecified
#Composable
override fun rippleAlpha(): RippleAlpha = RippleAlpha(0.0f,0.0f,0.0f,0.0f)
}
The shimmer effect is handled by the indication property.
Put it inside the clickable section.
You can create an extension function
inline fun Modifier.noRippleClickable(crossinline onClick: ()->Unit): Modifier = composed {
clickable(indication = null,
interactionSource = remember { MutableInteractionSource() }) {
onClick()
}
}
then simply replace Modifier.clickable {} with Modifier.noRippleClickable {}

Why does accompanist-systemuicontroller not work when using Jetpack Compose with Material3?

I'm learning the use of Material3 in jetpack compose, and I'm trying to set the statusbar to be transparent just as I used to. However, with the following code:
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
val systemUiController = rememberSystemUiController()
SideEffect {
systemUiController.setSystemBarsColor(
color = Color.Transparent,
darkIcons = true
)
}
TestStatusBarTheme {
Surface(
modifier = Modifier
.statusBarsPadding()
.fillMaxSize(),
color = MaterialTheme.colorscheme.background
) {
Text(text = "Test")
}
}
}
While navigationbar becomes transparent, statusbar does not change anyway.
Then I apply the same code, this time using original material design library while keeping everything else unchanged, and it works properly, as the statusbar turns into transparent too.
I can't figure out why I can't use accompanist to change the statusbar in material3. As navigationbar becomes transparent, it's obvious that the systemUiController has got the window and can make changes to navigationbar, then why it can't work with statusbar, which also is a systembar? Is there anything new I haven't notice to make accompanist-systemuicontroller cooperate with Material3, or is it just an unfixed bug for the current version of Material3 or accompanist?
My compose version is 1.2.0-beta02, accompanist version is 0.24.9-beta, and material3 version is 1.0.0-alpha12.
I was trying to move to Windows.Insets API in 1.2.0-rc02 version of compose. And looks like it's working. Try to start my sample:
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
makeStatusBarTransparent()
//WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
Box(
Modifier
.background(Color.Blue)
.fillMaxSize()
.padding(top = 10.dp, bottom = 10.dp)
.statusBarsPadding() //systemBarsPadding
) {
//Box(Modifier.background(Color.Green).navigationBarsPadding()) {
Greeting("TopStart", Alignment.TopStart)
Greeting("BottomStart", Alignment.BottomStart)
Greeting("TopEnd", Alignment.TopEnd)
Greeting("BottomEnd", Alignment.BottomEnd)
//}
}
}
/* setContent {
MyComposeApp1Theme {
// A surface container using the 'background' color from the theme
Surface(modifier = Modifier.fillMaxSize(), color = Color.Red) {
Box(Modifier
.fillMaxSize()
.padding(top = 34.dp)
) {
Greeting("Android")
}
}
}
}*/
}
}
#Composable
fun Greeting(name: String, contentAlignment: Alignment) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = contentAlignment
) {
Text(
text = "Hello $name!",
Modifier
.background(color = Color.Cyan)
)
}
}
#Preview(showBackground = true)
#Composable
fun DefaultPreview() {
MyComposeApp1Theme {
Greeting("Android", Alignment.TopStart)
}
}
#Suppress("DEPRECATION")
fun Activity.makeStatusBarTransparent() {
window.apply {
clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
decorView.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
statusBarColor = android.graphics.Color.GREEN//android.graphics.Color.TRANSPARENT
}
}
val Int.dp
get() = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
toFloat(),
Resources.getSystem().displayMetrics
)
It looks like Material3 version of Scaffold is adding insets by default:
#ExperimentalMaterial3Api
#Composable
fun Scaffold(
...
contentWindowInsets: WindowInsets = ScaffoldDefaults.contentWindowInsets,
content: #Composable (PaddingValues) -> Unit
) {
So if you want to add status bar padding, you have to remove top inset:
Scaffold(
modifier = Modifier
.statusBarsPadding(),
contentWindowInsets = WindowInsets(top = 0.dp)
)

Reduce padding in NavigationBar with Material 3

I'm using the new Material 3 NavigationBar and NavigationBarItem components, I want the NavigationBar to be thinner, because the default one is too large. I want one similar to the one Gmail or Drive has (see picture at the end for the comparison). Making the icon smaller doesn't work, and neither changing all the available paddings (Icon, NavigationBar and NavigationBarItem).
This is the Composable code, if I change the NavigationBar heigh (using Modifier) then this happens:
I primarly want to remove the space between the label and the bottom, and the one between the top and the icon.
#Composable
fun MyAppBottomBar(navController: NavController, tabs: Array<MenuBottom>) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route ?: MenuBottom.INICIO.route
val rutas = remember { MenuBottom.values().map { it.route } }
if (currentRoute in rutas) {
NavigationBar(containerColor = elevation01) {
tabs.forEachIndexed { index, item ->
NavigationBarItem(
selected = currentRoute == item.route,
onClick = {
if (item.route != currentRoute) {
navController.navigate(item.route) {
popUpTo(navController.graph.startDestinationId) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
}
},
label = { Text(stringResource(id = item.title)) },
icon = {
if (item.route == currentRoute) {
Icon(item.selectedIcon, contentDescription = null, tint = Color.Black)
} else {
Icon(item.unselectedIcon, contentDescription = null)
}
},
colors = NavigationBarItemDefaults.colors(
selectedIconColor = Color.Black,
unselectedIconColor = Color.Black,
indicatorColor = Greenyellow,
selectedTextColor = Color.Black,
unselectedTextColor = Color.Black
)
)
}
}
}
}
NavigationBar does not use padding. It uses a fixed height. How do I know that? While working with MUI (which is a React implementation of Material design) I found that they use a fixed height, and margin: auto to center items vertically. Then I figured jetpack compose NavigationBar might do the same and the idea was right. So whats the solution?
NavigationBar(
modifier = Modifier.height(64.dp)
) {...}
Or change it to your taste. It will shrink and grow the space taken up.
This most likely has to do with the bottom navigation. The grey bar you see has its own padding by default.
Have a look at this documentation on how to remove those System intents.
Here's what you need to do in a nutshell:
implementation "com.google.accompanist:accompanist-insets:<version>"
Then in your MainActivity call WindowCompat.setDecorFitsSystemWindows(window, false) like this:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {...}
}
Now your App should use the entire Screen and your NavigationBar should have the same height as the one from Gmail.
To apply single ones of them again e.g. StatusBar call a modifier:
Modifier.statusBarsPadding()

Modal Bottom Sheet scrim color is not shown in status bar in Jetpack compose

Migrating an app in view system to Jetpack compose.
The bottom sheet scrim color is shown on the status bar in the current app.
How to reproduce the same in Jetpack compose?
Screenshot of the app using views
Screenshot of the app using compose
Compose Code
val modalBottomSheetState = rememberModalBottomSheetState(
initialValue = ModalBottomSheetValue.Hidden,
)
val coroutineScope = rememberCoroutineScope()
ModalBottomSheetLayout(
sheetState = modalBottomSheetState,
sheetContent = {
// Not relevant
},
) {
Scaffold(
scaffoldState = scaffoldState,
topBar = {
// Not relevant
},
floatingActionButton = {
FloatingActionButton(
onClick = {
coroutineScope.launch {
if (!modalBottomSheetState.isAnimationRunning) {
if (modalBottomSheetState.isVisible) {
modalBottomSheetState.hide()
} else {
modalBottomSheetState.show()
}
}
}
},
) {
Icon(
imageVector = Icons.Rounded.Add,
contentDescription = "Add",
)
}
},
modifier = Modifier
.fillMaxSize(),
) { innerPadding ->
// Not relevant - Nav graph code
}
}
try to use the System UI Controller in the accompanist library to control the statusBar Color and navigationBar color
implementation "com.google.accompanist:accompanist-systemuicontroller:0.18.0"
// Remember a SystemUiController
val systemUiController = rememberSystemUiController()
val useDarkIcons = MaterialTheme.colors.isLight
SideEffect {
// Update all of the system bar colors to be transparent, and use
// dark icons if we're in light theme
systemUiController.setSystemBarsColor(
color = Color.Transparent,
darkIcons = useDarkIcons
)
// setStatusBarsColor() and setNavigationBarsColor() also exist
}
In the latest versions of Compose, there's a statusBarColor parameter that is set in the Theme.kt file by default. If you're not using accompanist, try altering that value to get the desired effect.
You can remove automatic insects and make status bar transparent:
WindowCompat.setDecorFitsSystemWindows(window, false)
window.statusBarColor = android.graphics.Color.TRANSPARENT;
Then draw your bottom sheet and it will go under all system bars including status bar
Just don't forget to add insects for the rest of the views when needed, it's in compose foundation now:
modifier = Modifier
.navigationBarsPadding()
.captionBarPadding()
.imePadding()
.statusBarsPadding(),

Categories

Resources