Search Docs
Loading...
Skip to content

Inspector Bar

The inspector bar provides context-sensitive editing controls that appear when you select a design element, offering tools specific to that element type like text formatting, image adjustments, or shape properties. This guide shows you how to customize these editing controls to match your app’s feature set and user experience goals.

Explore a complete code sample on GitHub.

Inspector Bar Architecture#

Inspector Bar

The inspector bar displays horizontally at the bottom when a design element is selected. It contains context-sensitive editing tools that adapt based on the selected element type (text, image, video, etc.).

Key Components:

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

Configuration#

Inspector bar customization is part of the EditorConfiguration, therefore, in order to configure the inspector bar we need to configure the EditorConfiguration. Below you can find the list of available configurations of the inspector bar. 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 when the parent scope is updated, when selection is changed to a different design block (or unselected), and when the parent of the currently selected design block is changed to a different design block.

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

  • visible - Control inspector bar visibility based on selection state. By default it is true only while a design block is selected and the current edit mode is not Crop.

  • enterTransition - Animation for when the inspector bar appears. By default, a vertical slide in animation is used.

  • exitTransition - Animation for when the inspector bar disappears. By default, a vertical slide out animation is used.

  • decoration - Apply custom styling like background, shadows, and padding. By default InspectorBar.DefaultDecoration adds a surface-colored background and horizontal padding.

  • listBuilder - Define the complete list of inspector bar items and their order. Items are only displayed when visible returns true.

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

  • itemDecoration - decoration of the items in the inspector bar. 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.

  • itemsRowEnterTransition - Animation for when the items row appears. By default, a horizontal slide in animation is used.

  • itemsRowExitTransition - Animation for when the items row disappears. Default value is always no exit transition.

The InspectorBar.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 {
inspectorBar = {
InspectorBar.remember {
// Implementation is too large, check the implementation of InspectorBar.rememberDefaultScope
scope = {
InspectorBar.rememberDefaultScope(parentScope = this)
}
modifier = { Modifier }
visible = { editorContext.safeSelection != null }
// Also available via InspectorBar.defaultEnterTransition
enterTransition = {
remember {
slideInVertically(
animationSpec = tween(
durationMillis = 400,
easing = CubicBezierEasing(0.05f, 0.7f, 0.1f, 1f),
),
initialOffsetY = { it },
)
}
}
// Also available via InspectorBar.defaultExitTransition
exitTransition = {
remember {
slideOutVertically(
animationSpec = tween(
durationMillis = 150,
easing = CubicBezierEasing(0.3f, 0f, 0.8f, 0.15f),
),
targetOffsetY = { it },
)
}
}
// Implementation is too large, check the implementation of InspectorBar.DefaultDecoration
decoration = { InspectorBar.DefaultDecoration(scope = this) { it() } }
listBuilder = { InspectorBar.ListBuilder.remember { /* Add items */ } }
horizontalArrangement = { Arrangement.Start }
// Also available via InspectorBar.defaultItemsRowEnterTransition
itemsRowEnterTransition = {
remember {
slideInHorizontally(
animationSpec = tween(400, easing = CubicBezierEasing(0.05F, 0.7F, 0.1F, 1F)),
initialOffsetX = { it / 3 },
)
}
}
// Also available via InspectorBar.defaultItemsRowExitTransition
itemsRowExitTransition = { ExitTransition.None }
// Default value is { it() }
itemDecoration = {
Box(modifier = Modifier.padding(2.dp)) {
it()
}
}
}
}
}

ListBuilder Configuration#

You can configure the items of the inspector bar 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:

InspectorBar.ListBuilder.remember {
add {
InspectorBar.Button.remember {
id = { EditorComponentId("my.package.inspectorBar.button.custom") }
onClick = {}
vectorIcon = null
textString = { "Custom Button" }
}
}
add { InspectorBar.Button.rememberDuplicate() }
add { InspectorBar.Button.rememberDelete() }
add { InspectorBar.Button.rememberAdjustments() }
add { InspectorBar.Button.rememberEffect() }
add { InspectorBar.Button.rememberBlur() }
add { InspectorBar.Button.rememberReplace() }
add { InspectorBar.Button.rememberEditText() }
add { InspectorBar.Button.rememberFormatText() }
add { InspectorBar.Button.rememberFillStroke() }
add { InspectorBar.Button.rememberTextBackground() }
add { InspectorBar.Button.rememberVolume() }
add { InspectorBar.Button.rememberCrop() }
add { InspectorBar.Button.rememberShape() }
add { InspectorBar.Button.rememberLayer() }
add { InspectorBar.Button.rememberSplit() }
add { InspectorBar.Button.rememberMoveAsClip() }
add { InspectorBar.Button.rememberMoveAsOverlay() }
}

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 = InspectorBar.ListBuilder.remember {
add { InspectorBar.Button.rememberLayer() }
add { InspectorBar.Button.rememberCrop() }
add { InspectorBar.Button.rememberFormatText() }
add { InspectorBar.Button.rememberDelete() }
}
existingListBuilder.modify {
addFirst {
InspectorBar.Button.remember {
id = { EditorComponentId("my.package.inspectorBar.button.first") }
vectorIcon = null
textString = { "First Button" }
onClick = {}
}
}
addLast {
InspectorBar.Button.remember {
id = { EditorComponentId("my.package.inspectorBar.button.last") }
vectorIcon = null
textString = { "Last Button" }
onClick = {}
}
}
addAfter(id = InspectorBar.Button.Id.layer) {
InspectorBar.Button.remember {
id = { EditorComponentId("my.package.inspectorBar.button.afterLayer") }
vectorIcon = null
textString = { "After Layer" }
onClick = {}
}
}
addBefore(id = InspectorBar.Button.Id.crop) {
InspectorBar.Button.remember {
id = { EditorComponentId("my.package.inspectorBar.button.beforeCrop") }
vectorIcon = null
textString = { "Before Crop" }
onClick = {}
}
}
replace(id = InspectorBar.Button.Id.formatText) {
InspectorBar.Button.remember {
id = { EditorComponentId("my.package.inspectorBar.button.replacedFormatText") }
vectorIcon = null
textString = { "Replaced Format Text" }
onClick = {}
}
}
remove(id = InspectorBar.Button.Id.delete)
}

Available modification operations:

  • addFirst - prepends a new item at the beginning:
addFirst {
InspectorBar.Button.remember {
id = { EditorComponentId("my.package.inspectorBar.button.first") }
vectorIcon = null
textString = { "First Button" }
onClick = {}
}
}
  • addLast - appends a new item at the end:
addLast {
InspectorBar.Button.remember {
id = { EditorComponentId("my.package.inspectorBar.button.last") }
vectorIcon = null
textString = { "Last Button" }
onClick = {}
}
}
  • addAfter - adds a new item right after a specific item:
addAfter(id = InspectorBar.Button.Id.layer) {
InspectorBar.Button.remember {
id = { EditorComponentId("my.package.inspectorBar.button.afterLayer") }
vectorIcon = null
textString = { "After Layer" }
onClick = {}
}
}
  • addBefore - adds a new item right before a specific item:
addBefore(id = InspectorBar.Button.Id.crop) {
InspectorBar.Button.remember {
id = { EditorComponentId("my.package.inspectorBar.button.beforeCrop") }
vectorIcon = null
textString = { "Before Crop" }
onClick = {}
}
}
  • replace - replaces an existing item with a new item:
replace(id = InspectorBar.Button.Id.formatText) {
InspectorBar.Button.remember {
id = { EditorComponentId("my.package.inspectorBar.button.replacedFormatText") }
vectorIcon = null
textString = { "Replaced Format Text" }
onClick = {}
}
}
  • remove - removes an existing item:
remove(id = InspectorBar.Button.Id.delete)

InspectorBar.Item Configuration#

Each InspectorBar.Item is an EditorComponent. Its id must be unique which is a requirement for proper component management. You have multiple options for creating inspector bar items, from simple predefined buttons to fully custom implementations.

Use Predefined Buttons#

Start with 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 rememberInspectorBarButton() = InspectorBar.Button.remember {
id = { EditorComponentId("my.package.inspectorBar.button.newButton") }
scope = {
val parentScope = this as Scope
rememberLastValue(parentScope) {
if (editorContext.safeSelection == null) lastValue else InspectorBar.ItemScope(parentScope = parentScope)
}
}
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 rememberInspectorBarButtonSimple() = InspectorBar.Button.remember {
id = { EditorComponentId("my.package.inspectorBar.button.newButton") }
scope = {
val parentScope = this as Scope
rememberLastValue(parentScope) {
if (editorContext.safeSelection == null) lastValue else InspectorBar.ItemScope(parentScope = parentScope)
}
}
modifier = { Modifier }
onClick = { editorContext.eventHandler.send(EditorEvent.CloseEditor()) }
visible = { true }
enterTransition = { EnterTransition.None }
exitTransition = { ExitTransition.None }
// Default value is { it() }
decoration = {
Surface(color = MaterialTheme.colorScheme.background) {
it()
}
}
// 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 rememberInspectorBarCustomItem() = EditorComponent.remember {
id = { EditorComponentId("my.package.inspectorBar.newCustomItem") }
scope = {
val parentScope = this as Scope
rememberLastValue(parentScope) {
if (editorContext.safeSelection == null) lastValue else InspectorBar.ItemScope(parentScope = parentScope)
}
}
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 InspectorBar.Buttons#

As you often saw in the previous sections, there are composable functions that look like this: InspectorBar.Button.remember{name}. All these functions return an InspectorBar.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.

ButtonIDDescriptionRenders For
InspectorBar.Button.rememberReplaceInspectorBar.Button.Id.replaceOpens library sheet via EditorEvent.Sheet.Open. By default, DesignBlockType, FillType and kind of the selected design block are used to find the library in the Asset Library. Selected asset will replace the content of the currently selected design block.Video, Image, Sticker, Audio
InspectorBar.Button.rememberEditTextInspectorBar.Button.Id.editTextEnters text editing mode for the selected design block.Text
InspectorBar.Button.rememberFormatTextInspectorBar.Button.Id.formatTextOpens format text sheet via EditorEvent.Sheet.Open.Text
InspectorBar.Button.rememberFillStrokeInspectorBar.Button.Id.fillStrokeOpens fill & stroke sheet via EditorEvent.Sheet.Open.Page, Video, Image, Shape, Text
InspectorBar.Button.rememberTextBackgroundInspectorBar.Button.Id.textBackgroundOpens text background sheet via EditorEvent.Sheet.Open.Text
InspectorBar.Button.rememberEditVoiceoverInspectorBar.Button.Id.editVoiceoverOpens voiceover sheet via EditorEvent.Sheet.Open.Video, Audio, Voiceover
InspectorBar.Button.rememberVolumeInspectorBar.Button.Id.volumeOpens volume sheet via EditorEvent.Sheet.Open.Video, Audio, Voiceover
InspectorBar.Button.rememberCropInspectorBar.Button.Id.cropOpens crop sheet via EditorEvent.Sheet.Open.Video, Image
InspectorBar.Button.rememberAdjustmentsInspectorBar.Button.Id.adjustmentsOpens adjustments sheet via EditorEvent.Sheet.Open.Video, Image
InspectorBar.Button.rememberFilterInspectorBar.Button.Id.filterOpens filter sheet via EditorEvent.Sheet.Open.Video, Image
InspectorBar.Button.rememberEffectInspectorBar.Button.Id.effectOpens effect sheet via EditorEvent.Sheet.Open.Video, Image
InspectorBar.Button.rememberBlurInspectorBar.Button.Id.blurOpens blur sheet via EditorEvent.Sheet.Open.Video, Image
InspectorBar.Button.rememberShapeInspectorBar.Button.Id.shapeOpens shape sheet via EditorEvent.Sheet.Open.Video, Image, Shape
InspectorBar.Button.rememberSelectGroupInspectorBar.Button.Id.selectGroupSelects the group design block that contains the currently selected design block via EditorEvent.selectGroupForSelection.Video, Image, Sticker, Shape, Text
InspectorBar.Button.rememberEnterGroupInspectorBar.Button.Id.enterGroupChanges selection from the selected group design block to a design block within that group via EditorEvent.enterGroupForSelection.Group
InspectorBar.Button.rememberLayerInspectorBar.Button.Id.layerOpens layer sheet via EditorEvent.Sheet.Open.Video, Image, Sticker, Shape, Text
InspectorBar.Button.rememberSplitInspectorBar.Button.Id.splitSplits currently selected design block via EditorEvent.splitSelection in a video scene.Video, Image, Sticker, Shape, Text, Audio
InspectorBar.Button.rememberMoveAsClipInspectorBar.Button.Id.moveAsClipMoves currently selected design block into the background track as clip via EditorEvent.moveSelectionAsClip.Video, Image, Sticker, Shape, Text
InspectorBar.Button.rememberMoveAsOverlayInspectorBar.Button.Id.moveAsOverlayMoves currently selected design block from the background track to an overlay via EditorEvent.moveSelectionAsOverlay.Video, Image, Sticker, Shape, Text
InspectorBar.Button.rememberReorderInspectorBar.Button.Id.reorderOpens reorder sheet via EditorEvent.Sheet.Open.Video, Image, Sticker, Shape, Text
InspectorBar.Button.rememberDuplicateInspectorBar.Button.Id.duplicateDuplicates currently selected design block via EditorEvent.duplicateSelection.Video, Image, Sticker, Shape, Text, Audio
InspectorBar.Button.rememberDeleteInspectorBar.Button.Id.deleteDeletes currently selected design block via EditorEvent.deleteSelection.Video, Image, Sticker, Shape, Text, Audio, Voiceover