What is PDF?
PDF means Portable Document Format developed by Abode in the year 1992. It is a combination of vector and bitmap graphics. It is popular as it is portable and less memory consuming format and the most advance feature is that it can be encrypted for security in which a password is required to open, edit or view the contents.
How to Show Pdf in Jetpack Compose?
There are many third party external library are available to display PDF files. To display a PDF in Jetpack Compose, you need to use an external library because Jetpack Compose doesn't have built-in support for PDF rendering. One popular library for this purpose is AndroidPdfViewer.
dependencies {
implementation ("com.github.barteksc:android-pdf-viewer:3.2.0-beta.1")
}But in this tutorial, we are going to use the built-in Android APIs. Specifically, you can use Pdf Renderer to render PDF pages into bitmaps, which can then be displayed using Jetpack Compose's Image composable.
Why Pdf Renderer is used?
Pdf Renderer is used to render each page of the PDF into a bitmap, which is then displayed using Jetpack Compose's Image composable. So, this approach does not rely on any third-party libraries and uses only Android's built-in PDF rendering capabilities.
Steps:
1. Open Android Studio
2. Create a New Project and choose Empty Compose Activity.
3. Create assets folder and save pdf files in it.
4. MainAcitivity
5. Create a new file named PdfViewerApp
MainActivity
package com.codingbihar.pdfviewerapp
import ...
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
JetpackComposeSkillTheme {
val navController = rememberNavController()
PdfViewerApp(navController)
}
}
}
}PdfViewerApp
Copy this code →
package com.codingbihar.pdfviewerapp
import ...
@Composable
fun PdfViewerApp(navController: NavHostController) {
NavHost(navController, startDestination = "list") {
composable("list") { PdfList(navController) }
composable("pdfViewer/{fileName}") { backStackEntry ->
PdfViewer(
navController,
fileName = backStackEntry.arguments?.getString("fileName") ?: ""
)
}
}
}
@Composable
fun PdfList(navController: NavHostController) {
val context = LocalContext.current
val pdfFiles = remember { mutableStateOf>(emptyList()) }
LaunchedEffect(Unit) {
pdfFiles.value = try {
context.assets.list("")?.filter { it.endsWith(".pdf") } ?: emptyList()
} catch (e: IOException) {
emptyList()
}
}
Column(modifier = Modifier.fillMaxSize().statusBarsPadding()) {
pdfFiles.value.forEach { fileName ->
Button(onClick = {
navController.navigate("pdfViewer/$fileName")
}, modifier = Modifier.fillMaxWidth().padding(bottom = 8.dp)) {
Text(text = fileName)
}
}
if (pdfFiles.value.isEmpty()) {
Text("No PDFs available", modifier = Modifier.fillMaxWidth().padding(top = 16.dp))
}
}
}
@Composable
fun PdfViewer(navController: NavHostController, fileName: String) {
val context = LocalContext.current
val zoomState = remember { mutableFloatStateOf(1f) }
val bitmapState = remember { mutableStateOf(null) }
val coroutineScope = rememberCoroutineScope()
val currentPage = remember { mutableIntStateOf(0) }
val totalPages = remember { mutableIntStateOf(0) }
// Handle back press to navigate back to the list
BackHandler {
navController.popBackStack()
}
LaunchedEffect(fileName, currentPage.intValue) {
coroutineScope.launch {
try {
// Copy PDF from assets to cache
val cacheFile = File(context.cacheDir, fileName)
if (!cacheFile.exists()) {
context.assets.open(fileName).use { inputStream ->
FileOutputStream(cacheFile).use { outputStream ->
inputStream.copyTo(outputStream)
}
}
}
// Open PDF file and get the current page
val fileDescriptor = ParcelFileDescriptor.open(cacheFile, ParcelFileDescriptor.MODE_READ_ONLY)
val pdfRenderer = PdfRenderer(fileDescriptor)
totalPages.intValue = pdfRenderer.pageCount // Set total pages
val page = pdfRenderer.openPage(currentPage.intValue)
val bitmap = Bitmap.createBitmap(page.width, page.height, Bitmap.Config.ARGB_8888)
page.render(bitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY)
withContext(Dispatchers.Main) {
bitmapState.value = bitmap
}
page.close()
pdfRenderer.close()
fileDescriptor.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
}
Column(modifier = Modifier.fillMaxSize()) {
Box(modifier = Modifier
.weight(1f)
.fillMaxSize()
.pointerInput(Unit) {
detectHorizontalDragGestures { change, dragAmount ->
change.consume()
if (dragAmount > 0) {
// Swipe right to left
if (currentPage.intValue < totalPages.intValue - 1) {
currentPage.intValue += 1
}
} else if (dragAmount < 0) {
// Swipe left to right
if (currentPage.intValue > 0) {
currentPage.intValue -= 1
}
}
}
}) {
bitmapState.value?.let { bmp ->
Image(
bitmap = bmp.asImageBitmap(),
contentDescription = "PDF Page",
modifier = Modifier
.fillMaxSize()
.simpleZoomable(zoomState)
.graphicsLayer(
scaleX = zoomState.floatValue,
scaleY = zoomState.floatValue
),
contentScale = ContentScale.Fit
)
} ?: run {
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
}
}
// Navigation Controls (Optional, for testing purposes)
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween
) {
Button(
onClick = {
if (currentPage.intValue > 0) {
currentPage.intValue -= 1
}
}
) {
Text("Previous")
}
Button(
onClick = {
if (currentPage.intValue < totalPages.intValue - 1) {
currentPage.intValue += 1
}
}
) {
Text("Next")
}
}
}
}
fun Modifier.simpleZoomable(
zoomState: MutableState
) = this.pointerInput(Unit) {
detectTransformGestures { _, _, zoomChange, _ ->
val newZoom = max(1f, zoomState.value * zoomChange)
zoomState.value = min(2f, newZoom)
}
}
OUTPUT:
FAQ: How to Show PDF with Jetpack Compose
❓ 1. Can Jetpack Compose display PDFs on its own?
No — Jetpack Compose doesn’t include built-in PDF rendering support. You must either use Android’s PdfRenderer API or a third-party library to display PDF content in your app.
❓ 2. What is the PdfRenderer API and why use it?
PdfRenderer is an Android API that renders each page of a PDF file into a Bitmap, which you can then show inside Compose using an Image. It doesn’t require any external libraries.
❓ 3. What dependencies do I need?
If you are only using PdfRenderer, you don’t need extra dependencies — it’s part of Android’s framework.
Alternatively, if you choose a library like AndroidPdfViewer or a Compose-friendly PDF viewer, you might add a dependency such as:
implementation "com.github.barteksc:android-pdf-viewer:3.2.0-beta.1"—but the tutorial example focuses on PdfRenderer without third-party libraries.
❓ 4. Where should the PDF file be placed?
Place the PDF file inside the assets folder of your Android project. If missing, create an assets/ directory under main/. Then list and load them dynamically.
❓ 5. How does the PDF get shown on screen?
- Copy the PDF from assets to cache.
- Use ParcelFileDescriptor + PdfRenderer to open the file.
- Render the desired page into a Bitmap.
- Display that bitmap using Image in Compose.
- You can add controls like “Next”/“Prev” to navigate pages.
❓ 6. How can I implement zooming?
The tutorial includes a custom modifier function (simpleZoomable) that uses pointer input and gesture detection to scale the image (e.g., pinch-to-zoom).
❓ 7. Is page navigation (swiping) supported?
Yes — the sample implementation detects horizontal drag gestures to move between pages (left/right).
❓ 8. Do I need third-party libraries for better PDF support?
Not necessarily — PdfRenderer works but it’s limited (shows bitmaps, no text selection, search, forms etc.). If you want richer PDF features (text search, annotations, form filling), consider libraries built for Compose or Android PDF SDKs.
❓ 9. Are there ready-made Compose libraries for PDF viewing?
Yes — open-source libraries like JetPDFVue or bouquet provide Compose-friendly PDF viewers with features like zoom, various sources (URL, URI), and interactive viewers.
❓ 10. Will this work on all Android versions?
PdfRenderer works on Android 5.0+ (API 21+) but if you need official system PDF viewer fragment components, newer AndroidX pdf-viewer APIs (alpha) are emerging which may offer more integrated support.





