Images play a crucial role in modern Android apps, enhancing the user experience with rich visuals. Jetpack Compose, the modern UI toolkit for Android, provides a declarative and efficient way to handle images. In this guide, we'll explore everything you need to know about using images in Jetpack Compose, from basic implementation to advanced techniques like caching, transformations, and dynamic manipulations.
An image refers to a visual element (such as a picture, icon, or graphic) used to enhance the user interface (UI) and improve the user experience (UX). Images can be used for branding, navigation, buttons, backgrounds, and illustrations.
Types of Images
1. Raster Images (Bitmap Images)
- Made of pixels and have a fixed resolution.
- Quality decreases when scaled up.
- Common formats: PNG, JPEG, WEBP, BMP, GIF
- Example: Photos, UI icons, and backgrounds.
2. Vector Images
- Made of mathematical paths (lines and curves).
- Scalable without losing quality.
- Common formats: SVG, PDF, AI (Adobe Illustrator), EPS
- Example: Logos, icons, illustrations.
3. Nine-Patch Images (Android-specific)
- Special PNG images that stretch dynamically.
- Used for buttons, backgrounds, and UI elements in Android.
4. Animated Images
- Moving images for better UI interaction.
- Formats: GIF (simple animations), WebP (optimized animation), Lottie (JSON-based vector animation)
- Example: Loading indicators, animated stickers.
5. Drawable Images (Android-specific)
- XML-based graphics used in Jetpack Compose and XML layouts.
- Example: Shape Drawables (buttons, borders), Layer Drawables, State List Drawables (for different button states).
6. Remote Images
- Images loaded from the internet using APIs.
- Example: Images from Firebase, Unsplash API.
- Libraries: Glide, Coil (for Jetpack Compose), Picasso.
How to Choose the Right Image Type
- For icons and logos: Use SVG (vector) for scalability.
- For high-quality photos: Use JPEG (compressed) or PNG (transparent backgrounds).
- For animations: Use Lottie (vector) or WebP (optimized GIF alternative).
- For app backgrounds: Use WebP (better compression and quality).
1. Loading Images in Jetpack Compose
Jetpack Compose provides the Image and painterResource APIs to load images efficiently.
1.1 Loading Local Images (From Drawable & Assets)
If your image is stored in the res/drawable or res/mipmap folders, use painterResource:
@Composable
fun LocalImageExample() {
Image(
painter = painterResource(id = R.drawable.local_image),
contentDescription = "Image by Pasi Mämmelä from Pixabay",
modifier = Modifier.size(200.dp)
)
Text("Image by Pasi Mämmelä from Pixabay")
}
@Composable
fun AssetImageExample() {
val context = LocalContext.current
val assetManager = context.assets
val bitmap = try {
BitmapFactory.decodeStream(assetManager.open("asset_image.jpg"))?.asImageBitmap()
} catch (_: Exception) {
null
}
bitmap?.let {
Image(bitmap = it,
contentDescription = "Asset Image",
modifier = Modifier.size(200.dp))
} ?: Text("Failed to load asset image")
}
1.2 Loading Remote Images (From a URL)
For loading images from the internet, popular libraries like Coil, Glide, and Picasso are used.
Using Coil (Recommended)
Coil is designed for Jetpack Compose and is lightweight.
Add the Coil dependency in build.gradle:
Now, use rememberAsyncImagePainter:implementation("io.coil-kt:coil-compose:2.6.0")
@Composable
fun RemoteImageExample() {
Image(
painter = rememberAsyncImagePainter("https://cdn.pixabay.com/photo/2025/03/30/08/07/entomology-9502316_1280.jpg"),
contentDescription = "Remote Image",
modifier = Modifier.size(200.dp)
)
}
2. Image Scaling and Cropping
Jetpack Compose offers various contentScale options to fit images properly.
Option | Description |
---|---|
Crop |
Fills the entire space, cropping the excess |
Fit |
Scales to fit inside without cropping |
FillBounds |
Stretches to fit |
Example:
@Composable
fun ImageScalingExample() {
Image(
painter = painterResource(id = R.drawable.local_image),
contentDescription = "Scaled Image",
contentScale = ContentScale.Crop,
modifier = Modifier.size(200.dp)
)
}
3. Applying Image Filters and Modifiers
3.1 Adding Rounded Corners & Circular Images
@Composable
fun RoundedImageExample() {
Image(
painter = painterResource(id = R.drawable.local_image),
contentDescription = "Rounded Image",
modifier = Modifier
.size(150.dp)
.clip(RoundedCornerShape(20.dp))
)
}
For circular images:
@Composable
fun CircularImageExample() {
Image(
painter = painterResource(id = R.drawable.local_image),
contentDescription = "Circular Image",
modifier = Modifier
.size(150.dp)
.clip(CircleShape)
)
}
3.2 Adding Blur Effect (Using RenderScript)
@Composable
fun BlurredImageExample() {
Image(
painter = painterResource(id = R.drawable.local_image),
contentDescription = "Blurred Image",
modifier = Modifier
.size(200.dp)
.graphicsLayer {
renderEffect = BlurEffect(
25f, 25f
)
}
)
}
Jetpack Compose does not natively support blurring images, but we can achieve this using RenderScript:
4. Advanced Image Features
4.1 Image Caching for Better Performance
Using Coil's caching mechanism:
@Composable
fun CachingImage() {
val painter = rememberAsyncImagePainter(
model = ImageRequest.Builder(LocalContext.current)
.data("https://cdn.pixabay.com/photo/2025/03/30/08/07/entomology-9502316_1280.jpg")
.diskCachePolicy(CachePolicy.ENABLED) // Enables caching
.build()
)
Image(
painter = painter,
contentDescription = "Cached Image",
Modifier.size(400.dp)
)
}
4.2 Gesture Support (Pinch to Zoom, Drag)
Jetpack Compose provides easy touch gesture handling.
@Composable
fun ZoomableImageExample() {
val scale = remember { mutableFloatStateOf(1f) }
val state = rememberTransformableState { zoomChange, _, _ ->
scale.floatValue *= zoomChange
}
Box(
modifier = Modifier
.fillMaxSize()
.transformable(state)
) {
Image(
painter = painterResource(id = R.drawable.local_image),
contentDescription = "Zoomable Image",
modifier = Modifier.scale(scale.floatValue)
)
}
}
5. Loading SVG and GIF Images
5.1 Loading SVG Images
Jetpack Compose does not support SVG directly, but you can use coil-svg:
implementation("io.coil-kt:coil-compose:2.4.0")
implementation("io.coil-kt:coil-svg:2.4.0")
@Composable
fun SvgFromAssetsWithCoil(fileName: String) {
val context = LocalContext.current
val imageLoader = ImageLoader.Builder(context)
.components {
add(SvgDecoder.Factory())
}
.build()
val painter = rememberAsyncImagePainter(
model = ImageRequest.Builder(context)
.data("file:///android_asset/$fileName")
.build(),
imageLoader = imageLoader
)
Image(
painter = painter,
contentDescription = "SVG from Assets",
Modifier.size(300.dp)
)
}
5.2 Loading GIFs
Use Coil with ImageDecoder support:
implementation("io.coil-kt:coil-gif:2.5.0")
@Composable
fun GifImageExample() {
Image(
painter = rememberAsyncImagePainter(
model = "https://cdn.pixabay.com/animation/2023/01/24/22/44/22-44-04-445_512.gif",
imageLoader = ImageLoader(LocalContext.current).newBuilder()
.components {
add(ImageDecoderDecoder.Factory()) // Enable GIF support
}
.build()
),
contentDescription = "Animated GIF",
modifier = Modifier.size(330.dp)
)
}
Conclusion
Jetpack Compose provides a robust and efficient way to handle images, whether they are local, remote, SVG, or GIFs. By leveraging libraries like Coil, Accompanist, and advanced transformations, you can enhance image handling in your Compose-based applications.
With these techniques, your app will be more optimized, visually appealing, and user-friendly! 🚀
Posted by Coding Bihar | Android & Jetpack Compose Tutorials