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

NutrientMainToolbar is the main toolbar of the Nutrient viewer as a composable. It’s the same toolbar PdfActivity renders. A Compose host can place it anywhere, give it a shape, replace its parts, or drop it into a Scaffold. It has the shape of Material 3’s TopAppBar(opens in a new tab): navigationIcon, title, and actions slots; a searchBar slot that takes over while inline search is open; and flat parameters for shape, elevation, insets, and colors.

Its content comes from a ToolbarCoordinator: the items, their state, the title, and whether the bar is visible. DocumentView creates the coordinator and publishes it through DocumentState.toolbarCoordinator, so the SDK keeps feeding the bar while you own its layout.

Basic usage

Disable the SDK’s own mount, and then compose the bar over the document once the coordinator is available:

val configuration = PdfActivityConfiguration.Builder(context)
.defaultToolbarEnabled(false)
.build()
val documentState = rememberDocumentState(uri, configuration)
val coordinator by documentState.toolbarCoordinator.collectAsState()
Box(Modifier.fillMaxSize()) {
DocumentView(documentState = documentState, modifier = Modifier.fillMaxSize())
coordinator?.let { NutrientMainToolbar(coordinator = it, modifier = Modifier.fillMaxWidth()) }
}

toolbarCoordinator is null until the document UI is ready, which is why the bar is composed inside let. The SDK’s items, the document title, the overflow menu, inline search, and the show and hide animation all work without further wiring. The Catalog app’s ComposePdfViewerExample is a complete viewer built this way.

Customizing the items

The items aren’t a parameter of the composable. They come from DocumentState, through the same three methods PdfActivity has, and they’re safe to call before the DocumentView attaches:

LaunchedEffect(documentState) {
documentState.setToolbarItemsProvider { defaultItems ->
defaultItems + ToolbarItem.Button(
actionId = "my.app.bookmark",
id = R.id.bookmark,
contentDescription = "Bookmark",
icon = ToolbarIcon.Resource(R.drawable.ic_bookmark),
)
}
documentState.setOnToolbarItemClickListener { id ->
if (id == R.id.bookmark) { addBookmark(); true } else false
}
}
// When the state feeding the provider changes:
documentState.invalidateToolbarItems()

The main toolbar guide covers the item model, ordering, hiding defaults, and intercepting the SDK’s own items. It applies unchanged here.

Slots

The default slots render the SDK’s pieces, and each is public, so replacing one slot never means losing the others:

SlotDefaultNotes
navigationIconNutrientMainToolbarNavigationIcon(coordinator)Renders the icon set with documentState.setToolbarNavigationIcon; empty when none is set.
titleNutrientMainToolbarTitle(coordinator)The document title from the coordinator’s frame.
actionsNutrientToolbarItems(coordinator, Modifier.weight(1f))The item row with its overflow menu, pinned to the trailing edge.
searchBarNutrientToolbarSearchBar(coordinator, Modifier.fillMaxWidth())Replaces the three slots above while inline search is open.

title, actions, and searchBar run in a NutrientToolbarScope, whose Modifier.weight() and Modifier.align() work like RowScope’s:

NutrientMainToolbar(
coordinator = coordinator,
modifier = Modifier.fillMaxWidth(),
navigationIcon = {
IconButton(onClick = onBack) {
Icon(
painterResource(R.drawable.ic_arrow_back),
contentDescription = "Back",
tint = UiTheme.colors.mainToolbar.iconColor, // the same scheme the SDK's items use.
)
}
},
title = { Text(documentTitle, Modifier.weight(1f), style = NutrientMainToolbarDefaults.titleTextStyle) },
)

For a bar that isn’t “navigation, title, actions” at all, the content overload lays out whatever you give it. The search bar still takes over while search is open:

NutrientMainToolbar(coordinator = coordinator, modifier = Modifier.align(Alignment.TopEnd)) {
NutrientToolbarItems(coordinator, Modifier.weight(1f))
NutrientMainToolbarTitle(coordinator)
}

Width and placement

The toolbar is as wide as its content, the way a Row is, and a width modifier wins:

  • Modifier.fillMaxWidth() is the full-bleed bar the SDK’s own mount uses.
  • Modifier.width(240.dp) is a fixed width.
  • No width modifier is a floating pill exactly as wide as its icons.

A slot that fills the width grows a wrapping toolbar to the width it was offered. That’s how the default search bar widens a pill while search is open and collapses it afterward. The toolbar animates that change itself, so don’t add Modifier.animateContentSize(), which clips the shadow.

modifier is applied to the outermost node, the show and hide animation, so BoxScope.align, padding, windowInsetsPadding, and onSizeChanged work from the call site. The subtree is removed once the toolbar animates away, so a modifier that must reserve space while hidden belongs on a container around it.

Chrome and colors

shape, shadowElevation, and windowInsets are drawn inside the animation as a Material Surface, so a pill slides and fades away with the bar. A tap that lands on the toolbar but misses every button stops there instead of reaching the document:

NutrientMainToolbar(
coordinator = coordinator,
modifier = Modifier.align(Alignment.TopCenter).padding(NutrientMainToolbarDefaults.FloatingMargin),
shape = NutrientMainToolbarDefaults.FloatingShape, // 28dp rounded corners.
shadowElevation = NutrientMainToolbarDefaults.FloatingShadowElevation,
windowInsets = NutrientMainToolbarDefaults.NoWindowInsets, // the host already handles the status bar.
colors = NutrientMainToolbarDefaults.colors().copy(
backgroundColor = MaterialTheme.colorScheme.surfaceContainer,
textColor = MaterialTheme.colorScheme.onSurface,
),
itemIndication = ToolbarItemIndication(shape = CircleShape),
)

colors is a MainToolbarColors: backgroundColor, textColor, titleTextColor, popup (the overflow menu’s background), and iconColor, iconSelectedColor, and iconDisabledColor, which default from textColor. It’s provided to everything inside the bar, so your slot content reads the same scheme through UiTheme.colors.mainToolbar. The default, NutrientMainToolbarDefaults.colors(), comes from the surrounding NutrientTheme, which reads the activity’s XML theme attributes. To theme the toolbar together with other Nutrient composables, wrap the bar in NutrientTheme(colorScheme = …).

itemIndication is the shape and ripple color every item shows its pressed, hovered, and selected states with — the SDK’s items and yours alike. The default is the 12dp round rect the toolbar draws its selection with.

Inline search opens inside the toolbar, in the searchBar slot, and survives rotation. Observe it through the coordinator:

val session by coordinator.searchSession.collectAsState()
val searchActive = session?.isActive?.collectAsState()?.value == true
val searchState = session?.state?.collectAsState()?.value // query, resultCount, selectedIndex.

session.actions exposes setQuery, selectNext, selectPrevious, selectIndex, and close for a host that drives search from its own UI.

Visibility and immersive mode

The bar hides and shows itself with the viewer’s user interface: coordinator.frame is a StateFlow<ToolbarFrame> whose isVisible flips in immersive mode and when the user taps the page. Other chrome you draw can follow it:

val frame by coordinator.frame.collectAsState()
AnimatedVisibility(frame.isVisible) { MyBottomBar() }

enter and exit are the transitions the toolbar uses for that; the defaults slide it in and out at the top.

Insets

By default, the toolbar reserves the status bar strip and, in landscape, the side system bars and display cutout. It does so because the SDK mounts it edge to edge at the top of the window. That default is NutrientMainToolbarDefaults.windowInsets(coordinator). A host whose container already handles the status bar passes NutrientMainToolbarDefaults.NoWindowInsets. A host that keeps the SDK’s mount but places it below other chrome calls coordinator.setMainToolbarInsets(ToolbarInsets.None) instead. See toolbar hosting for the inset model.

API

@Composable
fun NutrientMainToolbar(
coordinator: ToolbarCoordinator,
modifier: Modifier = Modifier,
navigationIcon: @Composable () -> Unit = { NutrientMainToolbarNavigationIcon(coordinator) },
title: @Composable NutrientToolbarScope.() -> Unit = { NutrientMainToolbarTitle(coordinator) },
actions: @Composable NutrientToolbarScope.() -> Unit = { NutrientToolbarItems(coordinator, Modifier.weight(1f)) },
searchBar: @Composable NutrientToolbarScope.() -> Unit = { NutrientToolbarSearchBar(coordinator, Modifier.fillMaxWidth()) },
shape: Shape = NutrientMainToolbarDefaults.Shape,
shadowElevation: Dp = NutrientMainToolbarDefaults.shadowElevation,
windowInsets: WindowInsets = NutrientMainToolbarDefaults.windowInsets(coordinator),
colors: MainToolbarColors = NutrientMainToolbarDefaults.colors(),
itemIndication: ToolbarItemIndication = NutrientMainToolbarDefaults.ItemIndication,
enter: EnterTransition = NutrientMainToolbarDefaults.enterTransition,
exit: ExitTransition = NutrientMainToolbarDefaults.exitTransition,
)
ParameterDefaultWhat it does
coordinatorThe ToolbarCoordinator the bar renders; from documentState.toolbarCoordinator.
modifierModifierApplied to the outermost node; width and placement.
navigationIconNutrientMainToolbarNavigationIcon(coordinator)Leading slot.
titleNutrientMainToolbarTitle(coordinator)Title slot, in NutrientToolbarScope.
actionsNutrientToolbarItems(coordinator, weight(1f))Item row slot, in NutrientToolbarScope.
searchBarNutrientToolbarSearchBar(coordinator)Replaces the slots while inline search is open.
shapeRectangleShapeShape of the surface; FloatingShape is a 28dp pill.
shadowElevation0.dpElevation of the surface; FloatingShadowElevation is 8dp.
windowInsetswindowInsets(coordinator)Insets reserved inside the surface. NoWindowInsets when the host handles them.
colorscolors()MainToolbarColors provided to everything inside the bar.
itemIndicationItemIndicationShape and ripple color of item states; also read by NutrientToolbarItemButton in your slots.
enter, exitslide in and out at the topTransitions the bar uses when frame.isVisible changes.

NutrientMainToolbarDefaults also exposes FloatingMargin (12dp horizontal, 8dp vertical padding for a pill) and titleTextStyle.

  • Main toolbar — The item model and the PdfActivity and PdfUiFragment hosts.
  • Toolbar hosting — The coordinator, NutrientToolbars, insets, and the status bar scrim.
  • Floating toolbars — NutrientFloatingToolbar, the Material 3 Expressive pill.
  • Contextual toolbar — The annotation, document editing, and content editing bars.
  • Catalog app — MainToolbarCustomizationExample, ComposePdfViewerExample, CustomToolbarsDocumentViewExample.