DrawBoxController API Reference¶
Overview¶
The DrawBoxController is the main interface for interacting with DrawBox. It manages state, handles intents, and provides methods for drawing operations, customization, and export.
Creating a Controller¶
Using rememberDrawBoxController¶
The simplest way to create and remember a controller across recompositions:
@Composable
fun MyDrawingScreen() {
val controller = rememberDrawBoxController()
// controller is retained across recompositions
}
Manual Creation¶
val controller = DrawBoxController()
Observable State¶
Observing Drawing State¶
@Composable
fun DrawingScreen() {
val controller = rememberDrawBoxController()
val state by controller.state.collectAsState()
// state contains all drawing information
println("Canvas background: ${state.bgColor}")
println("Current mode: ${state.mode}")
println("Elements on canvas: ${state.elements.size}")
}
The State object contains:
state.elements // List<Element> - All drawable elements
state.undoStack // List<Element> - Redo history
state.strokeColor // Color - Current stroke color
state.strokeWidth // Float - Current stroke width (pixels)
state.opacity // Float - Current opacity (0.0-1.0)
state.bgColor // Color - Canvas background color
state.mode // Mode - Current drawing mode
Observing Undo/Redo Availability¶
val canUndo by controller.canUndo.collectAsState(false)
val canRedo by controller.canRedo.collectAsState(false)
Button(onClick = { controller.undo() }, enabled = canUndo) {
Text("Undo")
}
Button(onClick = { controller.redo() }, enabled = canRedo) {
Text("Redo")
}
Drawing Mode Control¶
Switch Drawing Mode¶
// Freehand drawing
controller.setMode(Mode.PEN)
// Shape drawing
controller.setMode(Mode.RECTANGLE)
controller.setMode(Mode.CIRCLE)
controller.setMode(Mode.TRIANGLE)
controller.setMode(Mode.ARROW)
controller.setMode(Mode.LINE)
Get current mode:
val state by controller.state.collectAsState()
val currentMode = state.mode
when (currentMode) {
Mode.PEN -> println("Drawing freehand")
Mode.RECTANGLE -> println("Drawing rectangles")
// ... etc
}
Style & Appearance¶
Stroke Color¶
// Set stroke color for new drawings
controller.setColor(Color.Blue)
controller.setColor(Color.Red)
controller.setColor(Color(0xFF00FF00)) // Green
controller.setColor(Color(red = 1f, green = 0f, blue = 0f)) // RGB
Get current color:
val state by controller.state.collectAsState()
val currentColor = state.strokeColor
Stroke Width¶
// Set stroke width in pixels (1.0 to infinity)
controller.setStrokeWidth(5f) // Thin line
controller.setStrokeWidth(10f) // Medium
controller.setStrokeWidth(20f) // Thick
controller.setStrokeWidth(50f) // Very thick
// Get current width
val state by controller.state.collectAsState()
val currentWidth = state.strokeWidth
Opacity/Transparency¶
// Set opacity from 0.0 (fully transparent) to 1.0 (fully opaque)
controller.setOpacity(1.0f) // Opaque (default)
controller.setOpacity(0.8f) // 80% opaque
controller.setOpacity(0.5f) // 50% transparent
controller.setOpacity(0.2f) // 20% opaque
// Get current opacity
val state by controller.state.collectAsState()
val currentOpacity = state.opacity
Background Color¶
// Set canvas background color
controller.setBgColor(Color.White)
controller.setBgColor(Color.Black)
controller.setBgColor(Color.Gray)
controller.setBgColor(Color(0xFFF5F5F5)) // Light gray
History Management¶
Undo/Redo¶
// Undo the last drawing operation
controller.undo()
// Redo the last undone operation
controller.redo()
// Check if undo/redo is available
val canUndo by controller.canUndo.collectAsState(false)
val canRedo by controller.canRedo.collectAsState(false)
if (canUndo) {
controller.undo()
}
Reset Canvas¶
// Clear all drawings and reset canvas
controller.reset()
// This clears:
// - All elements
// - Undo/redo history
// - Does NOT reset colors or stroke width
Intent Handling¶
Send intents directly to the controller:
// For advanced users who want direct control
controller.onIntent(Intent.SetColor(Color.Blue))
controller.onIntent(Intent.SetMode(Mode.RECTANGLE))
For most use cases, use the high-level methods above.
Export & Import¶
Export as SVG¶
Export drawings as scalable vector graphics:
// Get SVG string
val svgString = controller.exportSvg()
// Save to file
File("drawing.svg").writeText(svgString)
// Or listen to export events
controller.events.collect { event ->
if (event is Event.SvgExported) {
val svg = event.svg
saveToFile(svg)
}
}
Export as PNG¶
Export drawings as raster image:
// Save as bitmap
controller.saveBitmap()
// Listen for PNG saved event
controller.events.collect { event ->
if (event is Event.PngSaved) {
val bitmap = event.bitmap
if (bitmap != null) {
saveBitmapToFile(bitmap)
} else if (event.throwable != null) {
handleError(event.throwable)
}
}
}
Export as JSON¶
Save/load drawing state:
// Export current state as JSON
val json = controller.exportPath()
// Save to preferences or file
preferences.saveDrawing(json)
// Later, load from JSON
val savedJson = preferences.loadDrawing()
controller.importPath(savedJson)
Event Listening¶
Listen to important events:
val controller = rememberDrawBoxController()
LaunchedEffect(Unit) {
controller.events.collect { event ->
when (event) {
is Event.ElementAdded -> {
println("Element added: ${event.element}")
}
is Event.ElementUpdated -> {
println("Element updated: ${event.element}")
}
is Event.ElementDeleted -> {
println("Element deleted: ${event.elementId}")
}
is Event.HistoryChanged -> {
println("Can undo: ${event.canUndo}, Can redo: ${event.canRedo}")
}
is Event.SvgExported -> {
println("SVG exported: ${event.svg.length} bytes")
}
is Event.PngSaved -> {
if (event.bitmap != null) {
println("PNG saved successfully")
} else {
println("Error saving PNG: ${event.throwable?.message}")
}
}
is Event.DrawingLoaded -> {
println("Drawing loaded")
}
is Event.Error -> {
println("Error: ${event.message}")
}
}
}
}
Complete Example¶
Here's a complete example using all major controller features:
@Composable
fun AdvancedDrawingScreen() {
val controller = rememberDrawBoxController()
val state by controller.state.collectAsState()
val canUndo by controller.canUndo.collectAsState(false)
val canRedo by controller.canRedo.collectAsState(false)
var showColorPicker by remember { mutableStateOf(false) }
var selectedColor by remember { mutableStateOf(Color.Red) }
// Listen to events
LaunchedEffect(Unit) {
controller.events.collect { event ->
when (event) {
is Event.SvgExported -> {
saveFile("drawing.svg", event.svg)
}
is Event.PngSaved -> {
if (event.bitmap != null) {
saveBitmap(event.bitmap)
}
}
is Event.Error -> {
showError(event.message)
}
else -> {}
}
}
}
Column(modifier = Modifier.fillMaxSize()) {
// Canvas
DrawBox(
state = state,
onIntent = controller::onIntent,
modifier = Modifier
.fillMaxSize()
.weight(1f)
)
// Control Panel
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
// Mode buttons
IconButton(onClick = { controller.setMode(Mode.PEN) }) {
Icon(Icons.Default.Edit, "Pen")
}
IconButton(onClick = { controller.setMode(Mode.RECTANGLE) }) {
Text("⬜")
}
// Color picker
Button(onClick = { showColorPicker = true }) {
Text("Color")
}
// Stroke width slider
Slider(
value = state.strokeWidth,
onValueChange = { controller.setStrokeWidth(it) },
valueRange = 1f..50f,
modifier = Modifier.weight(1f)
)
// Undo/Redo
IconButton(
onClick = { controller.undo() },
enabled = canUndo
) {
Text("↶")
}
IconButton(
onClick = { controller.redo() },
enabled = canRedo
) {
Text("↷")
}
// Export
IconButton(onClick = { controller.exportSvg() }) {
Text("SVG")
}
IconButton(onClick = { controller.saveBitmap() }) {
Text("PNG")
}
// Reset
IconButton(onClick = { controller.reset() }) {
Text("Reset")
}
}
}
if (showColorPicker) {
ColorPickerDialog(
onColorSelected = { color ->
controller.setColor(color)
selectedColor = color
showColorPicker = false
},
onDismiss = { showColorPicker = false }
)
}
}
Performance Considerations¶
- State collection: Only use
collectAsState()for values you need - Recomposition: Each state change triggers recomposition
- Memory: Large element lists may impact performance
- Export: SVG/PNG export is async and won't block UI
Thread Safety¶
DrawBox is safe to use from any thread. The StateFlow and event emissions are thread-safe.
// Safe to call from background threads
viewModelScope.launch(Dispatchers.IO) {
controller.setColor(Color.Blue)
controller.exportSvg()
}
Next: Learn about exporting drawings