Bonsai
Features
- Multiplatform: Android, Desktop
- State-aware: changes in the tree will trigger recomposition
- Unlimited levels
- File System integration
- JSON integration
- Built-in DSL
- Expandable
- Selectable
- Clickable
- Styleable
- Extendable
Roadmap
- iOS support
Usage
Start by creating a new tree with rememberTree
. Use SimpleLeafNode
and SimpleBranchNode
to create leaf and branch nodes, respectively. Call the Bonsai()
composable to render the tree.
@Composable
fun BonsaiExample() {
val tree = rememberTree(
nodes = listOf(
SimpleLeafNode(
content = "A leaf node"
),
SimpleBranchNode(
content = "A branch node",
children = { node ->
listOf(
SimpleLeafNode(
content = "A child node",
parent = node
),
)
}
)
)
)
Bonsai(tree)
}
Output:
Take a look at the sample app for a working example.
File System integration
Import cafe.adriel.bonsai:bonsai-file-system
module to use it.
val tree = rememberTree<Path>(
nodes = fileSystemNodes(
// Also works with java.nio.file.Path and okio.Path
rootPath = File(path),
// To show or not the root directory in the tree
selfInclude = true
)
)
Bonsai(
tree = tree,
// Custom style
style = FileSystemBonsaiStyle()
)
Output:
JSON integration
Import cafe.adriel.bonsai:bonsai-json
module to use it.
val tree = rememberTree<Path>(
// Sample JSON from https://rickandmortyapi.com/api/character
nodes = jsonNodes(json)
)
Bonsai(
tree = tree,
// Custom style
style = JsonBonsaiStyle()
)
Output:
DSL
Looking for a simpler and less verbose way to create a tree? Here's a handy DSL for you.
val tree = Tree<String> {
Branch("bonsai") {
Branch("bonsai-core") {
Branch("build")
Branch("src") {
Branch("androidMain") {
Leaf("AndroidManifest.xml")
}
Branch("commonMain")
}
Leaf("build.gradle")
}
Branch("bonsai-file-system")
Branch("sample")
Leaf(".gitignore")
Leaf("build.gradle")
Leaf("README.md")
}
}
Output:
Expanding & Collapsing
Easy control the expanded/collapsed state of your Tree
:
toggleExpansion(node)
collapseAll()
/expandAll()
collapseRoot()
/expandRoot()
collapseFrom(minLevel)
/expandUntil(maxLevel)
collapseNode(node)
/expandNode(node)
Selecting
Selected/Unselected state is also pretty simple to control:
selectedNodes
(observable state backed bySnapshotStateList
)toggleSelection(node)
selectNode(node)
/unselectNode(node)
clearSelection()
Click handling
Its also possible to set custom click behaviors for your Tree
. Control single, double and long clicks by using the expand and select APIs.
Bonsai(
tree = tree,
onClick = { node ->
tree.clearSelection()
tree.toggleExpansion(node)
},
onDoubleClick = { node -> /* ... */ },
onLongClick = { node -> /* ... */ }
)
Styling
Change your Tree
appearance as you wish. Take a look at BonsaiStyle
class for all available customizations.
Bonsai(
tree = tree,
style = BonsaiStyle(
expandTransition = slideIn(),
collapseTransition = slideOut(),
toggleIconRotationDegrees = 0f,
toggleIcon = { node ->
rememberVectorPainter(
if (node is BranchNode && node.isExpanded) Icons.Outlined.UnfoldLess
else Icons.Outlined.UnfoldMore
)
},
nodeIconSize = 18.dp,
nodeShape = CutCornerShape(percent = 20),
nodeCollapsedIcon = { rememberVectorPainter(Icons.Outlined.Circle) },
nodeExpandedIcon = { rememberVectorPainter(Icons.Outlined.Adjust) },
nodeNameTextStyle = MaterialTheme.typography.overline
)
)
Output:
Custom nodes
Need a deeper customization? Just extend the SimpleLeafNode
and SimpleBranchNode
classes, or even the Node
interface, to fit your needs.
class CustomLeafNode : SimpleLeafNode<CustomClass>(/* setup */) {
@Composable
override fun BonsaiScope.NodeIcon() {
// Custom leaf node icon
}
@Composable
override fun BonsaiScope.NodeName() {
// Custom leaf node name
}
}
class CustomBranchNode : SimpleBranchNode<CustomClass>(/* setup */) {
@Composable
override fun BonsaiScope.NodeIcon() {
// Custom branch node icon
}
@Composable
override fun BonsaiScope.NodeName() {
// Custom branch node name
}
}
Import to your project
Add the desired dependencies to your module's build.gradle
:
implementation "cafe.adriel.bonsai:bonsai-core:${latest-version}"
implementation "cafe.adriel.bonsai:bonsai-file-system:${latest-version}"
implementation "cafe.adriel.bonsai:bonsai-json:${latest-version}"