Utku Yildiz
01/25/2024, 2:51 PMZsolt
01/25/2024, 2:53 PMUtku Yildiz
01/25/2024, 2:53 PMUtku Yildiz
01/25/2024, 3:02 PMclass BottomNavNode(
nodeContext: NodeContext,
private val serverRequests: ServerRequests,
private val spotlightModel: SpotlightModel<Routing> = SpotlightModel(
items = Routing.entries,
savedStateMap = nodeContext.savedStateMap,
),
private val spotlight: Spotlight<Routing> = Spotlight(
model = spotlightModel,
visualisation = {
SpotlightSlider(it, spotlightModel.currentState)
},
),
) : Node<BottomNavNode.Routing>(
appyxComponent = spotlight,
nodeContext = nodeContext,
) {
enum class Routing {
Home, Profile
}
override fun buildChildNode(
navTarget: Routing,
nodeContext: NodeContext
): Node<*> = when (navTarget) {
Routing.Home -> {
Timber.i("testing nav: Home Created")
HomeNode(
nodeContext = nodeContext,
appStore = appStore,
serverRequests = serverRequests,
beatsRepository = beatsRepository,
navigator = navigator,
videoRepository = videoRepository,
analytics = analytics,
)
}
Routing.Profile -> {
node(nodeContext) {
Box(modifier = Modifier
.fillMaxSize()
.clickable { navigationBarNavigate(Routing.Home) }
.background(Color.Cyan))
}
}
}
@Composable
override fun Content(modifier: Modifier) {
Box(modifier = modifier) {
AppyxNavigationContainer(appyxComponent = spotlight)
Box(modifier = Modifier
.fillMaxWidth()
.height(100.dp)
.align(Alignment.BottomCenter)
.background(Color.Cyan)
.clickable {
navigationBarNavigate(destination = if (spotlight.activeIndex.value == 0f) Routing.Profile else Routing.Home)
}
)
}
}
private fun navigationBarNavigate(destination: Routing, pressed: () -> Unit = {}) {
spotlight.activate(Routing.entries.indexOf(destination).toFloat())
if (spotlightModel.currentState.activeElement != destination) {
pressed()
}
}
}
This is my HomeNode
class HomeNode(
nodeContext: NodeContext,
private val videoRepository: VideoRepository,
private val analytics: Analytics,
private val serverRequests: ServerRequests,
private val beatsRepository: BeatsRepository,
private val appStore: AppStore,
private val navigator: Navigator,
private val backStack: BackStack<Routing> = BackStack(
model = BackStackModel(
initialTarget = Routing.EventFeed,
savedStateMap = nodeContext.savedStateMap,
),
visualisation = { BackStackSlider(it) }
),
) : Node<HomeNode.Routing>(
appyxComponent = backStack,
nodeContext = nodeContext,
) {
sealed class Routing : Screen {
@Parcelize
data object EventFeed : Routing()
}
override fun buildChildNode(navTarget: Routing, nodeContext: NodeContext) =
when (navTarget) {
is Routing.EventFeed -> node(nodeContext) { nodeModifier ->
val userDataStorage: UserDataStorage = koinInject()
HomeController()
}
}
@Composable
override fun Content(modifier: Modifier) {
AppyxNavigationContainer(appyxComponent = backStack)
}
private fun navigateTo(routing: Routing) {
backStack.push(routing)
}
}
Utku Yildiz
01/25/2024, 3:02 PMZsolt
01/25/2024, 3:05 PMUtku Yildiz
01/25/2024, 3:07 PMZsolt
01/25/2024, 3:08 PMUtku Yildiz
01/25/2024, 3:10 PMZsolt
01/25/2024, 6:25 PMUtku Yildiz
01/25/2024, 8:54 PMUtku Yildiz
01/26/2024, 12:46 AMUtku Yildiz
01/26/2024, 12:46 AMZsolt
01/26/2024, 3:29 PMbackStack
history is not persisted, it’s your UI state.
You can do one of the following:
1. Have a dedicated Node
for the content, not just a composable (you can either extract that part to a new XYZNode
, or repurpose your ProfileNode
for this). Then store your counter state in the Node
, rather than in the composable content. This way it will survive being removed from the composition. Just make sure the state is either a MutableState
or is guaranteed otherwise to notify the composition of an update (e.g. Flow
collected as state)
2. Use rememberSaveable
for state declared in your composable. You can check this sample from the sandbox app in the library, here’s how it persists the random colours associated to back stack elements: https://github.com/bumble-tech/appyx/blob/f5cc89fbdf9a168a70f8e9f11313e6524914179a[…]/appyx/demos/sandbox/navigation/node/backstack/BackStackNode.kt
I checked your code with both of the above options. Option 1 works immediately, however for some weird reason Option 2 did not (only for your code – our sample with the colour persistence still works). It’s not obvious at this point why not, so we’ll look into it further. Meanwhile you could go with Option 1 similar to:
class XYZNode(
nodeContext: NodeContext,
) : LeafNode(
nodeContext = nodeContext
) {
private var number: Int by mutableIntStateOf(0)
@Composable
override fun Content(modifier: Modifier) {
Column(
modifier = modifier
.fillMaxSize()
.background(Color.Green),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(text = "Number $number")
Button(onClick = { number++ }) {
Text(text = "Increment")
}
}
}
}
Zsolt
01/26/2024, 3:30 PMbackStack
instance in your ProfileNode
at all, so you could have it as a simple LeafNode
(but maybe this is just because you stripped it down for the demo)
2. If you only have a single target, Routing.Profile
, it adds some redundancy (your ProfileNode
already embodies Profile
, no need for an embedded routing for that)
3. You could potentially benefit from https://bumble-tech.github.io/appyx/navigation/features/material3/Zsolt
01/26/2024, 7:00 PMUtku Yildiz
01/27/2024, 9:25 AMUtku Yildiz
01/27/2024, 9:27 AMUtku Yildiz
01/27/2024, 9:31 AM