ModalBottomSheet is showing above BottomBar in Jetpack Compose - android

I have BottomBar added in ComposeActivity. BottomBar is responsible for showing 3 composable. I don't want to add ModalBottomSheet to Activity instead I have added ModalBottomSheet inside 3rd Screen Composable. When ModalBottomSheet is invoked it is displayed above the BottomBar. How ModalBottomSheet can be displayed when Call to Action is done from 3rd screen Composable and it should be above BottomBar.
Any help will be appreciated.
It should display over BottomBar and on Bottom.

Just Position your ModalBottomSheetLayout on top of the Scaffold component.
Example:
AppTheme() {
ModalBottomSheetLayout(){ //<-- Here
Scaffold(){
//<-- Not Here or below
NavigationGraph(){
}
}
}
}
and call bottomSheet from NavHost.
Also you can hide Bottombar with action from screen but its long way;
*it may vary depending on your component child structure
Screen:
#Composable
fun 3rdScreen(onClickForHideBottomBar:() -> Unit){
Button(onClick = { onClickForHideBottomBar()}) {
Text(text = "Hide BottomBar")
}
}
NavGraph:
#Composable
fun NavigationGraph(onClickForHideBottomBar:() -> Unit,){
NavHost(){
composable(){
3rdScreen(onClickForHideBottomBar ={onClickForHideBottomBar()})
}
}
}
Scaffold:
var bottomBarVisibility by remember { mutableStateOf(false)}
Scaffold(
bottomBar = {BottomNavigationView(bottomBarVisibility=bottomBarVisibility)}){
NavigationGraph(onClickForHideBottomBar = bottomBarVisibility = !bottomBarVisibility){}
}
BottomNavigationView:
#Composable
fun BottomNavigationView(bottomBarVisibility: Boolean){
AnimatedVisibility(visibleState =MutableTransitionState(bottomBarVisibility)){
BottomNavigation()
}
}

Related

Jetpack compose Bottom sheet in multi screens

What is the best way to implement bottom sheet for multiple screens in jetpack compose? Do we have to define Bottom sheet layout in each screen? Then what to do if we wanted our bottom sheet to overlap on bottom nav bar?
You can create a custom layout like
MyAppCustomLayout(
showBottomBar: Boolean = false,
state: ModalBottomSheetState = ModalBottomSheetState(initialValue =
ModalBottomSheetValue.Hidden),
sheetContent: #Composable () -> Unit = {},
content: #Composable () -> Unit)
{
ModalBottomSheetLayout(
sheetState = state,
sheetContent = { sheetContent() })
{
Scaffold(
bottomBar = if(showBottomBar)
{{
YourBottomNavigationView()
}}
else {{}})
{ content() }
}
}
And use it anywhere in your app like below.
val state = rememberModalBottomSheetState()
MyAppCustomLayout(
state = state,
sheetcontent = {
Column {
Text("Some bottomSheet content")
}
})
{
Column {
Text("Some content")
}
}
If you are using Jetpack Navigation Compose in your project, you might consider using Jetpack Navigation Compose Material to implement it.
For more detail, refer to the samples

How to integrate AlertDialog with Navigation component in Jetpack Compose?

I am using Jetpack Compose and the Android navigation component. When I am on a screen with an AlertDialog, I am unable to navigate back. I guess it's due to the AlertDialog catching the back button event. However I don't know how to connect the AlertDialog to the navigation component? Is there any official way or best practice to do this? Here's my code:
// sample with a screen and a "navigate to dialog" button.
// once the button is pressed, an AlertDialog is shown.
// Using the back button while the AlertDialog is open has no effect ):
#Composable
fun MyNavHost(navController: NavHostController, modifier: Modifier = Modifier) {
NavHost(
navController = navController,
startDestination = "start_route",
modifier = modifier
) {
composable("start_route") {
Text("start screen")
}
// this is my screen with the dialog
dialog("dialog_route") {
AlertDialog(
onDismissRequest = { /*TODO*/ }, // guess I need to connect this to the navigation component? bug how?
title = {
Text(text = "Dialog title")
},
text = {
Text(text = "I am a dialog")
},
buttons = {}
)
}
}
}
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MyJetpackComposeTheme {
val navController = rememberNavController()
Scaffold() { innerPadding ->
Column {
Button(onClick = { navController.navigate("dialog_route") }) {
Text("navigate to dialog")
}
MyNavHost(navController, modifier = Modifier.padding(innerPadding))
}
}
}
}
}
}
As per the dialog documentation:
This is suitable only when this dialog represents a separate screen in your app that needs its own lifecycle and saved state, independent of any other destination in your navigation graph. For use cases such as AlertDialog, you should use those APIs directly in the composable destination that wants to show that dialog.
So you shouldn't be using a dialog destination at all: a dialog destination is specifically and only for providing the content lambda of a regular Dialog. What your code is actually doing is creating an empty Dialog (i.e., you don't emit any composables in the lambda you pass to dialog, then creating another AlertDialog stacked on top of that empty dialog. That's not what you want to be doing.
Instead, you should be following the AlertDialog docs and directly creating the AlertDialog where you want it to be used, setting your own boolean for when it should be shown/hidden.

Single NavHost with BottomBar and Sign Page in Android Jetpack compose

How to handle Single NavHost in Single Activity approach in Multi Module structure in Android Jetpack compose.
Did not see any example with below scenario to handle single NavHost in Single Activity.
Navigation should start from splash screen.
Custom Splash Screen -> Sign in Screen -> Home Screen with BottomBar
NavHost(navController, startDestination = "Splash")
{
composable(route = "Splash") {
Splash()
}
composable(route = "Sign In") {
SignIn()
}
composable(route = "Home") {
Home()
}
}
BottomBar has 3 options to navigate between each bottom bar items.
Where to add BottomBar in above scenario?
Updated as per comments
Scaffold(
bottomBar = { BottomBar(navController = navController) }) {
// sub graph for Home?
}

Jetpack Compose : How to keep UI state across pages/composable?

Is there a way to save the UI state of a Composable so that when switching between Composable their UI state is identical to when the view was left ?
I've tried using rememberLazyListState() (which uses rememberSaveable) to save the scroll state of a LazyColumn but it doesn't seems to work when coming back to the Composable.
Any ideas ?
Edit : I am using NavControllerto handle the navigation between the Composable
I just figured out how to do it. The idea is to hoist the LazyListState to the Composable managing the view navigation.
#Composable
fun AppScreenNav(screen: Screen) {
val listState = rememberLazyListState()
when (screen) {
Screen.Home -> Home()
Screen.Favorites -> Favorites(listState)
}
}
#Composable
fun Favorites(listState: LazyListState) {
val favorites: List<String> by rememberSaveable { mutableStateOf(List(1000) { "Favorites $it" }) }
LazyColumn(
state = listState
) {
items(favorites) { item ->
Text(
color = Color.Black,
text = item,
)
}
}
}
Here we are hoisting the list state to the parent component. When switching between the Home() and Favorites() composable the list scrolling state should remains identical.

Bottom Nav Bar overlaps screen content in Jetpack Compose

I have a BottomNavBar which is called inside the bottomBar of a Scaffold.
Every screen contains a LazyColumn which displays a bunch of images.
For some reason when I finish scrolling, the BottomNavBar overlaps the lower part of the items list.
How can I fix this?
Here the set content of MainActivity
SetContent{
Scaffold(
topBar = {
TopAppBar(
title = { Text(text = "tartufozon") },
actions = {
IconButton(onClick = { Timber.d("Mail clicked") }) {
Icon(Icons.Default.Email, contentDescription = null)
}
}
)
},
bottomBar = {
BottomNavBar(navController = navController)
}
) {
BottomNavScreensController(navController = navController)
}
}
As per the API definition for Scaffold, your inner content (the trailing lambda you have your BottomNavScreensController in), is given a PaddingValues object that gives you the right amount of padding for your top app bar, bottom bar, etc.
Right now, you're not referencing that padding at all and hence, your content is not padded in. This is what is causing your overlap.
You can add a Box around your BottomNavScreensController to apply the padding, or pass the padding directly into your BottomNavScreensController so that each individual screen can correctly apply the padding; either solution works.
Scaffold(
topBar = {
//
},
bottomBar = {
//
}
) { innerPadding ->
// Apply the padding globally to the whole BottomNavScreensController
Box(modifier = Modifier.padding(innerPadding)) {
BottomNavScreensController(navController = navController)
}
}
}
Following ianhanniballake's answer and its comments and just to save you few minutes. The code would be something like:
Scaffold(
topBar = {
//
},
bottomBar = {
//
}
) { innerPadding ->
Box(modifier = Modifier.padding(
PaddingValues(0.dp, 0.dp, 0.dp, innerPadding.calculateBottomPadding()) {
BottomNavScreensController(navController = navController)
}
}
}
You no longer need to do any calculations. In the scaffold content, do:
content = { padding ->
Column(
modifier = Modifier.padding(padding)
) {...
Scaffold(
bottomBar = {
BottomNavigationBar(navController = navController)
}
) { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) {
DestinationsNavHost(
navGraph = NavGraphs.root,
navController = navController,
engine = navHostEngine
)
}
}
Add your layouts and views in content with its padding like in the example:
Scaffold(
content = {
Box(modifier = Modifier.padding(it)) {
//add your layout here
}
},
topBar = {
//your top bar
},
floatingActionButton = {
//your floating action bar
},
bottomBar = {
//your bottom navigation
}
)
Following ianhanniballake answer, if you have a list in your main screen and want to show/hide the bottom bar. It will perform unexpected behavior when you back to list screen (the scroll state is in very bottom) and show the navigation bar.The list item will scroll up a bit which cause the list doesn't fully scrolled to the bottom.
This is because of when you back to the list screen and want to show the bottom bar, the list already there. Then the bottom bar will show and list will not calculate the innerPadding for more.
To handle this, just pass the innerPadding into the list screen like this:
Scaffold(
bottomBar = {
BottomNavBar(navController)
}) { innerPadding ->
NavHost(
navController = navController,
startDestination = MovieListDirections.destination
) {
MovieListDirections.screenWithPaddingBottomBar(
this,
innerPadding.calculateBottomPadding() // pass here
)
}
Then in the list screen
#Composable
fun MovieListMainView(viewModel: MovieListViewModel = hiltViewModel(),
bottomBarHeight: Float) {
val movieList by viewModel.movieList.collectAsState()
var navBarHeight by rememberSaveable { mutableStateOf(0f) }
if (bottomBarHeight != 0f) {
navBarHeight = bottomBarHeight
}
MovieListLayout().MovieGridLayout(
modifier = Modifier.padding(0.dp, 0.dp, 0.dp, navBarHeight.dp),
movies = movieList.movieList.movieItems,
onItemClick = {
viewModel.onMovieClicked(it)
})
}

Categories

Resources