About

Jetpack Compose is Android’s modern toolkit for building native UI. This library integrates with Compose to allow you to load images in your Compose apps with Glide in a performant manner.

Status

Glide’s Compose integration is new as of 9-2022 and as a result is labeled as an experimental API. Please submit bugs if you encounter any and/or feature requests to Glide’s Github Issues. We will try to minimize API churn, but at least while the Compose integration is experimental, there may be an occasional breaking change between versions.

How do I include the Compose integration library?

The Compose integration doesn’t include any components, so you don’t need any changes to your AppGlideModule to use it.

Instead, just add a Gradle dependency on the Compose integration library:

implementation "com.github.bumptech.glide:compose:1.0.0-alpha.1"

Usage

See Glide’s Gallery sample app for a small application that uses Compose and Glide’s Compose integration. See the Dokka page for detailed API documentation.

GlideImage

The primary integration point between Compose and Glide is GlideImage. GlideImage is meant to similarly to Compose’s Image function except that it uses Glide to asynchronously load images.

Simple use cases of GlideImage can include just a Model and a content description:

GlideImage(model = myUrl, contentDescription = getString(R.id.picture_of_cat))

You can supply a custom Modifier to customize how GlideImage is rendered:

GlideImage(
  model = myUrl,
  contentDescription = getString(R.id.picture_of_cat),
  modifier = Modifier.padding(padding).clickable(onClick = onClick).fillParentMaxSize(),
)

You can also provide the alignment, contentScale, colorFilter, and alpha parameters that have identical defaults and function identically to the same parameters in Compose’s Image.

To configure the Glide load, you can provide a RequestBuilderTransformation function. The function will be passed a RequestBuilder that already has load() called on it with your given model. You can then customize the request with any normal Glide option. The only exception is that Glide’s Transitions are currently not supported:

GlideImage(
  model = myUrl,
  contentDescription = getString(R.id.picture_of_cat),
  modifier = Modifier.padding(padding).clickable(onClick = onClick).fillParentMaxSize(),
) {
   it
    .thumbnail(
      requestManager
      .asDrawable()
      .load(item.uri)
      .signature(signature)
      .override(THUMBNAIL_DIMENSION)
    )
    .signature(signature)
}
Sizing

As with Glide’s View integration, Glide’s Compose integration will attempt to determine the size of your Composable and use that to load an appropriately sized image. That can only be done efficiently if you provide a Modifier that restricts the size of the Composable. If Glide determines that either the width or the height of the Composable is unbounded, it will use Target.SIZE_ORIGINAL, which can lead to excessive memory usage.

Whenever possible, make sure you either set a Modifier with a bounded size or provide an override() size to your Glide request. In addition to saving memory, loads from the disk cache will also be faster if your size is smaller.

Transitions and State Changes

Currently Glide’s Compose API does not explicitly support Glide’s Transition class. If you’d find this functionality useful, please file or a plus one an issue.

Similary while Glide’s placeholder, error and fallback request options work and can be provided via GlideImage’s RequestBuilderTransform, we do not currently support custom Composable functions for each of those states. Similarly please file or plus one an issue.

GlideLazyListPreloader

GlideLazyListPreloader uses Compose’s LazyListState to determine the direction the user is scrolling and preload images in the direction of scroll. Preloading, especially when used with relatively small image sizes in combination with Glide’s thumbnail API, can dramatically improve the UX of horiztonally or vertically scrolling UIs.

Using the preloader looks like this:

@Composable
fun DeviceMedia(mediaStoreData: List<MediaStoreData>) {
  val state = rememberLazyListState()
  LazyRow(state = state) {
    items(mediaStoreData) { mediaStoreItem ->
      // Uses GlideImage to display a MediaStoreData object
      MediaStoreView(mediaStoreItem, requestManager, Modifier.fillParentMaxSize())
    }
  }

   GlideLazyListPreloader(
    state = state,
    data = mediaStoreData,
    size = THUMBNAIL_SIZE,
    numberOfItemsToPreload = 15,
    fixedVisibleItemCount = 2,
  ) { item, requestBuilder ->
    requestBuilder.load(item.uri).signature(item.signature())
  }
}

@Composable
fun MediaStoreView(item: MediaStoreData, requestManager: RequestManager, modifier: Modifier) {
  val signature = item.signature()

  GlideImage(
    model = item.uri,
    contentDescription = item.displayName,
    modifier = modifier,
  ) {
    it
      // This thumbnail request exactly matches the request in GlideLazyListPreloader
      // so that the preloaded image can be used here and display more quickly than 
      // the primary request.
      .thumbnail(
        requestManager
          .asDrawable()
          .load(item.uri)
          .signature(signature)
          .override(THUMBNAIL_DIMENSION)
      )
      .signature(signature)
  }
}

state is a LazyListState that you can obtain using standard Compose APIs. data is the List of model objects that you’re displaying. size is the size of the image that you want to preload. This must exactly match at least one size provided to a Request or thumbnail Request used by GlideImage to display the item. numberOfItemsToPreload is total number of items you want to try to keep in memory ahead of the user’s position as they scroll. You may need some performance testing to find the right balance. Too high of a number and you may exceed the memory cache size. Too low and you won’t be able to keep up with scrolling. fixedVisibleItemCoutn is a guess of how many items you think will typically be visible on the screen at once.

Finally you can provide a PreloadRequestBuilderTransform, which will give you one object from the data list at a time to create a Glide request for. size will be applied for you automatically via Glide’s override API. So you only need to specify any additional components of the load that are required to make the request exactly match the load (or at least one thumbnail load) in the corresponding GlideImage.

As with much of Glide’s Compose API, there are likely to be some API changes to this class. In particular we’d like to simplify the process of specifying the request so that the request options are not duplicated, once in GlideLazyListPreloader and again in GlideImage.