> ## Documentation Index
> Fetch the complete documentation index at: https://mahmoud-b28887f9.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# KPdfExternalOpenState: open PDF in an external app

> KPdfExternalOpenState tracks handing the current PDF to an external app — from byte export through intent dispatch to confirmation, cancellation, or error.

`KPdfExternalOpenState` is a sealed interface that models the full lifecycle of sending the current PDF to an installed external application — for example, opening the file in a third-party PDF viewer or annotation tool. Collect it from `KPdfViewerState.externalOpenState` (a `StateFlow`) to surface progress and result messages. Trigger the flow by calling `viewerState.requestOpenInExternalApp()` or `viewerState.openInExternalApp()`. The state resets to `Idle` when a new request begins.

## Variants

<ResponseField name="Idle" type="data object">
  No external-open operation is in progress. This is the initial state and the state after a terminal event has been observed and the next request starts.
</ResponseField>

<ResponseField name="Exporting" type="data object">
  The SDK is serialising the current PDF into bytes before dispatching the platform intent or share sheet. Display a loading indicator while in this state. The external app has not been invoked yet.
</ResponseField>

<ResponseField name="AwaitingExternalApp" type="data class">
  Serialisation is complete and the platform intent (Android) or share sheet (iOS) has been dispatched. The SDK is waiting for the platform to confirm the handoff.

  <ResponseField name="requestId" type="Long" required>
    Unique identifier for this open request. Use it to correlate `AwaitingExternalApp`, `Success`, and `Cancelled` events across the lifecycle.
  </ResponseField>

  <ResponseField name="suggestedFileName" type="String" required>
    The file name embedded in the intent or share sheet, such as `document.pdf` or a title derived from PDF metadata.
  </ResponseField>

  <ResponseField name="mimeType" type="String" required>
    The MIME type passed to the intent or share sheet, typically `application/pdf`.
  </ResponseField>
</ResponseField>

<ResponseField name="Success" type="data class">
  The platform confirmed that the file was successfully handed off to an external app.

  <ResponseField name="requestId" type="Long" required>
    The identifier of the completed request, matching `AwaitingExternalApp.requestId`.
  </ResponseField>

  <ResponseField name="suggestedFileName" type="String" required>
    The file name that was used in the handoff.
  </ResponseField>

  <ResponseField name="mimeType" type="String" required>
    The MIME type that was used in the handoff.
  </ResponseField>

  <ResponseField name="location" type="String?" required>
    A platform-specific URI or path for the temporary file created during the handoff, if the platform makes it available. `null` on platforms that do not expose the path.
  </ResponseField>
</ResponseField>

<ResponseField name="Cancelled" type="data class">
  The user dismissed the share sheet or app chooser without selecting a target. No external app received the file.

  <ResponseField name="requestId" type="Long" required>
    The identifier of the cancelled request.
  </ResponseField>

  <ResponseField name="suggestedFileName" type="String" required>
    The file name that was proposed during the flow.
  </ResponseField>

  <ResponseField name="mimeType" type="String" required>
    The MIME type that was proposed during the flow.
  </ResponseField>
</ResponseField>

<ResponseField name="Error" type="data class">
  The external-open flow failed — either during byte serialisation or during the platform handoff.

  <ResponseField name="reason" type="KPdfError" required>
    A sealed `KPdfError` value describing the failure. Check `reason.message` for a human-readable description.
  </ResponseField>

  <ResponseField name="suggestedFileName" type="String?" required>
    The file name that was in use when the error occurred, or `null` if the error happened before a file name was determined.
  </ResponseField>

  <ResponseField name="mimeType" type="String?" required>
    The MIME type that was in use when the error occurred, or `null` if the error happened before a MIME type was determined.
  </ResponseField>
</ResponseField>

## Reacting to external open state in Compose

Collect `externalOpenState` and branch on each variant to update your UI. Call `requestOpenInExternalApp()` — optionally with a `suggestedFileName` — to start the flow.

```kotlin theme={null}
@Composable
fun ExternalOpenControls(viewerState: KPdfViewerState) {
    val externalOpenState by viewerState.externalOpenState.collectAsState()

    Button(
        onClick = {
            viewerState.requestOpenInExternalApp(
                suggestedFileName = "invoice.pdf"
            )
        }
    ) {
        Text("Open In External App")
    }

    when (val state = externalOpenState) {
        KPdfExternalOpenState.Idle -> Unit

        KPdfExternalOpenState.Exporting -> {
            LinearProgressIndicator()
            Text("Preparing PDF…")
        }

        is KPdfExternalOpenState.AwaitingExternalApp -> {
            Text("Opening "${state.suggestedFileName}" in external app…")
        }

        is KPdfExternalOpenState.Success -> {
            Text("Opened successfully.")
        }

        is KPdfExternalOpenState.Cancelled -> {
            Text("Open cancelled.")
        }

        is KPdfExternalOpenState.Error -> {
            Text("Could not open: ${state.reason.message}")
        }
    }
}
```

## Notes

* `KPdfViewerState.externalOpenState` is a `StateFlow<KPdfExternalOpenState>`, so it always has a current value and collecting it never suspends on the first frame.
* `requestOpenInExternalApp(suggestedFileName)` lets you override the default file name embedded in the intent or share sheet. Use `openInExternalApp()` to rely on the SDK-inferred name.
  * If you use `rememberPdfViewerState`, the platform effect that handles the intent result is already wired automatically. You do not need to register any platform callbacks yourself.
* `requestId` is a monotonically increasing `Long`. If the user triggers the flow multiple times in quick succession, each request receives a distinct identifier so stale terminal states can be safely ignored.
* `KPdfExternalOpenState` mirrors the shape of `KPdfSaveState`. The key difference is that `KPdfSaveState` writes a persistent file chosen by the user, while `KPdfExternalOpenState` hands a temporary file to another app and does not guarantee persistence.
