I am trying to figure out how I can create a navigation when clicked on a certain box. Basically when the user clicks a box, it should navigate to a composable screen. The user can then select what they want from the screen and then navigate back to the "start" screen of choices.
All my boxes are in a lazyRow. How can I acheieve this ? I have set up 1 sealed class of objects with different names. 1 Navhost with all the respective composable screens. How can I link the 2 together to create navigation for each respective Box(items) when clicked ?
Sealed class:
sealed class HobbiesItem(var route: String, var title: String) {
object Item1 : HobbiesItem("Item1", "Item1")
object Item2 : HobbiesItem("Item2", "Item2")
object Item3 : HobbiesItem("Item3", "Item3")
object Item4 : HobbiesItem("Item4", "Item4")
object Item5 : HobbiesItem("Item5", "Item5")
object Item6 : HobbiesItem("Item6", "Item6")
object Item7 : HobbiesItem("Item7", "Item7")
}
NavHost:
@Composable
fun Navigation2(navController2: NavHostController) {
NavHost(navController2, startDestination = HobbiesItem.Item1.route) {
composable(HobbiesItem.Item1.route) {
Hobbies01()
}
composable(HobbiesItem.Item2.route) {
Hobbies02()
}
composable(HobbiesItem.Item3.route) {
Hobbies03()
}
composable(HobbiesItem.Item4.route) {
Hobbies04()
}
composable(HobbiesItem.Item5.route) {
Hobbies05()
}
composable(HobbiesItem.Item6.route) {
Hobbies06()
}
composable(HobbiesItem.Item7.route) {
Hobbies07()
}
}
}
My LazyRow with the Boxes:
How it looks in Emulator: https://gyazo.com/cc62241a5ceb4769eeaebd42d241d27b
// Displays all the items in a lazyRow
@OptIn(ExperimentalSnapperApi::class)
@Composable
fun Trying(HobbyRowItems: HobbiesItem, onItemClick: (HobbiesItem) -> Unit) {
val allStackHobbies = listOf(
HobbiesItem.Item1.route,
HobbiesItem.Item4.route,
HobbiesItem.Item7.route,
HobbiesItem.Item2.route,
HobbiesItem.Item3.route,
HobbiesItem.Item5.route,
HobbiesItem.Item6.route
)
val allItems = remember { (allStackHobbies).map { it } }
val horizontalContentPadding = 3.dp // Moves the content left or right of the LazyRow
val boxSize = 115.dp
val lazyListState10 = rememberLazyListState()
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
BoxWithConstraints {
val halfRowWidth = constraints.maxWidth / 2
LazyRow(
state = lazyListState10,
flingBehavior = rememberSnapperFlingBehavior(lazyListState10),
horizontalArrangement = Arrangement.spacedBy(16.dp),
contentPadding = PaddingValues(horizontal = horizontalContentPadding, vertical = 8.dp),
modifier = Modifier
.fillMaxWidth()
) {
itemsIndexed(allItems) { i, item ->
val opacity by remember { derivedStateOf {
val currentItemInfo = lazyListState10.layoutInfo.visibleItemsInfo
.firstOrNull { it.index == i }
?: return@derivedStateOf 0.5f
val itemHalfSize = currentItemInfo.size / 2
(1f - minOf(1f, abs(currentItemInfo.offset itemHalfSize - halfRowWidth).toFloat() / halfRowWidth) * 0.5f)
}
}
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.scale(opacity)
.alpha(opacity)
.size(boxSize)
.background(Color.Blue)
.clickable {
onItemClick.invoke(HobbyRowItems)
Log.d("Hobby", "Pressed Box")
}
) {
Text(item, color = Color.White)
}
}
}
}
}
}
CodePudding user response:
I have set up 1 sealed class of objects with different names. 1 Navhost with all the respective composable screens. How can I link the 2 together to create navigation for each respective Box(items) when clicked ?
You are running into a problem because your allItems
list contains only routes (strings) but your onItemClick
callback wants the whole HobbiesItem
.
What you can do here is instead of passing just a list of routes to your LazyRow
, you can pass a list of all HobbiesItem
s.
You could also hoist the items outside the Trying
composable, which would make your composable stateless and would let you control the state outside of it.
// Displays all the items in a lazyRow
@OptIn(ExperimentalSnapperApi::class)
@Composable
// change the function signature
fun Trying(items: List<HobbiesItem>, onItemClick: (HobbiesItem) -> Unit) {
// get rid of these
// val allStackHobbies = listOf(...)
// val allItems = remember { (allStackHobbies).map { it } }
// `remember` is not needed anymore because `items` are now passed
// into this composable as a parameter
// ...
LazyRow(
// ...
) {
itemsIndexed(items) { i, item -> // <-- change here to `items`
// ...
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
// ...
.clickable {
onItemClick(item) // <-- change here to `item`
Log.d("Hobby", "Pressed Box")
}
) {
// you probably wanted to show the title here instead of the route,
// even though in your example both are the same?
Text(item.title, color = Color.White) // <-- change here to `item.title`
}
}
}
// ...
}
Then, in your top composable (usually the composable in your main/landing Activity), where (I assume) you are currently displaying your Trying
composable, you have to display the NavHost
composable instead. In your case that would be your Navigation2
composable.
So something like this (here I am assuming you are using the ComponentActivity
, but is similar in other scenarios)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
// if you already have a reference to the navController2
// from elsewhere then remove the next line
val navController2 = rememberNavController()
MyTheme { // this would be the name of your theme
// you can wrap this into a Scaffold in the future
Navigation2(navController2)
}
}
}
}
And finally you connect them together where you will be displaying your Trying
composable, which I assume it would be your actual staring destination.
So make a new composable desination that calls your Trying
composable and change the starting destination so that it uses the same route.
@Composable
fun Navigation2(navController2: NavHostController) {
NavHost(navController2, startDestination = "ItemList") {
composable("ItemList") {
val allItems = listOf(
HobbiesItem.Item1, HobbiesItem.Item4, HobbiesItem.Item7, HobbiesItem.Item2,
HobbiesItem.Item3, HobbiesItem.Item5, HobbiesItem.Item6
)
Trying(
items = allItems,
// navigating on an item click
onItemClick = { item -> navController2.navigate(item.route) }
)
}
composable(HobbiesItem.Item1.route) {
Hobbies01()
}
composable(HobbiesItem.Item2.route) {
Hobbies02()
}
// ...
}
}