Below is my mainActivity code from my jetpack compose. I'm looking for the current route to show bottom navigation menu accourding to that.
class MainActivity : ComponentActivity(), UIConfiguration {
private var fontFamily = Poppins
var isDarkModeEnable by Delegates.notNull<Boolean>()
companion object {
lateinit var activity: WeakReference<Context>
}
override fun onDestroy() {
super.onDestroy()
activity.clear()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity = WeakReference(this)
isDarkModeEnable = this.resources.configuration.uiMode and
Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
setContent {
MonoTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
MainScreen(rememberNavController())
}
}
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
isDarkModeEnable =
newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES
textStyle(
fontFamily = fontFamily,
lightColor = Color.Blue,
darkColor = Color.White
)
}
override fun textStyle(
fontFamily: FontFamily,
lightColor: Color,
darkColor: Color
) {
val fontColor = darkColor.takeIf { isDarkModeEnable } ?: lightColor
uiElements.apply {
large = TextStyle(
fontFamily = fontFamily,
fontWeight = FontWeight.ExtraBold,
fontSize = 56.sp,
color = fontColor
)
h1 = TextStyle(
fontFamily = fontFamily,
fontWeight = FontWeight.Normal,
fontSize = 32.sp,
color = fontColor
)
h2 = TextStyle(
fontFamily = fontFamily,
fontWeight = FontWeight.Bold,
fontSize = 24.sp,
color = fontColor
)
subtitle1 = TextStyle(
fontFamily = fontFamily,
fontWeight = FontWeight.SemiBold,
fontSize = 20.sp,
color = fontColor
)
subtitle2 = TextStyle(
fontFamily = fontFamily,
fontWeight = FontWeight.SemiBold,
fontSize = 16.sp,
color = fontColor
)
body1 = TextStyle(
fontFamily = fontFamily,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
color = fontColor
)
button = TextStyle(
fontFamily = fontFamily,
fontWeight = FontWeight.SemiBold,
fontSize = 16.sp,
color = fontColor
)
caption = TextStyle(
fontFamily = fontFamily,
fontWeight = FontWeight.Normal,
fontSize = 12.sp,
color = fontColor
)
bottomBar = TextStyle(
fontFamily = fontFamily,
fontWeight = FontWeight.Normal,
fontSize = 10.sp,
color = fontColor
)
}
this#MainActivity.recreate()
}
#Composable
private fun MainScreen(
navHostController: NavHostController
) {
val showBnm = showBnm(navHostController)
Scaffold(
bottomBar = {
if (showBnm) {
BottomNavigationView(navController = navHostController)
}
},
) {
it.calculateTopPadding()
Column(modifier = Modifier.padding(bottom = if (showBnm) 58.dp else 0.dp)) {
SetupNavGraph(rememberNavController())
}
}
}
#Composable
private fun showBnm(navHostController: NavHostController): Boolean {
return when (currentRoute(navHostController)) {
Screens.Intro.route -> true
Screens.Input.route -> true
Screens.Report.route -> true
Screens.Setting.route -> true
else -> false
}
}
#Composable
private fun BottomNavigationView(navController: NavHostController) {
val items = listOf(
Screens.Input, Screens.Report, Screens.Setting
)
BottomNavigation(
/**modifier = Modifier
.padding(10.dp)
.graphicsLayer(
shape = RoundedCornerShape(topStart = 25.dp, topEnd = 25.dp),
clip = true,
),*/
backgroundColor = colorResource(
id = R.color.background
),
contentColor = colorResource(
id = R.color.bottomBarIcon
),
) {
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
items.forEach { item ->
BottomNavigationItem(icon = {
Icon(
painterResource(id = item.selectedIcon.takeIf {
currentRoute == item.route
} ?: item.notSelectedIcon),
contentDescription
= item.name
)
}, label = {
Text(
text = item.name, style = uiElements.bottomBar
)
},
selectedContentColor = colorResource(id = R.color.bottomBarIcon),
unselectedContentColor = colorResource(id = R.color.bottomBarIcon),
alwaysShowLabel = true,
selected = currentRoute == item.route,
onClick = {
navController.navigate(item.route) {
navController.graph.startDestinationRoute?.let { route ->
popUpTo(route) {
inclusive = true
saveState = true
}
}
launchSingleTop = true
restoreState = true
}
})
}
}
}
#Composable
private fun currentRoute(navHostController: NavHostController): String? {
val navBackStackEntry by navHostController.currentBackStackEntryAsState()
return navBackStackEntry?.destination?.route
}
#Preview()
#Composable
fun Bnm() = MainScreen(rememberNavController())
}
This is the currentRoute
#Composable
private fun currentRoute(navHostController: NavHostController): String? {
val navBackStackEntry by navHostController.currentBackStackEntryAsState()
return navBackStackEntry?.destination?.route
}
This function suppose to return the route of current destination but it is returning null. Therefore, I'm not be able to get the desired result.
Related
I need to add a scroll to a page.
but i get this error: Vertically scrollable component was measured with an infinity maximum height constraints, which is disallowed.
I do have Lazy Column inside a big column.. and I think that is the problem.
but i need the whole page to scroll.
here is my code:
#Composable
fun ContactDetails(contactViewModel: ContactViewModel, contactId: String?) {
val contact = contactViewModel.getContactById(contactId)
val optionsScrollState = rememberScrollState()
Column(
modifier = Modifier
.fillMaxWidth()
.verticalScroll(state = optionsScrollState),
horizontalAlignment = Alignment.Start
) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(350.dp)
) {
Card(modifier = Modifier.fillMaxWidth()) {
if (contact[0].image != null) {
Image(
painter = rememberImagePainter(data = contact[0].image),
contentDescription = "Image",
modifier = Modifier.fillMaxWidth(),
contentScale = ContentScale.Crop
)
} else {
Box(
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
.background(color = Color.Magenta),
contentAlignment = Alignment.Center,
) {
Text(
text = contact[0].first_name[0].toString(),
fontSize = 26.sp,
fontWeight = FontWeight.Bold
)
}
}
Box(
modifier = Modifier
.fillMaxWidth()
.fillMaxHeight(),
contentAlignment = Alignment.BottomStart
) {
Text(
text = "${contact.first().first_name} ${contact.first().last_name}",
modifier = Modifier,
color = Color.White,
style = MaterialTheme.typography.h4,
fontWeight = FontWeight.Bold
)
}
}
}
Divider(
color = Color.LightGray,
thickness = 2.dp,
)
phoneCard(contact = contact.first(), contactViewModel)
emailCard(contact = contact.first(), contactViewModel)
}
}
#Composable
fun phoneCard(contact: Contact, contactViewModel: ContactViewModel) {
val phoneList = contactViewModel.getPhoneListByContactId(contact.id)
Row(modifier = Modifier.padding(10.dp)) {
Icon(imageVector = Icons.Default.Phone, contentDescription = "Phone Icon")
Text(
text = "Phone:",
modifier = Modifier.padding(start = 10.dp),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.h6,
fontWeight = FontWeight.Bold
)
}
LazyColumn {
items(items = phoneList) {
Row(modifier = Modifier.padding(10.dp)) {
Text(text = it.type + ": ", fontWeight = FontWeight.Bold)
Text(text = it.number)
}
}
}
}
#Composable
fun emailCard(contact: Contact, contactViewModel: ContactViewModel) {
val emailList = contactViewModel.getEmailListByContactId(contact.id)
Row(modifier = Modifier.padding(10.dp)) {
Icon(imageVector = Icons.Default.Email, contentDescription = "Email Icon")
Text(
text = "Email:",
modifier = Modifier.padding(start = 10.dp),
textAlign = TextAlign.Center,
style = MaterialTheme.typography.h6,
fontWeight = FontWeight.Bold
)
}
LazyColumn {
items(items = emailList) {
Row(modifier = Modifier.padding(10.dp)) {
Text(text = it.type + ": ", fontWeight = FontWeight.Bold)
Text(text = it.address)
}
}
}
}
here is my emulator:
my description page
Specify a height in your LazyColumns for example:
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
) {
This is my first time using the BottomSheet.
I follow a tutorial and implemented the bottomsheet in a code with bottomnavbar. This was the result. {The bottomsheet overlaps the bottomnavbar, when it is collapsed}
As you can see the bottomsheet overlays the bottomnavbar.
Here's the code of the bottomSheet
#SuppressLint("UnusedMaterialScaffoldPaddingParameter")
#OptIn(ExperimentalMaterialApi::class)
#Composable
fun BottomSheetLayout() {
val bottomSheetItems = listOf(
BottomSheetItem(title = "Notification", icon = Icons.Default.Notifications),
...
)
val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(
bottomSheetState = BottomSheetState(BottomSheetValue.Collapsed)
)
val coroutineScope = rememberCoroutineScope()
BottomSheetScaffold(
scaffoldState = bottomSheetScaffoldState,
sheetShape = RoundedCornerShape(topEnd = 30.dp),
sheetContent = {
Column(
content = {
Spacer(modifier = Modifier.padding(16.dp))
Text(
text = "Create New",
modifier = Modifier
.fillMaxWidth(),
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold,
fontSize = 21.sp,
color = Color.White
)
LazyVerticalGrid(
//cells = GridCells.Fixed(3),
columns = GridCells.Fixed(3)
) {
items(bottomSheetItems.size, itemContent = {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxWidth()
.padding(top = 24.dp)
.clickable {
},
) {
Spacer(modifier = Modifier.padding(8.dp))
Icon(
bottomSheetItems[it].icon,
bottomSheetItems[it].title,
tint = Color.White
)
Spacer(modifier = Modifier.padding(8.dp))
Text(text = bottomSheetItems[it].title, color = Color.White)
}
})
}
}, modifier = Modifier
.fillMaxWidth()
.height(350.dp)
//.background(MaterialTheme.colors.primary)
.background(
brush = Brush.linearGradient(colors = listOf(MaterialTheme.colors.primary, Color.White)
)
)
.padding(16.dp)
)
},
) {
Column(modifier = Modifier.fillMaxSize()) {
Button(
modifier = Modifier
.padding(20.dp),
onClick = {
coroutineScope.launch {
if (bottomSheetScaffoldState.bottomSheetState.isCollapsed) {
bottomSheetScaffoldState.bottomSheetState.expand()
} else {
bottomSheetScaffoldState.bottomSheetState.collapse()
}
}
}
) {
Text(
text = "Click to show Bottom Sheet"
)
}
}
}
}
Thanks for your help in advance.
I'd also appreciate any tip and advice about the bottomSheet, thanks again.
for practicing with reusable components in Jetpack Compose, I started a little exercise.
See picture below.
As I imagine the green row, the input row, and the rows between have the same construction.
The first element got the available space, the second takes 50.dp, and the last one got 70.dp.
I tried to seperate the width into variables an pass this vars as modifiers to the single elements in the row. I thought if I need additionally fields, the I can extend it whitout any problem.
CODE DOESN'T WORK!
#Composable
fun groundComponent(
modifier: Modifier = Modifier,
spaceBetween: Dp = 0.dp,
color: Color,
content: #Composable () -> Unit
) {
Surface(
color = color
) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(spaceBetween)
) {
content()
}
}
}
#Composable
fun inputSection() {
val firstRowWidth = 1F
val secondRowWidth = 70.dp
val thirdRowWidth = 50.dp
Text("Add Ingredient")
groundComponent(color = Color.Green){
Text( text="Ingredient", modifier = Modifier.weight(firstRowWidth ))
Text( text="Amount", modifier = Modifier.widthIn(secondRowWidth ))
Text( text="Unit", modifier = Modifier.widthIn(thirdRowWidth ))
}
groundComponent{
Text( text="Sugar", modifier = Modifier.weight(firstRowWidth ))
Text( text="500", modifier = Modifier.widthIn(secondRowWidth ))
Text( text="gr", modifier = Modifier.widthIn(thirdRowWidth ))
}
groundComponent{
Text( text="Carrot", modifier = Modifier.weight(firstRowWidth ))
Text( text="1.5", modifier = Modifier.widthIn(secondRowWidth ))
Text( text="kg", modifier = Modifier.widthIn(thirdRowWidth ))
}
groundComponent{
TextField(
value = "newIngredient",
onValueChange = {},
modifier = Modifier.weight(firstRowWidth ))
TextField(
value = "newAmount",
onValueChange = {},
modifier = Modifier.widthIn(secondRowWidth )
)
TextField(
value = "newUnit",
onValueChange = {},
modifier = Modifier.widthIn(thirdRowWidth )
)
}
Button(onClick={}){Text("add")}
}
I got several errors with the .weight modifier.
So how is the right aproach to solve such a situation.
Thanks!
Modifier.weight is a Modifier that defined in specific scopes such as RowScope and ColumnScope. To be able to use modifiers that are defined in specific scopes you need to add Receiver to your content. BoxScope as Modifier.align() that is defined for instance, you can define your scopes either.
#Composable
fun GroundComponent(
modifier: Modifier = Modifier,
spaceBetween: Dp = 0.dp,
color: Color=Color.Unspecified,
content: #Composable RowScope.() -> Unit
) {
Surface(
color = color
) {
// Can't call content here because it has RowScope as receiver
// content()
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(spaceBetween)
) {
content()
}
}
}
Also in InputSection you define weight fractions as
val firstRowWidth = 1F
val secondRowWidth = 70.dp
val thirdRowWidth = 50.dp
these values should be proportionate to each other
if you set 1/5/6 for instance. or between 0f-1f
And by convention you can name Composable with capital initial letter since they are considered as widgets.
Thanks for your reply and your pretty good explanation!
With your help I solved my problem this way.
#Composable
fun InputRowGroundComponent(
modifier: Modifier = Modifier,
spaceBetweenElements: Dp = 0.dp,
color: Color,
content: #Composable RowScope.() -> Unit
) {
Surface(
color = color
) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(spaceBetweenElements),
verticalAlignment = Alignment.CenterVertically
) {
content()
}
}
}
#Composable
fun OverviewHeader(
modifier: Modifier = Modifier,
text: String
) {
Text(
modifier = modifier,
text = text,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
textAlign = TextAlign.Center
)
}
#Composable
fun OverviewContent(
modifier: Modifier = Modifier,
text: String
) {
Text(
modifier = modifier,
text = text,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
)
}
#Preview(showBackground = true, widthDp = 460)
#Composable
fun testPrev() {
val rowWeights = listOf(6F,3F,2F)
val rowSpacing = 8.dp
val indentation = 10.dp
Column(
modifier = Modifier.padding(8.dp),
verticalArrangement = Arrangement.spacedBy(rowSpacing)
) {
InputRowGroundComponent(
modifier = Modifier.heightIn(45.dp),
spaceBetweenElements = rowSpacing,
color = Color.Green
) {
OverviewHeader(text = "Ingredient", modifier = Modifier.weight(rowWeights[0]))
OverviewHeader(text = "Amount", modifier = Modifier.weight(rowWeights[1]))
OverviewHeader(text = "Unit", modifier = Modifier.weight(rowWeights[2]))
}
InputRowGroundComponent(
modifier = Modifier.heightIn(30.dp),
spaceBetweenElements = rowSpacing,
color = Color.Unspecified
) {
OverviewContent(text = "Sugar", modifier = Modifier.weight(rowWeights[0]).padding(start=indentation))
OverviewContent(text = "500", modifier = Modifier.weight(rowWeights[1]).padding(start=indentation))
OverviewContent(text = "gr", modifier = Modifier.weight(rowWeights[2]).padding(start=indentation))
}
InputRowGroundComponent(
modifier = Modifier.heightIn(30.dp),
spaceBetweenElements = rowSpacing,
color = Color.Unspecified
) {
OverviewContent(text = "Carrot", modifier = Modifier.weight(rowWeights[0]).padding(start=indentation))
OverviewContent(text = "1.5", modifier = Modifier.weight(rowWeights[1]).padding(start=indentation))
OverviewContent(text = "kg", modifier = Modifier.weight(rowWeights[2]).padding(start=indentation))
}
InputRowGroundComponent(
spaceBetweenElements = rowSpacing,
color = Color.Unspecified
) {
TextField(value = "", onValueChange = {}, modifier = Modifier.weight(rowWeights[0]))
TextField(value = "", onValueChange = {}, modifier = Modifier.weight(rowWeights[1]))
TextField(value = "", onValueChange = {}, modifier = Modifier.weight(rowWeights[2]))
}
Button(
modifier = Modifier.fillMaxWidth(),
onClick = { /*Todo*/ },
content = {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Filled.Add,
contentDescription = "Add Ingredient"
)
Text(
text = "Add"
)
}
}
)
}
}
Is this approach now right?
I want the bottom bar to be where I show it at the bottom, but it will overlap with the surface, so I want the bottom bar at the bottom and the bottom bar on the surface. I tried to put it in the box, but I couldn't do that and I don't want to create your own dictionary text corrupted, I want to adjust accordingly. I couldn't adapt this to my own code. Please help me how can I do this.
my screen
#Composable
fun CreateDicScreen() {
var text = remember { mutableStateOf("") }
Card()
Surface(color = Color.White, modifier = Modifier
.requiredHeight(600.dp)
.fillMaxWidth(),
shape = RoundedCornerShape(60.dp).copy(topStart = ZeroCornerSize, topEnd = ZeroCornerSize)) {
Column(modifier = Modifier
.padding(5.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(
painter = painterResource(id = R.drawable.resim ),
contentDescription = "image",
modifier = Modifier
.padding(5.dp)
.requiredHeight(300.dp)
.requiredWidth(300.dp)
)
Spacer(modifier = Modifier.padding(16.dp))
OutlinedTextField(
value = text.value,
onValueChange = { text.value = it },
modifier= Modifier
.fillMaxWidth()
.padding(5.dp)
,
textStyle = TextStyle(color = Color.Black),
label = { Text(text = "dictionary name") },
colors = TextFieldDefaults.outlinedTextFieldColors(
//focusedBorderColor = Color.Blue,
unfocusedBorderColor = Color.Blue
),
)
Spacer(modifier=Modifier.padding(5.dp))
Button(onClick = {},
modifier= Modifier
.padding(5.dp)
.fillMaxWidth(),
colors = ButtonDefaults.buttonColors(
backgroundColor = orangish,
contentColor = Color.White),
shape = shapes.medium,
contentPadding = PaddingValues(8.dp),
) {
Text(text = "Save")
}
}
}
}
#Composable
fun Card(){
Surface(color = purplish, modifier = Modifier.fillMaxSize()) {
Column(verticalArrangement = Arrangement.Bottom,
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.offset(y = (-30).dp)
) {
Spacer(modifier = Modifier.padding(vertical = 8.dp))
Text(text = "create your own dictionary", color = Color.White)
}
}
}
#Composable
fun bottomBar(){
var bottomState = remember {
mutableStateOf("Home")
}
Scaffold(
bottomBar = {
BottomNavigation(
modifier = Modifier
.clip(shape = RoundedCornerShape(topStart = 30.dp, topEnd = 30.dp)),
backgroundColor = Color(0xFFFEDBD0),
contentColor = Color(0xFF442c2E)
) {
BottomNavigationItem(
selected = bottomState.equals("Home") ,
onClick = { bottomState.equals("Home")},
label = { Text(text = "Home")},
icon = { Icon(imageVector = Icons.Default.Home, contentDescription = null)}
)
BottomNavigationItem(
selected = bottomState.equals("Account") ,
onClick = { bottomState.equals("Account")},
label = { Text(text = "Account")},
icon = { Icon(imageVector = Icons.Default.AccountCircle, contentDescription = null)}
)
BottomNavigationItem(
selected = bottomState.equals("Search") ,
onClick = { bottomState.equals("Search")},
label = { Text(text = "Search")},
icon = { Icon(imageVector = Icons.Default.Search, contentDescription = null)}
)
BottomNavigationItem(
selected = bottomState.equals("Setting") ,
onClick = { bottomState.equals("Setting") },
label = { Text(text = "Setting")},
icon = { Icon(imageVector = Icons.Default.Settings, contentDescription = null)}
)
}
}
){
}
}
my screen right now
First, you can show bottomNavigation with Scaffold as you wrote in your code, but you don't need to use Scaffold if it's only purpose is to show bottomNavigation (inside your bottomBar() composable). You can use Box.
#Composable
fun bottomBar() {
val bottomState = remember {
mutableStateOf("Home")
}
Box {
BottomNavigation(
modifier = Modifier
.clip(shape = RoundedCornerShape(topStart = 30.dp, topEnd = 30.dp)),
backgroundColor = Color(0xFFFEDBD0),
contentColor = Color(0xFF442c2E)
) {
BottomNavigationItem(
selected = bottomState.equals("Home"),
onClick = { bottomState.equals("Home") },
label = { Text(text = "Home") },
icon = { Icon(imageVector = Icons.Default.Home, contentDescription = null) }
)
BottomNavigationItem(
selected = bottomState.equals("Account"),
onClick = { bottomState.equals("Account") },
label = { Text(text = "Account") },
icon = {
Icon(imageVector = Icons.Default.AccountCircle,
contentDescription = null)
}
)
BottomNavigationItem(
selected = bottomState.equals("Search"),
onClick = { bottomState.equals("Search") },
label = { Text(text = "Search") },
icon = { Icon(imageVector = Icons.Default.Search, contentDescription = null) }
)
BottomNavigationItem(
selected = bottomState.equals("Setting"),
onClick = { bottomState.equals("Setting") },
label = { Text(text = "Setting") },
icon = { Icon(imageVector = Icons.Default.Settings, contentDescription = null) }
)
}
}
}
To show your bottomBar() on screen, you have several ways to do that. You can use Box again:
#Composable
fun CreateDicScreen() {
var text = remember { mutableStateOf("") }
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
Card()
bottomBar()
Box(modifier = Modifier.fillMaxHeight()) {
Surface(
color = Color.White,
modifier = Modifier
.requiredHeight(600.dp)
.fillMaxWidth(),
shape = RoundedCornerShape(60.dp).copy(topStart = ZeroCornerSize,
topEnd = ZeroCornerSize)
) {
...
}
}
}
}
With this approach we were forced to set contentAlignment to Bottom, and to wrap Surface with another Box layout and fill height of it.
Second way if you want bottomBar() to be in your Surface (inside composable "Card" (as you named it)), you can do that like this:
#Composable
fun CreateDicScreen() {
var text = remember { mutableStateOf("") }
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.BottomCenter) {
Card()
Box(modifier = Modifier.fillMaxHeight()) {
Surface(
color = Color.White,
modifier = Modifier
.requiredHeight(600.dp)
.fillMaxWidth(),
shape = RoundedCornerShape(60.dp).copy(topStart = CornerSize(0.dp),
topEnd = CornerSize(0.dp))
) {
...
}
}
}
}
#Composable
fun Card() {
Surface(color = Color.Magenta, modifier = Modifier.fillMaxSize()) {
Column(
verticalArrangement = Arrangement.Bottom,
horizontalAlignment = Alignment.CenterHorizontally,
) {
Spacer(modifier = Modifier.padding(vertical = 8.dp))
bottomBar()
}
}
}
and the bottomBar() stays as I mentioned above.
Extra:
I recommend you to use Scaffold for you design (if you don't have specific reason to put your bottomBar in Surface) because it has all features you need here. Here is your design only with Scaffold:
#Composable
fun CreateDicScreen() {
var text = remember { mutableStateOf("") }
Scaffold(
modifier = Modifier
.fillMaxSize(),
bottomBar = { bottomBar() }
) {
Surface(
color = Color.LightGray,
modifier = Modifier
.requiredHeight(600.dp)
.fillMaxWidth(),
shape = RoundedCornerShape(60.dp).copy(topStart = CornerSize(0.dp),
topEnd = CornerSize(0.dp))
) {
...
}
}
}
P.S. While trying to run your code on my Android I've received several times
java.lang.IllegalArgumentException: Corner size in Px can't be negative(topStart = 78.75, topEnd = -196350.0, bottomEnd = 0.0, bottomStart = 0.0)!
change your ZeroCornerSize for Surface to CornerSize(0.dp) to avoid this error.
Your Screen on phone
Here is my code.
DrawerCompose
#Composable
fun DrawerCompose(
modifier: Modifier = Modifier,
onDestinationClicked: (route: String) -> Unit
) {
val versionName = BuildConfig.VERSION_NAME
val empno by remember {
mutableStateOf("")
}
val password by remember {
mutableStateOf("")
}
Column(
modifier = Modifier
.background(MaterialTheme.colors.primarySurface)
.fillMaxSize()
) {
Box(
modifier = Modifier
.fillMaxWidth()
) {
Column(
modifier = Modifier
.padding(20.dp)
) {
Text(
text = "Title",
fontSize = 25.sp,
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold,
color = Color.White
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "ID",
fontSize = 25.sp,
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold,
color = Color.White
)
OutlinedTextField(
value = empno,
onValueChange = {
},
modifier = Modifier
.fillMaxWidth(),
label = { Text(text = "ID") },
singleLine = true,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Next
),
keyboardActions = KeyboardActions(
onNext = {
}
)
)
Text(
text = "Password",
fontSize = 25.sp,
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold,
color = Color.White
)
OutlinedTextField(
value = password,
onValueChange = {
},
modifier = Modifier
.fillMaxWidth(),
label = { Text(text = "Password") },
singleLine = true,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
onDone = {
}
)
)
Text(
text = "v $versionName",
fontSize = 25.sp,
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold,
color = Color.White
)
}
}
}
}
MenuListScreen
#OptIn(ExperimentalFoundationApi::class)
#Composable
fun HomeScreen(
navController: NavController,
onNavigateToMenuDetailScreen: (String) -> Unit,
viewModel: MenuListViewModel,
) {
val context = LocalContext.current
val menuList = viewModel.menuList.value
val scope = rememberCoroutineScope()
val scaffoldState = rememberScaffoldState(
rememberDrawerState(initialValue = DrawerValue.Closed)
)
val loading = viewModel.loading.value
val dialogQueue = viewModel.dialogQueue
Scaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
TopAppBarCompose(
title = "Title",
navigationIcon = {
IconButton(onClick = {
scope.launch {
scaffoldState.drawerState.open()
}
}) {
Icon(imageVector = Icons.Filled.Menu, contentDescription = "")
}
}
)
},
scaffoldState = scaffoldState,
drawerContent = {
DrawerCompose(
onDestinationClicked = { route ->
scope.launch {
scaffoldState.drawerState.close()
}
}
)
},
drawerGesturesEnabled = true
) {
MenuList(
loading = loading,
menus = menuList,
onNavigateToSubmenuScreen = onNavigateToMenuDetailScreen
)
}
}
MenuListViewModel
#HiltViewModel
class MenuListViewModel #Inject constructor(
private val restoreMenus: RestoreMenus,
private val assetsManager: AssetsManager,
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
val id: MutableState<String> = mutableStateOf("")
val password: MutableState<String> = mutableStateOf("")
fun onChangeEmpNo(id: String) {
this.id.value = id
}
fun onChangePassword(password: String) {
this.password.value = password
}
}
If the value in the ID TextField in the drawer changes, the empno value in the viewModel changes, and if the value in the ID in the viewModel changes, the value in the ID TextField in the drawer changes.
I'd like to know how to communicate with Viewmodel and Child Compose.
Edit
I solved that pass the viewmodel variables to drawer, and viewmodel function
You have to pass callback function
#Composable
fun DrawerCompose(
modifier: Modifier = Modifier,
onDestinationClicked: (route: String) -> Unit,
onIdChange: (value: String) -> Unit,
onPasswordChange: (value: String) -> Unit,
) {
val versionName = BuildConfig.VERSION_NAME
val empno by remember {
mutableStateOf("")
}
val password by remember {
mutableStateOf("")
}
Column(
modifier = Modifier
.background(MaterialTheme.colors.primarySurface)
.fillMaxSize()
) {
Box(
modifier = Modifier
.fillMaxWidth()
) {
Column(
modifier = Modifier
.padding(20.dp)
) {
Text(
text = "Title",
fontSize = 25.sp,
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold,
color = Color.White
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "ID",
fontSize = 25.sp,
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold,
color = Color.White
)
OutlinedTextField(
value = empno,
onValueChange = onIdChange,
modifier = Modifier
.fillMaxWidth(),
label = { Text(text = "ID") },
singleLine = true,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Next
),
keyboardActions = KeyboardActions(
onNext = {
}
)
)
Text(
text = "Password",
fontSize = 25.sp,
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold,
color = Color.White
)
OutlinedTextField(
value = password,
onValueChange = onPasswordChange,
modifier = Modifier
.fillMaxWidth(),
label = { Text(text = "Password") },
singleLine = true,
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number,
imeAction = ImeAction.Done
),
keyboardActions = KeyboardActions(
onDone = {
}
)
)
Text(
text = "v $versionName",
fontSize = 25.sp,
textAlign = TextAlign.Center,
fontWeight = FontWeight.Bold,
color = Color.White
)
}
}
}
}
After that pass those function from main screen
#OptIn(ExperimentalFoundationApi::class)
#Composable
fun HomeScreen(
navController: NavController,
onNavigateToMenuDetailScreen: (String) -> Unit,
viewModel: MenuListViewModel,
) {
val context = LocalContext.current
val menuList = viewModel.menuList.value
val scope = rememberCoroutineScope()
val scaffoldState = rememberScaffoldState(
rememberDrawerState(initialValue = DrawerValue.Closed)
)
val loading = viewModel.loading.value
val dialogQueue = viewModel.dialogQueue
Scaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
TopAppBarCompose(
title = "Title",
navigationIcon = {
IconButton(onClick = {
scope.launch {
scaffoldState.drawerState.open()
}
}) {
Icon(imageVector = Icons.Filled.Menu, contentDescription = "")
}
}
)
},
scaffoldState = scaffoldState,
drawerContent = {
DrawerCompose(
onDestinationClicked = { route ->
scope.launch {
scaffoldState.drawerState.close()
}
},
viewModel.onChangeEmpNo,
viewModel.onChangePassword
)
},
drawerGesturesEnabled = true
) {
MenuList(
loading = loading,
menus = menuList,
onNavigateToSubmenuScreen = onNavigateToMenuDetailScreen
)
}
}