In the world of modern app development, reading and displaying PDF files is a common requirement. Whether you want to build a document viewer, an e-book reader, or simply integrate PDF file support into your application, Jetpack Compose with Kotlin is a powerful combination that can make the task seamless and efficient. In this blog post, we will explore how to leverage Jetpack Compose and Kotlin to read and display PDF files with ease.
Prerequisites
Before we dive into the implementation, ensure you have a basic understanding of Jetpack Compose and Kotlin. Familiarity with Android development and layout composition using Compose would be beneficial as well.
Setting up the project
To get started, create a new Android project in Android Studio and make sure you have the necessary dependencies for Jetpack Compose. Add the following dependency to your app-level build.gradle file:
Note: We are using Latest Version Catalogue For Gradle Dependency Open Link
Add Following Dependency In libs.versions.toml
[versions]
agp = "8.2.0-alpha09"
kotlin = "1.8.10"
core-ktx = "1.9.0"
junit = "4.13.2"
pdfium-android = "1.9.0"
androidx-test-ext-junit = "1.1.5"
espresso-core = "3.5.1"
kotlin-gradle-plugin = "1.8.10"
lifecycle-runtime-ktx = "2.6.1"
activity-compose = "1.7.2"
compose-bom = "2023.06.00"
library = "8.0.1"
material = "1.9.0"
navigation-compose = "2.5.3"
androidx-appcompat = "1.6.1"
[libraries]
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" }
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin-gradle-plugin" }
lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle-runtime-ktx" }
activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity-compose" }
compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "compose-bom" }
ui = { group = "androidx.compose.ui", name = "ui" }
ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx-appcompat" }
#Compose
navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigation-compose" }
pdfium-android = { group = "com.github.barteksc", name = "pdfium-android", version.ref = "pdfium-android" }
[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
library = { id = "com.android.library", version.ref = "library" }
[bundles]
After That, Change build.gradle(project-level)
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
dependencies {
// Add this line
classpath(libs.kotlin.gradle.plugin)
}
repositories {
mavenCentral()
}
}
@Suppress("DSL_SCOPE_VIOLATION") // TODO: Remove once KTIJ-19369 is fixed
plugins {
alias(libs.plugins.androidApplication) apply false
alias(libs.plugins.kotlinAndroid) apply false
alias(libs.plugins.library) apply false
}
true // Needed to make the Suppress annotation work for the plugins block
Also, Change build.gradle(app-level)
dependencies {
implementation(libs.core.ktx)
implementation(libs.lifecycle.runtime.ktx)
implementation(libs.activity.compose)
implementation(platform(libs.compose.bom))
implementation(libs.ui)
implementation(libs.ui.graphics)
implementation(libs.ui.tooling.preview)
implementation(libs.material3)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.test.ext.junit)
androidTestImplementation(libs.espresso.core)
androidTestImplementation(platform(libs.compose.bom))
androidTestImplementation(libs.ui.test.junit4)
debugImplementation(libs.ui.tooling)
debugImplementation(libs.ui.test.manifest)
implementation(project(":pdfreader"))
implementation(libs.navigation.compose)
}
Don’t forget to add android.enableJetifier=true in gradle.properties because pdfium lib uses an older version so we have to add it to make our project run.
Once Successfully Sync the Gradle Now let’s move on to the Designing Section In Jetpack Compose.
Here is the PDF Viewer Screen.
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PDFViewerScreen(
navigateUp: () -> Unit
) {
Log.e(TAG, "PDFViewerScreen: ************** ")
val context = LocalContext.current
Scaffold(topBar = {
CenterAlignedTopAppBar(
navigationIcon = {
androidx.compose.material3.Icon(
modifier = Modifier
.padding(start = 15.dp, end = 16.dp)
.clickable(
interactionSource = MutableInteractionSource(),
indication = rememberRipple(
bounded = false,
color = Color.Black,
radius = 22.dp
),
onClick = {
navigateUp()
}
),
painter = painterResource(id = R.drawable.back),
contentDescription = "back",
tint = Color.White
)
},
title = {
androidx.compose.material3.Text(
text = "PDF Viewer",
color = Color.White,
fontWeight = FontWeight.Bold,
fontSize = 20.sp,
fontFamily = Urbanist,
)
}, colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
containerColor = androidx.compose.material3.MaterialTheme.colorScheme.primary
)
)
}) {
Column(
modifier = Modifier
.padding(it)
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally
) {
val inputStream = context.assets.open("gold.pdf")
AndroidView(
factory = { context ->
val adView = PDFView(context, null)
adView.fromStream(inputStream)
.enableDoubletap(true)
.spacing(10)
.load()
adView
},
modifier = Modifier
.fillMaxSize()
.padding(bottom = 8.dp),
update = { pdfViewer ->
pdfViewer.doOnLayout {
}
})
}
}
}
That’s it! You have successfully implemented PDF file reading using Jetpack Compose and Kotlin. Now, when you run your application, you should be able to see the PDF file rendered within your Compose UI.
If you enjoyed this blog post, don’t forget to give it a clap! Your support is greatly appreciated.
Example Code