This HTML page is not optimized for LLM or AI agent consumption. Fetch the Markdown version instead: /guides/android/jetpack-compose/components/toolbar-hosting.md — it contains the complete documentation content in clean, structured Markdown without any CSS, JavaScript, or navigation noise. Hosting the Nutrient toolbars in Jetpack Compose | Nutrient Android SDK

Every Nutrient toolbar, including the SDK’s own and any that a host composes, renders from one shared object: the ToolbarCoordinator. The SDK writes the items, document title, UI visibility, shown contextual toolbar, and search session into the coordinator. It also reads the actions that the toolbars emit. This page explains the coordinator and how to get it in each kind of host. It also covers the composables that place the toolbars with the SDK’s docking behavior, so a host doesn’t have to rebuild that behavior.

The coordinator

ToolbarCoordinator is state held in a ViewModel. The ViewModel is scoped to the host that requested the coordinator, which is either an activity or a fragment. Because of that scoping, the coordinator survives configuration changes and outlives the view of PdfUiFragment. It has these main surfaces:

SurfaceWhat it holds
frame: StateFlow<ToolbarFrame>The main toolbar’s isVisible, title, height, insets, and navigationIcon.
items: StateFlow<ToolbarItems>The main toolbar’s visible and overflow items, after the host’s ToolbarItemsProvider ran.
contextualToolbar: StateFlow<ContextualToolbarState?>The shown contextual bar, or null: its kind, items, position, isDraggable, and allowedPositions.
searchSession: StateFlow<ToolbarSearchSession?>Inline search: isActive, state (query, results), and actions.
addActionListener, addActionInterceptor, dispatchThe action bus every toolbar dispatches into and the SDK handles from.
setMainToolbarHostVisible, setMainToolbarInsets, setContextualToolbarPosition, …Host controls over what the SDK renders.

Every add… method returns a ToolbarSubscription. Close the subscription when the host goes away.

Getting it

HostHow
PdfActivity or any ComponentActivitynutrientToolbarCoordinator() from com.pspdfkit.jetpack.compose.toolbar
PdfUiFragment or any FragmentnutrientToolbarCoordinator() on the fragment, which returns the fragment’s own coordinator
Compose DocumentViewdocumentState.toolbarCoordinator.collectAsState(), null until the document UI is ready
A Compose host without an SDK UIrememberNutrientToolbarCoordinator(), a coordinator scoped to the composition

Choosing a hosting shape

You haveUse
PdfActivity with the default layoutNothing to host. Customize items with the main toolbar and contextual toolbars guides.
PdfActivity with a custom layoutA ComposeView with the ID pspdf__toolbar_compose_host overlaying the content; the SDK mounts into it. See extending PdfActivity.
PdfActivity, but your own toolbar lookdefaultToolbarEnabled(false) and/or defaultContextualToolbarEnabled(false). Then compose NutrientMainToolbar or a floating pill over the activity’s coordinator. CustomToolbarsPdfActivityExample.
A bare PdfFragment in a Compose activityNutrientToolbars around the fragment container, plus the binders. CustomToolbarsPdfFragmentExample. See usage within fragment.
A bare PdfFragment in a View layoutnutrientToolbarsComposeView(context, coordinator) added over the fragment container. CustomToolbarsPdfFragmentViewExample.
Compose DocumentViewdefaultToolbarEnabled(false) and compose the bars from documentState.toolbarCoordinator. CustomToolbarsDocumentViewExample.

NutrientToolbars

NutrientToolbars is the SDK’s toolbar stack as a composable. It places the main toolbar in the top slot and the contextual toolbar in its top, start, or end dock. It also draws the drop-zone chrome while the user drags a bar. When a vertical bar doesn’t fit, fit correction moves the bar from a side dock to the top. NutrientToolbars also saves the dock the user chose, under the same preference keys that PdfActivity uses. Wrap the content in it:

NutrientToolbars(
coordinator = coordinator,
showMainToolbar = true,
topWindowInset = topInset, // the insets your layout has not consumed.
bottomWindowInset = bottomInset,
modifier = Modifier.fillMaxSize(),
) {
// Content reads the inset so it clears a top-docked bar.
MyDocument(Modifier.fillMaxSize().padding(top = LocalToolbarContentInset.current))
}

LocalToolbarContentInset is the height that the content should leave free at the top. The value is zero while no bar is docked there, so the document doesn’t move when a side-docked bar appears.

@Composable
fun NutrientToolbars(
coordinator: ToolbarCoordinator,
modifier: Modifier = Modifier,
showMainToolbar: Boolean = true,
showContextualToolbar: Boolean = true,
defaultGroupingProvider: ContextualToolbarGroupingProvider? = null,
dragListener: NutrientContextualToolbarDragListener? = null,
onEffectivePositionChanged: ((ToolbarPosition?) -> Unit)? = null,
topWindowInset: Dp = 0.dp,
bottomWindowInset: Dp = 0.dp,
leftWindowInset: Dp = 0.dp,
rightWindowInset: Dp = 0.dp,
content: @Composable () -> Unit,
)

With showMainToolbar = false, the composable keeps only the contextual bars. This is the common setup for a host with its own app bar. onEffectivePositionChanged reports the dock where the contextual bar is actually laid out, after fit correction.

NutrientToolbarScaffold

NutrientToolbarScaffold is the layer under NutrientToolbars. It provides the same placement, docking, and drag chrome, with slots for the content of each toolbar position. Use it to put custom composables into the SDK’s dock slots:

NutrientToolbarScaffold(
coordinator = coordinator,
mainToolbar = { NutrientMainToolbar(coordinator, Modifier.fillMaxWidth(), shape = RoundedCornerShape(bottomStart = 16.dp, bottomEnd = 16.dp)) },
contextualToolbar = {
// ContextualToolbarSlotScope: the dock the scaffold resolved, and the drag listener that moves the bar between docks.
NutrientContextualToolbar(coordinator, effectivePosition = effectivePosition, dragListener = dragListener)
},
) {
// Required: the content the toolbars are placed over, usually the document.
DocumentView(documentState = documentState, modifier = Modifier.fillMaxSize())
}

The trailing content lambda is required and holds the content that the toolbars are placed over. The other parameters configure the docks. contextualToolbarSize, verticalMargin, horizontalMargin, dragTargetColor, and an outer dragListener default to the themed values in NutrientContextualToolbarDefaults. The four window insets default to 0.dp.

nutrientToolbarsComposeView

For a View layout, nutrientToolbarsComposeView(context, coordinator, showMainToolbar, defaultGroupingProvider) returns a ComposeView containing NutrientToolbars. Add the view over the document with match_parent in both dimensions. While no toolbar is shown, the view paints nothing and passes touches through. Its docking logic follows the view’s window-attach lifecycle. The pspdf__toolbar_compose_host slot in the SDK’s own activity layout plays the same role.

Insets

Three layers decide what the main toolbar keeps clear of:

  1. The coordinator’s frame. ToolbarFrame.insets is a ToolbarInsets. StatusBars is the default, and it’s correct for an edge-to-edge mount at the top of the window. None means the host already spaces the bar, and Custom(top, bottom, start, end) takes the four insets. Set the insets with coordinator.setMainToolbarInsets(...). This setting controls the SDK-mounted toolbar in PdfActivity and PdfUiFragment. It’s useful when an embedded fragment sits below other chrome.
  2. NutrientMainToolbarDefaults.windowInsets(coordinator), the composable’s default windowInsets parameter. It uses the frame’s insets, plus the side system bars and the display cutout in landscape. This keeps the trailing overflow item from rendering under the navigation buttons.
  3. The windowInsets parameter on NutrientMainToolbar replaces both when a host passes it.

NutrientToolbars and NutrientToolbarScaffold take the insets as Dp values because they position several bars. Pass the insets that the host layout hasn’t consumed.

Main toolbar visibility

The SDK toggles coordinator.frame.isVisible whenever the user interface shows or hides. To hide the main toolbar and keep it hidden through those toggles, use the host veto:

coordinator.setMainToolbarHostVisible(false) // the SDK's own show/hide cannot undo this.
coordinator.setMainToolbarHostVisible(true) // back to whatever the SDK last requested.
  • Main toolbar, contextual toolbar, floating toolbars, and tab bar — The composables you place with these hosts.
  • Usage within fragment — The binders that connect a bare PdfFragment’s modes to the coordinator.
  • Catalog app — CustomToolbarsPdfActivityExample, CustomToolbarsPdfFragmentExample, CustomToolbarsPdfFragmentViewExample, CustomToolbarsDocumentViewExample, and DocumentPagerExample.