Quick start guide¶
You can check out Composable navigation, which explains the concepts you'll encounter in this guide.
The scope of this guide¶
The steps below will cover:
- Integrating Appyx into your project
- Creating a very simple
Node
hierarchy - We'll use a simple back stack for navigation
- We'll see how to change transition animations easily
This should be enough to get you started as a rudimentary application structure.
1. Add Appyx to your project¶
Please refer to the Downloads to find the relevant artifacts (also depending on whether you're doing this in a multiplatform project or not).
For the scope of this quick start guide, you will need to add dependencies for:
2. Create a root Node¶
class RootNode(
nodeContext: NodeContext
) : LeafNode(
nodeContext = nodeContext
) {
@Composable
override fun Content(modifier: Modifier) {
Text("Hello world!")
}
}
3. Connect to your platform¶
Plug your root node into your platform: Multiplatform | Node hosts.
Use the multiplatform imports Appyx provides in any of the code snippets from now on, such as:
import com.bumble.appyx.utils.multiplatform.Parcelable
import com.bumble.appyx.utils.multiplatform.Parcelize
import com.bumble.appyx.utils.multiplatform.RawValue
4. Define children¶
A single leaf node isn't all that interesting. Let's add some children to the root!
First, let's define the possible set of children using a sealed class. We'll refer them via these navigation targets:
import com.bumble.appyx.utils.multiplatform.Parcelable
import com.bumble.appyx.utils.multiplatform.Parcelize
/**
* You can create this class inside the body of RootNode
*
* Note: You must apply the 'kotlin-parcelize' plugin to use @Parcelize
* https://developer.android.com/kotlin/parcelize
*/
sealed class NavTarget : Parcelable {
@Parcelize
object Child1 : NavTarget()
@Parcelize
object Child2 : NavTarget()
@Parcelize
object Child3 : NavTarget()
}
Next, let's modify RootNode
so it extends Node
instead of LeafNode
:
class RootNode(
nodeContext: NodeContext
) : Node<NavTarget>(
appyxComponent = TODO("We will come back to this in Step 5"),
nodeContext = nodeContext
) {
Node
expects us to implement the abstract method buildChildNode
. This is how we relate navigation targets to actual children. Let's use these helper methods to define some placeholders for the time being – we'll soon make them more appealing:
override fun buildChildNode(reference: NavTarget, nodeContext: NodeContext): Node =
when (reference) {
NavTarget.Child1 -> node(nodeContext) { Text(text = "Placeholder for child 1") }
NavTarget.Child2 -> node(nodeContext) { Text(text = "Placeholder for child 2") }
NavTarget.Child3 -> node(nodeContext) { Text(text = "Placeholder for child 3") }
}
Great! With this mapping created, we can now just refer to children using the sealed class elements, and Appyx will be able to relate them to other nodes.
5. Add a back stack¶
The project wouldn't compile just yet. Node
expects us to pass an instance of an AppyxComponent
– the main control structure in any case when we want to add children. No need to worry now – for simplicity, let's just go with a simple BackStack
implementation here:
class RootNode(
nodeContext: NodeContext,
private val backStack: BackStack<NavTarget> = BackStack(
model = BackStackModel(
initialTarget = NavTarget.Child1,
savedStateMap = nodeContext.savedStateMap,
),
visualisation = { BackStackFader(it) }
)
) : Node<NavTarget>(
nodeContext = nodeContext,
appyxComponent = backStack // pass it here
) {
With this simple addition we've immediately gained a lot of power! Now we can use the back stack's API to add, replace, pop children with operations like:
backStack.push(NavTarget.Child2) // will add a new navigation target to the end of the stack and make it active
backStack.replace(NavTarget.Child3) // will replace the currently active child
backStack.pop() // will remove the currently active child and restore the one before it
Since we passed the back stack to the Node
, all such changes will be immediately reflected. We only need to add it to the composition:
@Composable
override fun Content(modifier: Modifier) {
Column(
modifier = modifier
) {
// Let's include the elements of our component into the composition
AppyxNavigationContainer(
appyxComponent = backStack,
modifier = Modifier.weight(0.9f)
)
// Let's also add some controls so we can test it
Row(
horizontalArrangement = Arrangement.Center,
modifier = Modifier
.fillMaxWidth()
.weight(0.1f)
) {
TextButton(onClick = { backStack.push(NavTarget.Child1) }) {
Text(text = "Push child 1")
}
TextButton(onClick = { backStack.push(NavTarget.Child2) }) {
Text(text = "Push child 2")
}
TextButton(onClick = { backStack.push(NavTarget.Child3) }) {
Text(text = "Push child 3")
}
TextButton(onClick = { backStack.pop() }) {
Text(text = "Pop")
}
}
}
}
6. Visualisations, transitions¶
Adding a different visual representation of our model and its state changes is as simple as changing this:
visualisation = { BackStackFader(it) }
to this:
visualisation = { BackStackSlider(it) }
or this:
visualisation = { BackStackParallax(it) }
or this:
visualisation = { BackStack3D(it) }
Be sure to check the Back stack documentation where you can also find live previews of the above visualisations.
Need something more custom?
- You can create your own visualisations.
- Instead of a back stack, you can also find other Components in the library, or you can create your own.
7. Proper child nodes¶
As a last step, let's replace at least one of the child placeholders with another proper node.
Let's create a dedicated class:
class SomeChildNode(
nodeContext: NodeContext
) : LeafNode(
nodeContext = nodeContext
) {
@Composable
override fun Content(modifier: Modifier) {
Text("This is SomeChildNode")
}
}
Now we can update the buildChildNode
method in RootNode
so that the target Child3
refers to this node. It should work out of the box:
override fun buildChildNode(reference: NavTarget, nodeContext: NodeContext): Node =
when (reference) {
NavTarget.Child1 -> node(nodeContext) { Text(text = "Placeholder for child 1") }
NavTarget.Child2 -> node(nodeContext) { Text(text = "Placeholder for child 2") }
NavTarget.Child3 -> SomeChildNode(nodeContext)
}
What's next?¶
Congrats, you've just built your first Appyx tree!
You can repeat the same pattern and make any embedded children also a Node
with their own children, navigation models, and transitions. As complexity grows, generally you would:
- Have a
LeafNode
- At some point make it a
Node
and add children to it - At some point extract the increasing complexity from a placeholder to another
LeafNode
- Repeat the same on children, go to
1.
Further reading¶
- Check out Model-driven navigation how to take your navigation to the next level
- You can (and probably should) also extract local business logic, the view, any any other components into separate classes and Plugins.