how to make Compose Preview display image/vector resources loaded from web?
Asked Answered
J

1

8

I use Coil to load .svg resources from the web, for example:

@Composable
fun Widget(modifier: Modifier = Modifier) {
    AsyncImage(
        modifier = Modifier.fillMaxSize(),
        model = ImageRequest.Builder(LocalContext.current)
            .decoderFactory(SvgDecoder.Factory())
            .data("https://www.svgrepo.com/show/326168/circle-large.svg")
            .build(),
        contentDescription = null,
        contentScale = ContentScale.FillWidth
    )
}

It loads successfully when running on devices, but when running in Compose Preview, no resources is displayed. How to have Compose Preview load and display resources fetched from the Web?

Jupiter answered 6/7, 2022 at 7:56 Comment(2)
I don't think it's possible to access the network inside the preview. You need to run on the real device or use the placeholder parameter to display the local resource which will render inside the preview. Preview LimitationsHutson
Local resource is not shown as well, async loader is not loading async during the preview.Mckenna
E
0

Image downloaded from internet are usually displayed with AsyncImage, but the whole asynchronous thing won't work with @Preview. It's a pity because preview is the fastest way of debugging the user interface.

However, there is one feature of AsyncImage that does work even in preview - the placeholder painter.

This quick workaround doesn't add much complexity and can be adapted to different cases:

  1. Check the image source.
  2. If it is an integer, then let's assume it is a DrawableRes identifier, and use it to initialize the painter for the placeholder.
  3. If it is a string, then it is probably a URL, so use the normal placeholder.

I'm adding an example of it :

@Composable
@Preview
fun SomeImageFromInternetPreview() {
    SomeImageFromInternet(
        src = R.drawable.some_test_image.toString())
}

@Composable
fun SomeImageFromInternet(src: String, modifier: Modifier = Modifier) {
    // Things
    // ...
    // More things
    // ...

    AsyncImage(
        // Preview mode will not even try to use the source:
        model = src, 
        contentDescription = null,
        // But preview will fetch the placeholder:
        placeholder = placeHolderOrDrawableRes(src),
        error = painterResource(id = R.drawable.broken_image),
        modifier = Modifier
            .fillMaxWidth()
            .clip(MaterialTheme.shapes.small),
        contentScale = ContentScale.Fit)

    // Yet more things
    // ...
}

/**
 * Returns a [Painter] that is either the one specified
 * in the url, or a default value specified in the placeholder parameter.
 * @param urlOrDrawableRes If it contains an integer, then use it
 * as a [DrawableRes] instead of the placeholder.
 * @param placeholder The [DrawableRes] to use if the
 * url is not an integer.
 * @return An initialised [Painter].
 */
@Composable
fun placeHolderOrDrawableRes(
    urlOrDrawableRes: String,
    @DrawableRes placeholder: Int = R.drawable.loading_image): Painter {
    val drawableRes = urlOrDrawableRes.toIntOrNull()
    return when {
        drawableRes != null -> painterResource(id = drawableRes)
        else -> painterResource(id = placeholder)
    }
}

Enchilada answered 5/5 at 7:57 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.