why i Should use UnusedMaterialScaffoldPaddingParameter top of Compose function? - android

When I don't use the following code, it is displayed on the page.
#SuppressLint("UnusedMaterialScaffoldPaddingParameter")

You should use the PaddingValues of the Scaffold because these values are calculated based on the other items inside of your scaffold. For instance when you use a topBar or BottomBar, your screen will be partially covered with the UI element. When you use the PaddingValues inside the content of your scaffold this won't happen. You can use de PaddingValues like this:
Scaffold(
topBar = {
// some TopBar
},
bottomBar = {
// some BottomBar
},
) { paddingValues ->
Box(
modifier = Modifier.padding(bottom = paddingValues.calculateBottomPadding(), top = paddingValues.calculateTopPadding())
) {
// your UI
}
}

Related

Stop the keyboard pushing the top app bar off the screen in Compose

I have a Compose activity, where there's a top app bar, and some TextFields towards the bottom of the screen. When I focus on one of the TextFields and the keyboard is invoked, I want the text field to appear above the keyboard, so I can see what I'm typing. This works fine, however the whole screen content is pushed upwards making the top app bar disappear or be cut off at the top.
I think ideally, the top app bar would be pinned to the top, and only the contents below would shift. It doesn't matter if the top app bar is part of the scaffold, or above the scaffold in a Column:
Scaffold(
topBar = {
TopAppBar("...")
}
) {
// scrollable contents with TextFields
}
---> OR
Column {
TopAppBar("...")
Scaffold {
// scrollable contents with TextFields
}
}
This is the unwanted behaviour illustrated:
Is there a way to achieve my desired behaviour of pinning the top app bar? Should the top app bar be pushed up by default in Compose?
It looks like you have to specify a weight to one of your composable.
Taking some reference from an answer from this post. Assuming you have set adjustResize in your manifest,
android:windowSoftInputMode="adjustResize"/>
you can consider this as a rough solution
Scaffold(
topBar = {
TopAppBar(title = { Text("Hello World")})
}
) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(it)
) {
Box(
modifier = Modifier
.weight(1f)
.background(Color.Gray)
.fillMaxWidth()
)
Box(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.background(Color(0xFF6f4d8c)),
contentAlignment = Alignment.Center
) {
TextField(
value = "User Name",
onValueChange = {}
)
}
Box(
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.background(Color(0xFF6286bd)),
contentAlignment = Alignment.Center
) {
TextField(
value = "Password",
onValueChange = {}
)
}
}
}
I just put some colors to show how the widgets occupy the space in their parent Column.
Try this
class YourActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
window.setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING
);
setContent { /* Composable Content */ }
}
}

Compose: What is the proper way to add to a (Lazy)Column enough bottom padding to avoid the FAB to cover its content?

The issue is the very similar to the one discussed here for Flutter, but happens in native Android using Jetpack Compose:
Is there a way to dynamically add bottom padding to a (Lazy)Column so its content is never obscured by the FAB?
The FAB is added to the Scaffold so I would expect some way to get this padding dynamically.
You can try this though this may not be a good solution,
#Composable
fun MyScreen() {
var fabHeight by remember {
mutableStateOf(0)
}
val heightInDp = with(LocalDensity.current) { fabHeight.toDp() }
Scaffold(
floatingActionButton = {
FloatingActionButton(
modifier = Modifier.onGloballyPositioned {
fabHeight = it.size.height
},
shape = CircleShape,
onClick = {},
) {
Icon(imageVector = Icons.Filled.Add, contentDescription = "icon")
}
},
floatingActionButtonPosition = FabPosition.End
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(it),
contentPadding = PaddingValues(bottom = heightInDp + 16.dp)
) {
items(100) {
Text(text = " Hello world Hello world Hello world Hello world Hello world")
}
}
}
}
Edit: Just simply add the modified paddings to contentPadding of LazyColumn
The hardcoded 16.dp is added because internally the Scaffold implementations has a private property that offsets the fab from the bottom.
So having all of these will produce something like this:

How to place tabs at bottom of screen with bottom navbar in jetpack compose

I'm already using bottom navigation bar in app. For one composable screen, I need to place tabs at bottom of screen (above bottom bar) and its content to above tabs. How do I achieve that setup?
You can use a Scaffold with a bottomBar:
Scaffold(
scaffoldState = scaffoldState,
bottomBar = {
BottomAppBar() {
//....
}
},
content = { innerPadding ->
Column(
Modifier
.fillMaxSize()
.padding(bottom = innerPadding.calculateBottomPadding()),
) {
Column( modifier = Modifier.weight(1f)){
//Content
}
TabRow(selectedTabIndex = state) {
//....
}
}
}
)

fillMaxSize() on Scaffold content having no impact

I am trying to make a short content center-aligned on the screen inside a Scaffold content. Using fillMaxSize() on the scaffold contents seems like having no impact at all. How can I make this content full size?
Scaffold(
topBar = { },
floatingActionButtonPosition = FabPosition.End,
floatingActionButton = { },
bottomBar = { }
) { paddingValues ->
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(paddingValues)
) {
NavHost(
navController = navController,
startDestination = Screen.Running.route
) {
composable(Screen.Business.route) {
BusinessScreen(onSetAppTitle = { appTitle = it })
}
...
}
#Composable
fun BusinessScreen(onSetAppTitle: (String) -> Unit) {
LaunchedEffect(Unit) {
onSetAppTitle("Business Dashboard")
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxSize()
.background(Color.White)
.padding(16.dp)
) {
Text("Business Dashboard")
}
}
verticalScroll wraps content, and can be stretched to very long. That's why .fillMaxHeight (which is a part of fillMaxSize) doesn't work inside verticalScroll: it's ambiguous.
You need to set height explicitly. This is the case when you can pass it from onSizeChanged modifier added on the top Column.
But I believe that your composition is generally not really good: applying verticalScroll to a view on top of the layout tree is not the best solution.
Try adding verticalScroll inside each route only to those views that are really bigger than the screen.

How to implement BottomAppBar and BottomDrawer pattern using Android Jetpack Compose?

I'm building Android app with Jetpack Compose. Got stuck while trying to implement BottomAppBar with BottomDrawer pattern.
Bottom navigation drawers are modal drawers that are anchored to the bottom of the screen instead of the left or right edge. They are only used with bottom app bars. These drawers open upon tapping the navigation menu icon in the bottom app bar.
Description on material.io, and direct link to video.
I've tried using Scaffold, but it only supports side drawer. BottomDrawer appended to Scaffold content is displayed in content area and BottomDrawer doesn't cover BottomAppBar when open. Moving BottomDrawer after Scaffold function doesn't help either: BottomAppBar is covered by some invisible block and prevents clicking buttons.
I've also tried using BottomSheetScaffold, but it doesn't have BottomAppBar slot.
If Scaffold doesn't support this pattern, what would be correct way to implement it? Is it possible to extend Scaffold component? I fear that incorrect implementation from scratch might create issues later, when I'll try to implement navigation and snackbar.
I think the latest version of scaffold does have a bottom app bar parameter
They (Google Devs) invite you in the Jetpack Compose Layouts pathway to try adding other Material Design Components such as BottomNavigation or BottomDrawer to their respective Scaffold slots, and yet do not give you the solution.
BottomAppBar does have its own slot in Scaffold (i.e. bottomBar), but BottomDrawer does not - and seems to be designed exclusively for use with the BottomAppBar explicitly (see API documentation for the BottomDrawer).
At this point in the Jetpack Compose pathway, we've covered state hoisting, slots, modifiers, and have been thoroughly explained that from time to time we'll have to play around to see how best to stack and organize Composables - that they almost always have a naturally expressable way in which they work best that is practically intended.
Let me get us set up so that we are on the same page:
class MainActivity : ComponentActivity() {
#ExperimentalMaterialApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
LayoutsCodelabTheme {
// A surface container using the 'background' color from the theme
Surface(color = MaterialTheme.colors.background) {
LayoutsCodelab()
}
}
}
}
}
That's the main activity calling our primary/core Composable. This is just like in the codelab with the exception of the #ExperimentalMaterialApi annotation.
Next is our primary/core Composable:
#ExperimentalMaterialApi
#Composable
fun LayoutsCodelab() {
val ( gesturesEnabled, toggleGesturesEnabled ) = remember { mutableStateOf( true ) }
val scope = rememberCoroutineScope()
val drawerState = rememberBottomDrawerState( BottomDrawerValue.Closed )
// BottomDrawer has to be the true core of our layout
BottomDrawer(
gesturesEnabled = gesturesEnabled,
drawerState = drawerState,
drawerContent = {
Button(
modifier = Modifier.align( Alignment.CenterHorizontally ).padding( top = 16.dp ),
onClick = { scope.launch { drawerState.close() } },
content = { Text( "Close Drawer" ) }
)
LazyColumn {
items( 25 ) {
ListItem(
text = { Text( "Item $it" ) },
icon = {
Icon(
Icons.Default.Favorite,
contentDescription = "Localized description"
)
}
)
}
}
},
// The API describes this member as "the content of the
// rest of the UI"
content = {
// So let's place the Scaffold here
Scaffold(
topBar = {
AppBarContent()
},
//drawerContent = { BottomBar() } // <-- Will implement a side drawer
bottomBar = {
BottomBarContent(
coroutineScope = scope,
drawerState = drawerState
)
},
) {
innerPadding ->
BodyContent( Modifier.padding( innerPadding ).fillMaxHeight() )
}
}
)
}
Here, we've leveraged the Scaffold exactly as the codelab in the compose pathway suggests we should. Notice my comment that drawerContent is an auto-implementation of the side-drawer. It's a rather nifty way to bypass directly using the [respective] Composable(s) (material design's modal drawer/sheet)! However, it won't work for our BottomDrawer. I think the API is experimental for BottomDrawer, because they'll be making changes to add support for it to Composables like Scaffold in the future.
I base that on how difficult it is to use the BottomDrawer, designed for use solely with BottomAppBar, with the Scaffold - which explicitly contains a slot for BottomAppBar.
To support BottomDrawer, we have to understand that it is an underlying layout controller that wraps the entire app's UI, preventing interaction with anything but its drawerContent when the drawer is open. This requires that it encompasses Scaffold, and that requires that we delegate necessary state control - to the BottomBarContent composable which wraps our BottomAppBar implementation:
#ExperimentalMaterialApi
#Composable
fun BottomBarContent( modifier: Modifier = Modifier, coroutineScope: CoroutineScope, drawerState: BottomDrawerState ) {
BottomAppBar{
// Leading icons should typically have a high content alpha
CompositionLocalProvider( LocalContentAlpha provides ContentAlpha.high ) {
IconButton(
onClick = {
coroutineScope.launch { drawerState.open() }
}
) {
Icon( Icons.Filled.Menu, contentDescription = "Localized description" )
}
}
// The actions should be at the end of the BottomAppBar. They use the default medium
// content alpha provided by BottomAppBar
Spacer( Modifier.weight( 1f, true ) )
IconButton( onClick = { /* doSomething() */ } ) {
Icon( Icons.Filled.Favorite, contentDescription = "Localized description" )
}
IconButton( onClick = { /* doSomething() */ } ) {
Icon( Icons.Filled.Favorite, contentDescription = "Localized description" )
}
}
}
The result shows us:
The TopAppBar at top,
The BottomAppBar at bottom,
Clicking the menu icon in the BottomAppBar opens our BottomDrawer, covering the BottomAppBar and entire content space appropriately while open.
The BottomDrawer is properly hidden, until either the above referenced button click - or gesture - is utilized to open the bottom drawer.
The menu icon in the BottomAppBar opens the drawer partway.
Gesture opens the bottom drawer partway with a quick short swipe, but as far as you guide it to otherwise.
You may have to do something as shown below..
Notice how the Scaffold is called inside the BottomDrawer().
It's confusing though how the documentation says "They (BottomDrawer) are only used with bottom app bars". It made me think I have to look for a BottomDrawer() slot inside Scaffold or that I have to call BottomDrawer() inside BottomAppBar(). In both cases, I experienced weird behaviours. This is how I worked around the issue. I hope it helps someone especially if you are attempting the code lab exercise in Module 5 of Layouts in Jetpack Compose from the Jetpack Compose course.
#ExperimentalMaterialApi
#Composable
fun MyApp() {
var selectedItem by rememberSaveable { mutableStateOf(1)}
BottomDrawer(
modifier = Modifier.background(MaterialTheme.colors.onPrimary),
drawerShape = Shapes.medium,
drawerContent = {
Column(Modifier.fillMaxWidth()) {
for(i in 1..6) {
when (i) {
1 -> Row(modifier = Modifier.clickable { }.padding(16.dp)){
Icon(imageVector = Icons.Rounded.Inbox, contentDescription = null)
Text(text = "Inbox")
}
2 -> Row(modifier = Modifier.clickable { }.padding(16.dp)){
Icon(imageVector = Icons.Rounded.Outbox, contentDescription = null)
Text(text = "Outbox")
}
3 -> Row(modifier = Modifier.clickable { }.padding(16.dp)){
Icon(imageVector = Icons.Rounded.Archive, contentDescription = null)
Text(text = "Archive")
}
}
}
}
},
gesturesEnabled = true
) {
Scaffold(
topBar = {
TopAppBar(
title = {
Text(text = "Learning Compose Layouts" )
},
actions = {
IconButton(onClick = { /*TODO*/ }) {
Icon(Icons.Filled.Favorite, contentDescription = null)
}
}
)
},
bottomBar = { BottomAppBar(cutoutShape = CircleShape, contentPadding = PaddingValues(0.dp)) {
for (item in 1..4) {
BottomNavigationItem(
modifier = Modifier.clipToBounds(),
selected = selectedItem == item ,
onClick = { selectedItem = item },
icon = {
when (item) {
1 -> { Icon(Icons.Rounded.MusicNote, contentDescription = null) }
2 -> { Icon(Icons.Rounded.BookmarkAdd, contentDescription = null) }
3 -> { Icon(Icons.Rounded.SportsBasketball, contentDescription = null) }
4 -> { Icon(Icons.Rounded.ShoppingCart, contentDescription = null) }
}
}
)
}
}
}
) { innerPadding -> BodyContent(
Modifier
.padding(innerPadding)
.padding(8.dp))
}
}
}

Categories

Resources