---
title: "Compose PDF Viewer"
canonical_url: "https://www.nutrient.io/guides/android/samples/compose-pdf-viewer-kotlin/"
md_url: "https://www.nutrient.io/guides/android/samples/compose-pdf-viewer-kotlin.md"
last_updated: "2026-11-02T14:08:15.330Z"
description: "A complete Compose viewer app: recent documents with page thumbnails, system file import, light/dark/dynamic-color settings, and a host-rendered main toolbar."
---

# Compose PDF Viewer

A complete Compose viewer app: recent documents with page thumbnails, system file import, light/dark/dynamic-color settings, and a host-rendered main toolbar.

[Get Started](https://www.nutrient.io/sdk/android/getting-started.md)

[All Samples](https://www.nutrient.io/guides/android/samples.md)

[Download](https://www.nutrient.io/guides/android/downloads.md)

[Launch Demo](https://www.nutrient.io/demo/)

---

```kotlin

/*
 *   Copyright © 2026 PSPDFKit GmbH. All rights reserved.
 *
 *   The PSPDFKit Sample applications are licensed with a modified BSD license.
 *   Please see License for details. This notice may not be removed from this file.
 */

package com.pspdfkit.catalog.examples.kotlin.composeviewer

import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.pspdfkit.catalog.R
import com.pspdfkit.catalog.SdkExample
import com.pspdfkit.catalog.tasks.ExtractAssetTask
import com.pspdfkit.catalog.ui.theming.CatalogTheme
import com.pspdfkit.configuration.activity.PdfActivityConfiguration
import com.pspdfkit.utils.getSupportParcelableExtra
import java.io.File

/**
 * A complete Jetpack Compose PDF viewer app in one catalog example — the in-catalog successor
 * of the standalone `simple-compose` app:
 *
 * - **Library**: recent documents as a grid of live page-1 thumbnails (rendered with
 *   `PdfDocumentLoader`), with system file import (SAF).
 * - **Viewer** ([ViewerScreen]): `DocumentView` with a host-rendered `NutrientMainToolbar`,
 *   the content/viewport inset bookkeeping a custom main toolbar requires, custom toolbar
 *   items, `DefaultListeners` wiring, and a live demonstration of
 *   [com.pspdfkit.jetpack.compose.interactors.DocumentState.setCustomPdfSource]'s async
 *   completion and view-state restore.
 * - **Settings**: light/dark/system theme and Material You dynamic color, persisted in a
 *   DataStore; the choice drives both the app chrome and the document theme.
 *
 * State lives in [ComposeViewerViewModel] — plain `ViewModel` + DataStore, no DI framework or
 * database, so the whole example is copy-paste-able.
 */
class ComposePdfViewerExample(context: Context) :
    SdkExample(context, R.string.composePdfViewerExampleTitle, R.string.composePdfViewerExampleDescription) {
    override fun launchExample(context: Context, configuration: PdfActivityConfiguration.Builder) {
        // Extract the bundled demo document so a fresh install starts with a library entry.
        ExtractAssetTask.extract(WELCOME_DOC, title, context) { documentFile ->
            val intent = Intent(context, ComposePdfViewerActivity::class.java).putExtra(ComposePdfViewerActivity.EXTRA_DEMO_DOCUMENT, Uri.fromFile(documentFile))
            context.startActivity(intent)
        }
    }
}

class ComposePdfViewerActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        enableEdgeToEdge()
        super.onCreate(savedInstanceState)
        val demoDocument = intent.getSupportParcelableExtra(EXTRA_DEMO_DOCUMENT, Uri::class.java)

        setContent {
            val viewModel: ComposeViewerViewModel = viewModel()
            // Seed the library with the bundled demo document on every launch — recordOpened
            // upserts, so an existing entry only refreshes its timestamp. Label it with the asset
            // name: ExtractAssetTask writes the file as "<asset>_<sha1-of-title>", which is not a
            // name to show a reader.
            LaunchedEffect(viewModel) {
                demoDocument?.path?.let { viewModel.recordOpened(File(it), name = SdkExample.WELCOME_DOC) }
            }

            val darkMode by viewModel.darkMode.collectAsState()
            val dynamicColor by viewModel.dynamicColor.collectAsState()
            // The catalog's own theme (brand palette + typography), driven by this example's
            // persisted settings rather than CatalogTheme's system defaults — so the Settings
            // screen's theme and dynamic-color controls still work while the viewer looks like
            // the rest of the catalog.
            CatalogTheme(darkTheme = viewerDarkMode(darkMode), dynamicColor = dynamicColor) {
                ViewerNavHost(viewModel)
            }
        }
    }

    companion object {
        const val EXTRA_DEMO_DOCUMENT = "ComposePdfViewerActivity.DemoDocument"
    }
}

/** The app's three destinations; the viewer takes the document's file path as its argument. */
@Composable
private fun ViewerNavHost(viewModel: ComposeViewerViewModel) {
    val navController = rememberNavController()
    NavHost(navController = navController, startDestination = "library") {
        composable("library") {
            LibraryScreen(
                viewModel = viewModel,
                onOpenDocument = { document ->
                    viewModel.recordOpened(File(document.path))
                    navController.navigate("viewer/${Uri.encode(document.path)}")
                },
                onOpenSettings = { navController.navigate("settings") },
            )
        }
        composable("settings") {
            SettingsScreen(viewModel = viewModel, onBack = { navController.popBackStack() })
        }
        composable("viewer/{path}") { backStackEntry ->
            val path = Uri.decode(backStackEntry.arguments?.getString("path").orEmpty())
            ViewerScreen(
                path = path,
                isDark = viewerInDarkTheme(viewModel),
                onBack = { navController.popBackStack() },
            )
        }
    }
}

/** Resolves the effective dark flag from the tri-state setting. */
@Composable
internal fun viewerInDarkTheme(viewModel: ComposeViewerViewModel): Boolean {
    val darkMode by viewModel.darkMode.collectAsState()
    return viewerDarkMode(darkMode)
}

/** Resolves the tri-state [ViewerDarkMode] setting into the effective dark flag. */
@Composable
private fun viewerDarkMode(darkMode: Int): Boolean = when (darkMode) {
    ViewerDarkMode.DARK -> true
    ViewerDarkMode.LIGHT -> false
    else -> isSystemInDarkTheme()
}

```

This code sample is an example that illustrates how to use our SDK. Please adapt it to your specific use case.

---

## Related pages

- [Add LTV to Existing Signature](/guides/android/samples/add-ltv-to-existing-signature-kotlin.md)
- [AES Encrypted File](/guides/android/samples/aes-encrypted-file-java.md)
- [AI Assistant (Multiple Documents, Compose)](/guides/android/samples/ai-assistant-multiple-documents-compose-kotlin.md)
- [AI Assistant (Multiple Documents, ViewPager)](/guides/android/samples/ai-assistant-multiple-documents-viewpager-kotlin.md)
- [AI Assistant (Single Document)](/guides/android/samples/ai-assistant-single-document-kotlin.md)
- [Annotation Configuration](/guides/android/samples/annotation-configuration-kotlin.md)
- [Annotation Creation](/guides/android/samples/annotation-creation-kotlin.md)
- [Annotation Flags](/guides/android/samples/annotation-flags-kotlin.md)
- [Annotation Overlay](/guides/android/samples/annotation-overlay-java.md)
- [Annotation Rendering](/guides/android/samples/annotation-rendering-kotlin.md)
- [Annotation Selection Styling](/guides/android/samples/annotation-selection-styling-kotlin.md)
- [Annotation Sidebar](/guides/android/samples/annotation-sidebar-kotlin.md)
- [Annotation Toolbar: Custom Items](/guides/android/samples/annotation-toolbar-custom-items-kotlin.md)
- [Annotation Toolbar: Item Grouping](/guides/android/samples/annotation-toolbar-item-grouping-kotlin.md)
- [Annotation Toolbar: User-Editable Tools](/guides/android/samples/annotation-toolbar-user-editable-tools-kotlin.md)
- [Annotations with Transparency](/guides/android/samples/annotations-with-transparency-kotlin.md)
- [Application Policy](/guides/android/samples/application-policy-kotlin.md)
- [Bookmark Highlighting](/guides/android/samples/bookmark-highlighting-kotlin.md)
- [Compose Image Document](/guides/android/samples/compose-image-document-kotlin.md)
- [Compose Navigation](/guides/android/samples/compose-navigation-kotlin.md)
- [Construction Floor Plan](/guides/android/samples/construction-floor-plan-kotlin.md)
- [Custom Activity Form Editing](/guides/android/samples/custom-activity-form-editing-java.md)
- [Custom Annotation Inspector](/guides/android/samples/custom-annotation-inspector-java.md)
- [Custom Data Provider](/guides/android/samples/custom-data-provider-kotlin.md)
- [Custom Download Dialog](/guides/android/samples/custom-download-dialog-java.md)
- [Custom Electronic Signature](/guides/android/samples/custom-electronic-signature-java.md)
- [Custom Form Highlight Color](/guides/android/samples/custom-form-highlight-color-java.md)
- [Custom Layout](/guides/android/samples/custom-layout-kotlin.md)
- [Custom Note Hinter](/guides/android/samples/custom-note-hinter-kotlin.md)
- [Custom Outline Provider](/guides/android/samples/custom-outline-provider-kotlin.md)
- [Custom Page Templates](/guides/android/samples/custom-page-templates-java.md)
- [Custom Search UI (Compose)](/guides/android/samples/custom-search-ui-compose-kotlin.md)
- [Custom Search UI (Views)](/guides/android/samples/custom-search-ui-views-java.md)
- [Custom Settings, Info and Outline UI](/guides/android/samples/custom-settings-info-and-outline-ui-kotlin.md)
- [Custom Sharing Dialog](/guides/android/samples/custom-sharing-dialog-java.md)
- [Custom Sharing Menu](/guides/android/samples/custom-sharing-menu-java.md)
- [Custom Stamp Annotations](/guides/android/samples/custom-stamp-annotations-java.md)
- [Digital Signature (Basic)](/guides/android/samples/digital-signature-basic-kotlin.md)
- [Digital Signature (Manual)](/guides/android/samples/digital-signature-manual-kotlin.md)
- [Digital Signature (Third-Party)](/guides/android/samples/digital-signature-third-party-kotlin.md)
- [Digital Signature (Two-Step)](/guides/android/samples/digital-signature-two-step-kotlin.md)
- [Disabled Annotation Property](/guides/android/samples/disabled-annotation-property-java.md)
- [Document Comparison](/guides/android/samples/document-comparison-kotlin.md)
- [Document Download](/guides/android/samples/document-download-kotlin.md)
- [Document from Canvas](/guides/android/samples/document-from-canvas-kotlin.md)
- [Document Processing](/guides/android/samples/document-processing-kotlin.md)
- [Document Sharing](/guides/android/samples/document-sharing-java.md)
- [Document Switcher](/guides/android/samples/document-switcher-java.md)
- [DocumentView Composable](/guides/android/samples/documentview-composable-kotlin.md)
- [DocumentView in a Dialog](/guides/android/samples/documentview-in-a-dialog-kotlin.md)
- [Download Progress](/guides/android/samples/download-progress-kotlin.md)
- [Dynamic Pages on Scroll](/guides/android/samples/dynamic-pages-on-scroll-kotlin.md)
- [E-Learning](/guides/android/samples/e-learning-kotlin.md)
- [Electronic + Digital Signing](/guides/android/samples/electronic-digital-signing-kotlin.md)
- [External Document](/guides/android/samples/external-document-kotlin.md)
- [File Annotation Creation](/guides/android/samples/file-annotation-creation-kotlin.md)
- [Filterable Thumbnail Grid](/guides/android/samples/filterable-thumbnail-grid-kotlin.md)
- [Form Click Intercept (Compose)](/guides/android/samples/form-click-intercept-compose-kotlin.md)
- [Form Creation](/guides/android/samples/form-creation-kotlin.md)
- [Form Filling](/guides/android/samples/form-filling-kotlin.md)
- [Forms with JavaScript](/guides/android/samples/forms-with-javascript-kotlin.md)
- [Fragment Runtime Configuration](/guides/android/samples/fragment-runtime-configuration-kotlin.md)
- [Generate PDF Report](/guides/android/samples/generate-pdf-report-kotlin.md)
- [Hide and Reveal Areas](/guides/android/samples/hide-and-reveal-areas-kotlin.md)
- [HTML-to-PDF Conversion](/guides/android/samples/html-to-pdf-conversion-kotlin.md)
- [Image Document](/guides/android/samples/image-document-kotlin.md)
- [Inline Multimedia](/guides/android/samples/inline-multimedia-kotlin.md)
- [Instant Document JSON](/guides/android/samples/instant-document-json-kotlin.md)
- [Instant JSON Attachment](/guides/android/samples/instant-json-attachment-kotlin.md)
- [JavaScript Actions](/guides/android/samples/javascript-actions-kotlin.md)
- [JavaScript Calculator](/guides/android/samples/javascript-calculator-kotlin.md)
- [JavaScript Form Filling](/guides/android/samples/javascript-form-filling-kotlin.md)
- [Kiosk Grid](/guides/android/samples/kiosk-grid-kotlin.md)
- [LTV Signature](/guides/android/samples/ltv-signature-kotlin.md)
- [Main Toolbar: Custom Layout & Items](/guides/android/samples/main-toolbar-custom-layout-items-kotlin.md)
- [Main Toolbar: FAB Menu (M3 Expressive)](/guides/android/samples/main-toolbar-fab-menu-m3-expressive-kotlin.md)
- [Main Toolbar: Floating Pill (M3 Expressive)](/guides/android/samples/main-toolbar-floating-pill-m3-expressive-kotlin.md)
- [Measurement Tools](/guides/android/samples/measurement-tools-kotlin.md)
- [Memory Pressure Recovery](/guides/android/samples/memory-pressure-recovery-kotlin.md)
- [Merge Documents](/guides/android/samples/merge-documents-kotlin.md)
- [Multimedia Annotations](/guides/android/samples/multimedia-annotations-kotlin.md)
- [Multiple Documents (Compose Pager)](/guides/android/samples/multiple-documents-compose-pager-kotlin.md)
- [OCR](/guides/android/samples/ocr-kotlin.md)
- [Overlay Views](/guides/android/samples/overlay-views-kotlin.md)
- [Overlay Visibility](/guides/android/samples/overlay-visibility-kotlin.md)
- [Password Protected PDF](/guides/android/samples/password-protected-pdf-kotlin.md)
- [PDF from Image](/guides/android/samples/pdf-from-image-kotlin.md)
- [PdfFragment](/guides/android/samples/pdffragment-kotlin.md)
- [PdfUiFragment](/guides/android/samples/pdfuifragment-kotlin.md)
- [Playground](/guides/android/samples/playground-kotlin.md)
- [Popup Toolbar: Custom Items](/guides/android/samples/popup-toolbar-custom-items-kotlin.md)
- [Programmatic Zoom](/guides/android/samples/programmatic-zoom-kotlin.md)
- [Reader View](/guides/android/samples/reader-view-kotlin.md)
- [Remote URL](/guides/android/samples/remote-url-kotlin.md)
- [Replace and Refresh Documents](/guides/android/samples/replace-and-refresh-documents-kotlin.md)
- [Runtime Configuration](/guides/android/samples/runtime-configuration-kotlin.md)
- [Scientific Paper](/guides/android/samples/scientific-paper-kotlin.md)
- [Screen Reader](/guides/android/samples/screen-reader-java.md)
- [Search Indexing](/guides/android/samples/search-indexing-kotlin.md)
- [Selection Customization](/guides/android/samples/selection-customization-java.md)
- [Signature Parcelize](/guides/android/samples/signature-parcelize-kotlin.md)
- [Signature Storage Database](/guides/android/samples/signature-storage-database-kotlin.md)
- [Sound Extraction](/guides/android/samples/sound-extraction-kotlin.md)
- [Split Screen (Compose)](/guides/android/samples/split-screen-compose-kotlin.md)
- [Split View](/guides/android/samples/split-view-java.md)
- [Tabbed Documents (Compose)](/guides/android/samples/tabbed-documents-compose-kotlin.md)
- [Tabbed Documents](/guides/android/samples/tabbed-documents-kotlin.md)
- [Tabbed Documents (Persistent)](/guides/android/samples/tabbed-documents-persistent-kotlin.md)
- [Text Field Suggestions](/guides/android/samples/text-field-suggestions-kotlin.md)
- [Thumbnail Bar Modes](/guides/android/samples/thumbnail-bar-modes-kotlin.md)
- [Try Instant](/guides/android/samples/try-instant-kotlin.md)
- [UI View Modes](/guides/android/samples/ui-view-modes-kotlin.md)
- [Vertical Scrollbar](/guides/android/samples/vertical-scrollbar-java.md)
- [Watermarks](/guides/android/samples/watermarks-kotlin.md)
- [XFDF Import/Export](/guides/android/samples/xfdf-import-export-kotlin.md)

