Search Docs
Loading...
Skip to content

Dock

The dock provides quick access to content libraries and editing tools, appearing at the bottom of the editor interface. This guide shows you how to customize dock items and their layout to match your app’s content strategy and user workflow.

Explore a complete code sample on GitHub.

Dock Architecture#

Dock

The dock displays horizontally at the bottom of the editor and provides quick access to content libraries and editing tools. It adapts its content based on the selected editor solution.

Key Components:

  • Dock.Button - Pre-built button implementation with icon and text
  • Dock.Scope - Provides access to the engine, event handler, and editor context

Configuration#

Dock customization is part of the EditorConfiguration, therefore, in order to configure the dock we need to configure the EditorConfiguration. Below you can find the list of available configurations of the dock. To demonstrate the default values, all properties are assigned to their default values unless specified otherwise.

Available configuration properties:

  • scope - Scope of this component. Every new value will trigger recomposition of all ScopedPropertys such as visible, enterTransition, exitTransition etc. Consider using Compose androidx.compose.runtime.State objects in the lambdas for granular recompositions over updating the scope, since scope change triggers full recomposition of the component. Ideally, scope should be updated when the parent scope (scope of the parent component) is updated and when you want to observe changes from the Engine. By default the scope is updated only when the parent scope is updated.

  • visible - Control dock visibility. Default value is always true.

  • modifier - Jetpack Compose modifier of this component. By default empty modifier is applied.

  • enterTransition - Animation for when the dock appears. Default value is always no enter transition.

  • exitTransition - Animation for when the dock disappears. Default value is always no exit transition.

  • decoration - Apply custom styling like background, shadows, and padding. The default decoration adds background color and applies paddings to the dock.

  • listBuilder - Define the complete list of dock items and their order. Items are only displayed when visible returns true. By default, listBuilder does not add anything to the dock.

  • horizontalArrangement - Layout arrangement for dock items. Controls spacing and alignment of items within the dock. Default value is Arrangement.Start.

  • itemDecoration - decoration of the items in the dock. Useful when you want to add custom background, foreground, shadow, paddings etc to the items. Prefer using this decoration when you want to apply the same decoration to all the items, otherwise set decoration to individual items. Default value is always no decoration.

The Dock.Scope (this instance in all lambdas) provides access to the engine, event handler, and editor context. Use this for advanced customization logic and to maintain consistency with the current editor state.

EditorConfiguration.remember {
dock = {
Dock.remember {
scope = {
remember(this) { Dock.Scope(parentScope = this) }
}
modifier = { Modifier }
visible = { true }
enterTransition = { EnterTransition.None }
exitTransition = { ExitTransition.None }
decoration = {
// Also available via Dock.DefaultDecoration
Box(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surface1.copy(alpha = 0.95f))
.padding(vertical = 10.dp),
) {
it()
}
}
listBuilder = { Dock.ListBuilder.remember { /* Add items */ } }
horizontalArrangement = { Arrangement.SpaceEvenly }
// Default value is { it() }
itemDecoration = {
Box(modifier = Modifier.padding(2.dp)) {
it()
}
}
}
}
}

ListBuilder Configuration#

You can configure the items of the dock using two main approaches:

New List Builder#

The simplest approach is to create a new list builder from scratch. In this example, we add both already declared and custom buttons:

Dock.ListBuilder.remember {
add {
Dock.Button.remember {
id = { EditorComponentId("my.package.dock.button.custom") }
vectorIcon = null
textString = { "Custom Button" }
onClick = {}
}
}
add { Dock.Button.rememberSystemGallery() }
add { Dock.Button.rememberSystemCamera() }
add { Dock.Button.rememberElementsLibrary() }
add { Dock.Button.rememberStickersLibrary() }
add { Dock.Button.rememberImagesLibrary() }
add { Dock.Button.rememberTextLibrary() }
}

All available predefined buttons are listed below.

Modify Existing List Builder#

In case you already have an existing list builder and want to make simple modifications on it (prepend, append, replace, insert or remove an item) without touching the order invoke modify on the ListBuilder.

// Makes sense to use only with builders that are already available and cannot be modified by you directly.
val existingListBuilder = Dock.ListBuilder.remember {
add { Dock.Button.rememberSystemGallery() }
add { Dock.Button.rememberSystemCamera() }
add { Dock.Button.rememberTextLibrary() }
add { Dock.Button.rememberShapesLibrary() }
}
existingListBuilder.modify {
addFirst {
Dock.Button.remember {
id = { EditorComponentId("my.package.dock.button.first") }
vectorIcon = null
textString = { "First Button" }
onClick = {}
}
}
addLast {
Dock.Button.remember {
id = { EditorComponentId("my.package.dock.button.last") }
vectorIcon = null
textString = { "Last Button" }
onClick = {}
}
}
addAfter(id = Dock.Button.Id.systemGallery) {
Dock.Button.remember {
id = { EditorComponentId("my.package.dock.button.afterSystemGallery") }
vectorIcon = null
textString = { "After System Gallery" }
onClick = {}
}
}
addBefore(id = Dock.Button.Id.systemCamera) {
Dock.Button.remember {
id = { EditorComponentId("my.package.dock.button.beforeSystemCamera") }
vectorIcon = null
textString = { "Before System Camera" }
onClick = {}
}
}
replace(id = Dock.Button.Id.textLibrary) {
Dock.Button.remember {
id = { EditorComponentId("my.package.dock.button.replacedTextLibrary") }
vectorIcon = null
text = { "Replaced Text Library" }
onClick = {}
}
}
remove(id = Dock.Button.Id.shapesLibrary)
}

Available modification operations:

  • addFirst - prepends new Dock.Item:
addFirst {
Dock.Button.remember {
id = { EditorComponentId("my.package.dock.button.first") }
vectorIcon = null
textString = { "First Button" }
onClick = {}
}
}
  • addLast - appends new Dock.Item:
addLast {
Dock.Button.remember {
id = { EditorComponentId("my.package.dock.button.last") }
vectorIcon = null
textString = { "Last Button" }
onClick = {}
}
}
  • addAfter - adds new Dock.Item right after the item with the provided id:
addAfter(id = Dock.Button.Id.systemGallery) {
Dock.Button.remember {
id = { EditorComponentId("my.package.dock.button.afterSystemGallery") }
vectorIcon = null
textString = { "After System Gallery" }
onClick = {}
}
}
  • addBefore - adds new Dock.Item right before the item with the provided id:
addBefore(id = Dock.Button.Id.systemCamera) {
Dock.Button.remember {
id = { EditorComponentId("my.package.dock.button.beforeSystemCamera") }
vectorIcon = null
textString = { "Before System Camera" }
onClick = {}
}
}
  • replace - replaces the Dock.Item with the provided id with new item:
replace(id = Dock.Button.Id.textLibrary) {
Dock.Button.remember {
id = { EditorComponentId("my.package.dock.button.replacedTextLibrary") }
vectorIcon = null
text = { "Replaced Text Library" }
onClick = {}
}
}
  • remove - removes the Dock.Item with the provided id:
remove(id = Dock.Button.Id.shapesLibrary)

Dock.Item Configuration#

Each Dock.Item is an EditorComponent. Its id must be unique which is a requirement for proper component management. Depending on your needs there are multiple ways to define an item. In this example, we demonstrate your options with increasing complexity.

Use Predefined Buttons#

The most basic option is to use our predefined buttons which are provided as composable functions. All available predefined buttons are listed below.

Create New Buttons#

If our predefined buttons don’t fit your needs you can create your own. To demonstrate the default values, all properties are assigned to their default values unless specified otherwise:

@Composable
fun rememberDockButton() = Dock.Button.remember {
id = { EditorComponentId("my.package.dock.button.newButton") }
scope = {
remember(this) { Dock.ItemScope(parentScope = this) }
}
modifier = { Modifier }
visible = { true }
enterTransition = { EnterTransition.None }
exitTransition = { ExitTransition.None }
// Default value is { it() }
decoration = {
Surface(color = MaterialTheme.colorScheme.background) {
it()
}
}
onClick = { editorContext.eventHandler.send(EditorEvent.Sheet.Open(SheetType.Volume())) }
// Default value is null
icon = {
Icon(
imageVector = IconPack.Music,
contentDescription = null,
)
}
// Default value is null
text = {
Text(
text = "Hello World",
)
}
enabled = { true }
}

Required and optional properties:

  • id - the id of the button. Note that it is highly recommended that every unique EditorComponent has a unique id. By default property contains a random value.

  • scope - scope of this component. Every new value will trigger recomposition of all ScopedPropertys such as visible, enterTransition, exitTransition etc. Consider using Compose androidx.compose.runtime.State objects in the lambdas for granular recompositions over updating the scope, since scope change triggers full recomposition of the component. Ideally, scope should be updated when the parent scope (scope of the parent component) is updated and when you want to observe changes from the Engine. By default the scope is updated only when the parent component scope is updated.

  • modifier - Jetpack Compose modifier of this component. By default empty modifier is applied.

  • visible - whether the button should be visible. Default value is always true.

  • enterTransition - transition of the button when it enters the parent composable. Default value is always no enter transition.

  • exitTransition - transition of the button when it exits the parent composable. Default value is always no exit transition.

  • decoration - decoration of the button. Useful when you want to add custom background, foreground, shadow, paddings etc. Default value is always no decoration.

  • onClick - the callback that is invoked when the button is clicked. By default it is a no-op.

  • icon - the icon content of the button. If null, it will not be rendered. Default value is null.

  • text - the text content of the button. If null, it will not be rendered. Default value is null.

  • tint - the tint color of the content. By default it is MaterialTheme.colorScheme.onSurfaceVariant.

  • enabled - whether the button is enabled. Default value is always true.

This gives full control over the content of the button. However, there are simpler configuration options if you do not want to fully customize text and icon composables. Let’s have a look at this example:

@Composable
fun rememberDockButtonSimple() = Dock.Button.remember {
id = { EditorComponentId("my.package.dock.button.newButton") }
scope = {
remember(this) { Dock.ItemScope(parentScope = this) }
}
modifier = { Modifier }
visible = { true }
enterTransition = { EnterTransition.None }
exitTransition = { ExitTransition.None }
// Default value is { it() }
decoration = {
Surface(color = MaterialTheme.colorScheme.background) {
it()
}
}
onClick = { editorContext.eventHandler.send(EditorEvent.CloseEditor()) }
// Default value is null
vectorIcon = { IconPack.Music }
// Default value is null
textString = { "Hello World" }
tint = { MaterialTheme.colorScheme.onSurfaceVariant }
enabled = { true }
contentDescription = null
}

It has three differences:

  1. icon is replaced with vectorIcon lambda, that returns ImageVector instead of drawing the icon content.
  2. text is replaced with textString lambda, that returns String instead of drawing the text content.
  3. contentDescription property is added that is used by accessibility services to describe what the button does. Provide it whenever the button does not contain visible text explaining its action.

Create Custom Items#

For completely custom implementations, use EditorComponent.remember and render your custom UI inside decoration. To demonstrate the default values, all properties are assigned to their default values unless specified otherwise:

@Composable
fun rememberCustomItem() = EditorComponent.remember {
id = { EditorComponentId("my.package.dock.newCustomItem") }
scope = {
remember(this) { Dock.ItemScope(parentScope = this) }
}
modifier = { Modifier }
visible = { true }
enterTransition = { EnterTransition.None }
exitTransition = { ExitTransition.None }
decoration = {
Box(
modifier = Modifier
.fillMaxHeight()
.clickable {
Toast
.makeText(editorContext.activity, "Hello World Clicked!", Toast.LENGTH_SHORT)
.show()
},
) {
Text(
modifier = Modifier.align(Alignment.Center),
text = "Hello World",
)
}
}
}

Required and optional properties:

  • id - the id of the custom item. Note that it is highly recommended that every unique EditorComponent has a unique id. By default it contains a random value.

  • scope - scope of this component. Every new value will trigger recomposition of all ScopedPropertys such as visible, enterTransition, exitTransition etc. Consider using Compose androidx.compose.runtime.State objects in the lambdas for granular recompositions over updating the scope, since scope change triggers full recomposition of the component. Ideally, scope should be updated when the parent scope (scope of the parent component) is updated and when you want to observe changes from the Engine. By default it is derived from the parent component scope.

  • modifier - Jetpack Compose modifier of this component. By default empty modifier is applied.

  • visible - whether the custom item should be visible. Default value is always true.

  • enterTransition - transition of the custom item when it enters the parent composable. Default value is always no enter transition.

  • exitTransition - transition of the custom item when it exits the parent composable. Default value is always no exit transition.

  • decoration - render your custom item here. You are responsible for drawing the UI, handling clicks, and applying any custom styling. Default value is always no decoration.

List of Available Dock.Buttons#

As you often saw in the previous sections, there are composable functions that look like this: Dock.Button.remember{name}. All these functions return a Dock.Button, they are public and can be used in your application. Note that all the properties of these functions have default values, therefore, you do not need to provide any values, however, if you want to modify any of the properties, it is exactly the same as described in Create New Buttons section.

ButtonIDDescription
Dock.Button.rememberElementsLibraryDock.Button.Id.elementsLibraryOpens library sheet with elements via EditorEvent.Sheet.Open. By default, the LibraryCategory is picked from the Asset Library.
Dock.Button.rememberOverlaysLibraryDock.Button.Id.overlaysLibraryOpens library sheet with overlays via EditorEvent.Sheet.Open. By default, the LibraryCategory is picked from the Asset Library.
Dock.Button.rememberImagesLibraryDock.Button.Id.imagesLibraryOpens library sheet with images via EditorEvent.Sheet.Open. By default, the LibraryCategory is picked from the Asset Library.
Dock.Button.rememberTextLibraryDock.Button.Id.textLibraryOpens library sheet with text via EditorEvent.Sheet.Open. By default, the LibraryCategory is picked from the Asset Library.
Dock.Button.rememberShapesLibraryDock.Button.Id.shapesLibraryOpens library sheet with shapes via EditorEvent.Sheet.Open. By default, the LibraryCategory is picked from the Asset Library.
Dock.Button.rememberStickersLibraryDock.Button.Id.stickersLibraryOpens library sheet with stickers via EditorEvent.Sheet.Open. By default, the LibraryCategory is picked from the Asset Library.
Dock.Button.rememberAudiosLibraryDock.Button.Id.audiosLibraryOpens library sheet with audios via EditorEvent.Sheet.Open. By default, the LibraryCategory is picked from the Asset Library.
Dock.Button.rememberSystemGalleryDock.Button.Id.systemGalleryOpens the system gallery via EditorEvent.LaunchContract.
Dock.Button.rememberSystemCameraDock.Button.Id.systemCameraOpens the system camera via EditorEvent.LaunchContract.
Dock.Button.rememberImglyCameraDock.Button.Id.imglyCameraOpens the IMG.LY camera via EditorEvent.LaunchContract. Note that the button can be used only if ly.img:camera:<same version as editor> dependency is added in your build.gradle file. For more information, check the camera documentation.
Dock.Button.rememberReorderDock.Button.Id.reorderOpens reorder sheet via EditorEvent.Sheet.Open.
Dock.Button.rememberAdjustmentsDock.Button.Id.adjustmentsOpens adjustment sheet via EditorEvent.Sheet.Open.
Dock.Button.rememberFilterDock.Button.Id.filterOpens filter sheet via EditorEvent.Sheet.Open.
Dock.Button.rememberEffectDock.Button.Id.effectOpens effect sheet via EditorEvent.Sheet.Open.
Dock.Button.rememberBlurDock.Button.Id.blurOpens blur sheet via EditorEvent.Sheet.Open.
Dock.Button.rememberCropDock.Button.Id.cropOpens crop sheet via EditorEvent.Sheet.Open.
Dock.Button.rememberResizeAllDock.Button.Id.resizeAllOpens resize sheet via EditorEvent.Sheet.Open.