This HTML page is not optimized for LLM or AI agent consumption. Fetch the Markdown version instead: /guides/android/customizing-the-interface/using-toolbars-within-fragment.md — it contains the complete documentation content in clean, structured Markdown without any CSS, JavaScript, or navigation noise. Using contextual toolbars within PdfFragment | Nutrient

PdfActivity provides toolbars out of the box for the special modes: annotation creation, document editing, and content editing. When you build your own activity around a bare PdfFragment, you can mount the same toolbars yourself. They’re Jetpack Compose components driven by a ToolbarCoordinator. The SDK ships binders that connect them to the fragment’s special mode controllers. A custom host therefore gets the same bars, docking, and drag behavior as PdfActivity without rebuilding them.

If you host a PdfUiFragment instead, none of this is needed. It mounts the toolbars itself, and you customize them as described in customizing contextual toolbars.

Special modes

When a document is in a special mode, a specific interaction is taking place and the UI behaves differently. In annotation creation mode with the ink tool selected, for example, sliding a finger across the page draws instead of scrolling.

Each special mode has a controller. PdfActivity wires those controllers to their toolbars; with a bare fragment, you either do the same or build your own UI on the controller:

Getting the coordinator

The coordinator is activity-scoped and survives configuration changes:

import com.pspdfkit.jetpack.compose.toolbar.nutrientToolbarCoordinator
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val coordinator = nutrientToolbarCoordinator()
}
}

The same extension exists on Fragment. Everything in customizing contextual toolbars works on this coordinator too: observing the shown bar, transforming items, listening to actions, grouping, and docking.

Mounting the toolbar stack

The toolbar stack is the SDK’s main toolbar slot plus the contextual toolbar. The contextual toolbar has top, start, and end dock slots; a drag handle; and drop-zone chrome. You can mount the stack in two ways, depending on whether your activity’s content is Compose or Views. Both overlay the document and let touches through while no toolbar is shown.

In a Compose layout

NutrientToolbars places the toolbars around its content. The content reads LocalToolbarContentInset to stay clear of a top-docked bar:

setContent {
NutrientToolbars(
coordinator = coordinator,
showMainToolbar = true, // `false` keeps only the contextual bars, for hosts with their own app bar.
modifier = Modifier.fillMaxSize(),
) {
AndroidView(
factory = { context -> FragmentContainerView(context).apply { id = R.id.fragmentContainer } },
modifier = Modifier.fillMaxSize().padding(top = LocalToolbarContentInset.current),
)
LaunchedEffect(Unit) {
if (supportFragmentManager.findFragmentById(R.id.fragmentContainer) == null) {
supportFragmentManager.commit { add(R.id.fragmentContainer, fragment) }
}
}
}
}

Pass the window insets your layout hasn’t consumed as topWindowInset, bottomWindowInset, leftWindowInset, and rightWindowInset, so the docks and drop zones sit inside them.

In a View layout

nutrientToolbarsComposeView returns a ComposeView with the same stack. Add it to your layout over the fragment container, match_parent, both ways:

val root = FrameLayout(this)
root.addView(FragmentContainerView(this).apply { id = R.id.fragmentContainer }, matchParent)
root.addView(nutrientToolbarsComposeView(this, coordinator, showMainToolbar = true), matchParent)
setContentView(root)

Docking wiring follows the view’s window-attach lifecycle, so you release nothing on teardown. Dock changes are persisted under the same preference keys PdfActivity uses, app-wide.

Binding the special modes

The binders publish a bar into the coordinator when a mode is entered and remove it when the mode is left. Create them once with the coordinator. Then attach and detach from the fragment’s mode listeners:

class MyActivity : AppCompatActivity(), OnAnnotatingModeChangeListener {
private lateinit var annotationToolbar: AnnotationToolbarBinder
private lateinit var fragmentBinder: PdfFragmentToolbarBinder
private var creationToolbarShown = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val coordinator = nutrientToolbarCoordinator()
annotationToolbar = AnnotationToolbarBinder(coordinator)
fragmentBinder = PdfFragmentToolbarBinder(coordinator)
fragment.addOnAnnotatingModeChangeListener(this)
supportFragmentManager.registerFragmentLifecycleCallbacks(object : FragmentManager.FragmentLifecycleCallbacks() {
override fun onFragmentViewCreated(fm: FragmentManager, f: Fragment, v: View, savedInstanceState: Bundle?) {
if (f === fragment) fragmentBinder.attach(fragment)
}
override fun onFragmentViewDestroyed(fm: FragmentManager, f: Fragment) {
if (f === fragment) fragmentBinder.detach()
}
}, false)
}
override fun onEnterAnnotatingMode(controller: AnnotatingController) {
// A tool is active (not just a selected annotation): show the creation toolbar.
if (controller.activeAnnotationTool != null && !creationToolbarShown) {
annotationToolbar.attach(controller)
creationToolbarShown = true
}
}
override fun onChangeAnnotatingMode(controller: AnnotatingController) = Unit
override fun onExitAnnotatingMode(controller: AnnotatingController) {
if (controller.activeAnnotationTool == null && creationToolbarShown) {
annotationToolbar.detach()
creationToolbarShown = false
}
}
override fun onDestroy() {
fragment.removeOnAnnotatingModeChangeListener(this)
if (creationToolbarShown) annotationToolbar.detach()
super.onDestroy()
}
}
  • AnnotationToolbarBinder builds the annotation creation toolbar from the AnnotatingController and keeps its tools’ selection in sync. Its position and isDraggable properties apply on every attach().
  • PdfFragmentToolbarBinder keeps the coordinator’s undo, redo, and annotation tool items in sync with the fragment. Attach it once the fragment’s view exists and detach it when the view goes away. Use its ACTION_UNDO (pspdf.undo) and ACTION_REDO (pspdf.redo) constants as the action IDs of your own undo and redo items. The binder then keeps their enabled state in sync. The binder doesn’t handle their taps: Call fragment.undoManager.undo() and redo() from your click handler, as CustomToolbarsPdfFragmentExample in the Catalog app does.

Providing main toolbar items

With showMainToolbar = true, the stack renders the SDK’s main toolbar, but a bare-fragment host has no PdfActivity to fill it. Set its title and items on the coordinator, and handle taps with addItemClickHandler:

coordinator.setMainToolbarTitle("Hosted toolbars")
coordinator.setMainToolbarItems(
ToolbarItems(
visible = persistentListOf(
ToolbarItem.Button(
actionId = "my.app.annotate",
id = R.id.annotate,
contentDescription = "Toggle annotation mode",
icon = ToolbarIcon.Resource(com.pspdfkit.R.drawable.pspdf__ic_edit_annotations),
overflowBehavior = ToolbarItem.OverflowBehavior.AlwaysShow,
),
ToolbarItem.Button(
actionId = PdfFragmentToolbarBinder.ACTION_UNDO,
id = R.id.undo,
contentDescription = "Undo",
icon = ToolbarIcon.Resource(com.pspdfkit.R.drawable.pspdf__ic_undo),
),
),
),
)
val clicks = coordinator.addItemClickHandler { id ->
if (id == R.id.annotate) {
if (creationToolbarShown) fragment.exitCurrentlyActiveMode() else fragment.enterAnnotatingMode()
}
}
// clicks.close() in onDestroy.

Docking

The binders restore the last-used dock, but only the host places the bar. NutrientToolbars and nutrientToolbarsComposeView provide the same top, start, and end dock slots and drop-zone chrome as PdfActivity, so nothing else is needed. A host that composes NutrientContextualToolbar in its own container instead must either implement docking or pin the position with annotationToolbar.position = ToolbarPosition.Top. Otherwise, a restored side dock renders a vertical bar at the container’s origin with no way to move it. The drag handle only shows when the bar is given a NutrientContextualToolbarDragListener.

Integrating annotation inspectors

To show the default annotation inspectors next to the toolbars, follow the using property inspectors within fragment guide. CustomToolbarsPdfFragmentExample in the Catalog app binds DefaultAnnotationCreationInspectorController and DefaultAnnotationEditingInspectorController from the same mode listener as the toolbar binder.

Working examples

These examples are in the Catalog app, under Toolbars & Menus:

  • CustomToolbarsPdfFragmentExample — NutrientToolbars around a bare PdfFragment, with the annotation toolbar and the annotation inspector.
  • CustomToolbarsPdfFragmentViewExample — The same activity with the toolbar stack mounted as a ComposeView in a classic View layout.