Documentation Index
Fetch the complete documentation index at: https://mahmoud-b28887f9.mintlify.app/llms.txt
Use this file to discover all available pages before exploring further.
KPDF exposes every stage of a document’s lifecycle — loading, rendering, saving, and picking from device — as observable StateFlow properties on KPdfViewerState. Collect these flows in your composables and respond to each state with the appropriate UI. This page covers KPdfLoadState in depth and introduces the patterns for KPdfSaveState, KPdfOpenDocumentState, and KPdfExternalOpenState.
KPdfLoadState
KPdfLoadState tracks the primary document load pipeline from the moment you call open() until the document is ready to display or an error occurs.
States
| State | Description |
|---|
Idle | No load has been initiated, or a previous document was closed. |
Loading | The SDK is actively fetching, decoding, or processing the source. |
Ready(pageCount, title?) | The document is open. pageCount is the total number of pages. title is the document title from metadata, or null if none is set. |
Error(reason) | The load pipeline failed. reason is a KPdfError describing what went wrong. |
Collecting load state
@Composable
fun PdfScreen(source: KPdfSource) {
val viewerState = rememberPdfViewerState(source = source)
val loadState by viewerState.loadState.collectAsState()
Box(modifier = Modifier.fillMaxSize()) {
KPdfViewer(
state = viewerState,
modifier = Modifier.fillMaxSize(),
)
when (val state = loadState) {
KPdfLoadState.Idle -> {
// Nothing to show yet
}
KPdfLoadState.Loading -> {
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
}
is KPdfLoadState.Ready -> {
// Document is visible — optionally show page count
Text(
text = "${state.pageCount} pages",
modifier = Modifier.align(Alignment.TopEnd).padding(8.dp),
)
}
is KPdfLoadState.Error -> {
Column(modifier = Modifier.align(Alignment.Center)) {
Text("Failed to load document: ${state.reason}")
Button(onClick = { viewerState.retry() }) {
Text("Retry")
}
}
}
}
}
}
Once the state is Ready, access pageCount and title from the state object:
val loadState by viewerState.loadState.collectAsState()
val ready = loadState as? KPdfLoadState.Ready
val pageCount = ready?.pageCount ?: 0
val docTitle = ready?.title ?: "Untitled"
KPdfSaveState
KPdfSaveState tracks the full lifecycle of a save or export operation initiated by requestSave() or savePdf(). The platform integration layer included with rememberPdfViewerState handles the file-picker handoff automatically, so you normally only need to observe this flow to update your UI.
| State | Description |
|---|
Idle | No save is in progress. |
Exporting | The SDK is serializing the PDF bytes before presenting the destination picker. |
AwaitingDestination(requestId, suggestedFileName, mimeType) | The platform file picker is open and waiting for the user to choose a save location. |
Success(requestId, suggestedFileName, mimeType, location?) | The file was saved. location is the resolved path or URI, if the platform provides it. |
Cancelled(requestId, suggestedFileName, mimeType) | The user dismissed the picker without selecting a destination. |
Error(reason, suggestedFileName?, mimeType?) | The save failed. |
val saveState by viewerState.saveState.collectAsState()
when (saveState) {
KPdfSaveState.Idle -> Unit
KPdfSaveState.Exporting -> Text("Preparing PDF...")
is KPdfSaveState.AwaitingDestination -> Text("Choose where to save...")
is KPdfSaveState.Success -> Text("PDF saved successfully.")
is KPdfSaveState.Cancelled -> Text("Save cancelled.")
is KPdfSaveState.Error -> Text("Save failed: ${saveState.reason}")
}
Trigger the save flow from a button:
Button(onClick = { viewerState.savePdf(suggestedFileName = "report.pdf") }) {
Text("Save")
}
KPdfOpenDocumentState
KPdfOpenDocumentState tracks the platform file picker flow initiated by requestOpenFromDevice(). When the user selects a file, the state moves to Success and exposes the selected KPdfSource. Call viewerState.open(source) to load it.
| State | Description |
|---|
Idle | No picker session is active. |
AwaitingSelection(requestId, mimeTypes) | The platform picker is open and waiting for the user to select a file. |
Success(source) | The user selected a file. source is the resulting KPdfSource. |
Cancelled | The user dismissed the picker without selecting a file. |
Error(reason) | The picker session failed. |
val openState by viewerState.openDocumentState.collectAsState()
LaunchedEffect(openState) {
val selectedSource = (openState as? KPdfOpenDocumentState.Success)?.source
?: return@LaunchedEffect
viewerState.open(selectedSource)
}
Button(onClick = { viewerState.requestOpenFromDevice() }) {
Text("Open local PDF")
}
If openDocumentState never leaves Idle after you call requestOpenFromDevice(), check that your KPdfViewerConfig and KPdfSource are stable references wrapped in remember. Recreating them on every recomposition also recreates viewerState, which resets all in-flight flows.
KPdfExternalOpenState
KPdfExternalOpenState tracks the lifecycle of handing the current PDF to an external application via openInExternalApp(). The states mirror KPdfSaveState in structure.
| State | Description |
|---|
Idle | No external-open is in progress. |
Exporting | The SDK is preparing the PDF bytes to share. |
AwaitingExternalApp(requestId, suggestedFileName, mimeType) | The platform app picker is open. |
Success(requestId, suggestedFileName, mimeType, location?) | The external app received the file. |
Cancelled(requestId, suggestedFileName, mimeType) | The user dismissed the picker. |
Error(reason, suggestedFileName?, mimeType?) | The operation failed. |
val externalOpenState by viewerState.externalOpenState.collectAsState()
when (externalOpenState) {
KPdfExternalOpenState.Idle -> Unit
KPdfExternalOpenState.Exporting -> Text("Preparing PDF...")
is KPdfExternalOpenState.AwaitingExternalApp -> Text("Opening external app...")
is KPdfExternalOpenState.Success -> Text("Opened in external app.")
is KPdfExternalOpenState.Cancelled -> Text("Cancelled.")
is KPdfExternalOpenState.Error -> Text("Failed to open external app.")
}