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:
| Surface | What 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, dispatch | The 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
| Host | How |
|---|---|
PdfActivity or any ComponentActivity | nutrientToolbarCoordinator() from com.pspdfkit.jetpack.compose.toolbar |
PdfUiFragment or any Fragment | nutrientToolbarCoordinator() on the fragment, which returns the fragment’s own coordinator |
Compose DocumentView | documentState.toolbarCoordinator.collectAsState(), null until the document UI is ready |
| A Compose host without an SDK UI | rememberNutrientToolbarCoordinator(), a coordinator scoped to the composition |
Choosing a hosting shape
| You have | Use |
|---|---|
PdfActivity with the default layout | Nothing to host. Customize items with the main toolbar and contextual toolbars guides. |
PdfActivity with a custom layout | A ComposeView with the ID pspdf__toolbar_compose_host overlaying the content; the SDK mounts into it. See extending PdfActivity. |
PdfActivity, but your own toolbar look | defaultToolbarEnabled(false) and/or defaultContextualToolbarEnabled(false). Then compose NutrientMainToolbar or a floating pill over the activity’s coordinator. CustomToolbarsPdfActivityExample. |
A bare PdfFragment in a Compose activity | NutrientToolbars around the fragment container, plus the binders. CustomToolbarsPdfFragmentExample. See usage within fragment. |
A bare PdfFragment in a View layout | nutrientToolbarsComposeView(context, coordinator) added over the fragment container. CustomToolbarsPdfFragmentViewExample. |
Compose DocumentView | defaultToolbarEnabled(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.
@Composablefun 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:
- The coordinator’s frame.
ToolbarFrame.insetsis aToolbarInsets.StatusBarsis the default, and it’s correct for an edge-to-edge mount at the top of the window.Nonemeans the host already spaces the bar, andCustom(top, bottom, start, end)takes the four insets. Set the insets withcoordinator.setMainToolbarInsets(...). This setting controls the SDK-mounted toolbar inPdfActivityandPdfUiFragment. It’s useful when an embedded fragment sits below other chrome. NutrientMainToolbarDefaults.windowInsets(coordinator), the composable’s defaultwindowInsetsparameter. 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.- The
windowInsetsparameter onNutrientMainToolbarreplaces 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.Related
- 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, andDocumentPagerExample.