Compose Camp 2: Mastering Images in Jetpack Compose with Coil-Compose and Landscapist
Images and graphics add beauty and life to android apps. In android development, you will be required to design and load images and graphics into your application. Jetpack Compose has APIs and external libraries that helps you handle the visuals like images and graphics in your android app, from your logo to complex images and graphics. In this article, we will cover
How to design and customize images in Jetpack Compose
Image loading in compose
Design and Customize Images in Jetpack Compose
Jetpack compose makes it easy to design and customize images and graphics. Here is an example of how to create an image
@Composable
fun PosView
Image(
painter = painterResource(R.drawable.post_image),
contentDescription = "Post image",
modifier = Modifier.fillMaxSize()
)
}
In the above code, you declare an Image
that takes in painterResource,
which loads the image from the drawable folder, has contentDescription
of “Post Image” and occupies the available space using fillMaxSize
modifier.
Sizing and Scaling
You can modify the size of the image above by adding size
modifier.
@Composable
fun PosView(){
Image(
painter = painterResource(R.drawable.post_image),
contentDescription = "Post image",
modifier = Modifier
.size(150.dp)
)
}
You need to tell Jetpack Compose how to scale the image. Some of the common scale types are
Fit — scales the image uniformly inside the available space
FillWidth — image fills the available width
FillHeight — image fills the available height
Crop — crops image to fit destination size
Inside — image maintains aspect ratio inside destination
None — no scaling applied
You can apply scaling to the above image using the ContentScale
as shown below.
@Composable
fun PosView(){
Image(
painter = painterResource(R.drawable.post_image),
contentDescription = "Post image",
contentScale = ContentScale.Crop
modifier = Modifier
// Change image size to 150 dp
.size(150.dp)
)
}
You can read more on scaling in the official documentation here.
Shape
You can modify the shape of image in Jetpack Compose to common shapes like Circle, Rectangle or Square with rounded corners and custom shapes.
Compose uses clip modifer to use shapes such as CircleShape,
RoundedCornerShape
.
Here, we will change the shape of the PostView
to CircleShape
@Composable
fun PosView(){
Image(
painter = painterResource(R.drawable.post_image),
contentDescription = "Post image",
contentScale = ContentScale.Crop
modifier = Modifier
.size(150.dp)
// Make the image circular
.clip(CircleShape)
)
}
When using RoundedCornerShape
clip, ensure you declare the size of corner radius. For example
@Composable
fun PosView(){
Image(
painter = painterResource(R.drawable.post_image),
contentDescription = "Post image",
contentScale = ContentScale.Crop
modifier = Modifier
.size(150.dp)
// Rounded corner shape with 8dp radius
.clip(RoundedCornerShape(8.dp))
)
}
When creating an image with custom shape, you use aspectRatio
. Common image aspect ratios are
1:1 — square image
4:3 — standard aspect ratio
16:9 wide image. Commonly used in widescreen
3:2 — classic aspect ratio
@Composable
fun PosView(){
Image(
painter = painterResource(R.drawable.post_image),
contentDescription = "Post image",
contentScale = ContentScale.Crop
modifier = Modifier
.aspectRatio(16f / 9f)
)
}
You can read more about image scaling and additional modifiers here
Loading Images
You have to load images from a source to your application, either local or remote. We have been loading images from the drawable folder in the above examples. You can also load images from the web.
In this section, we will look at two common libraries used in compose — landscapist and coil-compose
Coil Compose
Coil-compose is built on top of coil image loading library. It simplifies asynchronous loading of images using coroutines. To get started, add coil-compose dependency in your application. You can change the version to the latest version.
implementation("io.coil-kt:coil-compose:2.2.2")
We use AsycImage in Coil-Compose, and the above example changes as shown below.
@Composable
fun PosView(){
AsyncImage(
//This is an example image from Unspash.com
model = "https://unsplash.com/photos/6xv4A1VA1rU",
contentDescription = "Post image",
contentScale = ContentScale.Crop
modifier = Modifier
.aspectRatio(16f / 9f)
)
}
Coil provides additional modifications when loading images from the web. Here are some of the modifications you can apply to your app.
- Model
This is an instance of ImageRequest
, which defines the details of the image to be loaded. In this case, it specifies the URL of the image to be loaded from the internet and builds it to an image.
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data("https://unsplash.com/photos/6xv4A1VA1rU")
.build(),
contentDescription = "Post image",
contentScale = ContentScale.Crop,
modifier = Modifier
.aspectRatio(16f / 9f)
)
- Placeholder
You can have placeholder images loading from the local storage, which will be displayed when the network image is loading or has failed.
AsyncImage(
model = ImageRequest.Builder(LocalContext.current)
.data("https://unsplash.com/photos/6xv4A1VA1rU")
.build(),
contentDescription = "Post image",
contentScale = ContentScale.Crop,
placeholder = painterResource(id = R.drawable.loading_placeholder),
error = painterResource(id = R.drawable.image_error),
modifier = Modifier
.aspectRatio(16f / 9f)
)
- Observe State
We will use AsyncImagePainter
to observe state when loading images.
val asyncPainter = rememberAsyncImagePainter(
model = ImageRequest.Builder(LocalContext.current)
.data("https://unsplash.com/photos/6xv4A1VA1rU")
.size(Size.ORIGINAL)
.build(),
)
Image(
painter = asyncPainter,
contentDescription = "Post image"
modifier = Modifier
.aspectRatio(16f / 9f)
)
- Crossfade
Coil Compose has an inbuilt crossfade
modifier for transitions. Custom transition requires reference to the view, thus not available in AsyncImage
and AsyncImagePainter
we have used in the above example. You can add crossfade
in the above code and achieve beautiful transitions.
val asyncPainter = rememberAsyncImagePainter(
model = ImageRequest.Builder(LocalContext.current)
.data("https://unsplash.com/photos/6xv4A1VA1rU")
.size(Size.ORIGINAL)
.crossfade(true)
.build(),
)
Image(
painter = asyncPainter,
contentDescription = "Post image"
modifier = Modifier
.aspectRatio(16f / 9f)
)
You can read more about Coil-compose in the official documentation.
Landscapist
Landscapist is another library that simplifies image loading using popular image-loading libraries like Glide, Coil and Fresco.
- Glide
Add landscapist-glide dependency in your grade build file.
implementation "com.github.skydoves:landscapist-glide:2.1.8"
To load image using glide, use the following
GlideImage(
imageModel = { "<https://unsplash.com/photos/6xv4A1VA1rU>" }
imageOptions = ImageOptions(
contentScale = ContentScale.Crop,
alignment = Alignment.Center
modifier = Modifier
.aspectRatio(16f / 9f)
)
)
- Coil
Add landscapist-coil dependency in your grade build file.
implementation "com.github.skydoves:``landscapist-coil:2.1.8"
To load image using glide, use the following
CoilImage(
imageModel = { "<https://unsplash.com/photos/6xv4A1VA1rU>" }
imageOptions = ImageOptions(
contentScale = ContentScale.Crop,
alignment = Alignment.Center
modifier = Modifier
.aspectRatio(16f / 9f)
)
)
- Fresco
Add landscapist-fresco dependency in your grade build file.
implementation "com.github.skydoves:``landscapist-fresco:2.1.8"
To load image using glide, use the following
CoilImage(
imageModel = { "<https://unsplash.com/photos/6xv4A1VA1rU>" }
imageOptions = ImageOptions(
contentScale = ContentScale.Crop,
alignment = Alignment.Center
modifier = Modifier
ko.aspectRatio(16f / 9f)
)
)
To use Fresco, you need an additional configuration called pipeline.
class ImageApp : Application() {
override fun onCreate() {
super.onCreate()
val pipelineConfig =
OkHttpImagePipelineConfigFactory
.newBuilder(this, OkHttpClient.Builder().build())
.setDiskCacheEnabled(true)
.setDownsampleEnabled(true)
.setResizeAndRotateEnabledForNetwork(true)
.build()
Fresco.initialize(this, pipelineConfig)
}
}
Check the official documentation to learn more about customizing images and image-loading libraries in Jetpack Compose.