Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider providing Jetpack Compose support #2203

Open
zach-klippenstein opened this issue Apr 20, 2021 · 7 comments
Open

Consider providing Jetpack Compose support #2203

zach-klippenstein opened this issue Apr 20, 2021 · 7 comments
Milestone

Comments

@zach-klippenstein
Copy link

The current state-of-the-art for image loading in Compose apps is the Accompanist library, which provides a Compose wrapper around Coil and Glide APIs. We still use Picasso heavily internally though, so it might make sense to add Compose support directly to Picasso, at least if it's not too much trouble.

Open questions

I don't have strong opinions on the API yet, but there are some questions that we need to answer:

  1. Should we even bother? Or just start using Coil instead? Coil is a great library, it provides (nearly all, and even some more) the features of Picasso, it's extensible, etc. There's also Kamel which even supports Kotlin multiplatform.
  2. Assuming we want to support it, should it ship as part of the core Picasso artifact or a separate, compose-specific support artifact?
  3. Some of the Picasso APIs expose drawables. Drawables are not support in Compose, so how should the Compose integration handle them?
    • The simplest thing to do would probably be just host an Android View inside the composition to render drawables.
    • Accompanist actually provides an internal-only implementation that even supports animated drawables. Not sure we want to maintain that much code.
  4. How much of the existing API should be reused? E.g. should we provide a Composable function that takes all the parameters that can be configured on a RequestCreator, or some way to just provide a RequestCreator to the composable? Since RequestCreator is a mutable, non-compose-stable type, we can't just pass a RequestCreator instance to a composable function.
  5. Should we provide error/placeholder overloads that take composable functions?
  6. Should the composable require an explicit Picasso instance to be passed in, or should we allow one to be provided through a CompositionLocal? This question might not matter depending on the answer to (4).

There are probably more questions to be asked, feel free to edit this description or add them in the comments.

@JakeWharton
Copy link
Collaborator

Nobody works on Picasso. If you want something in the next N years definitely use Coil. Or Glide. Or whatever. They're all fine.

Image loading is a terrible, horrible business to be in. It's been really nice not being in that business for the last few years. I don't see a reason to resume. I certainly have no intent to support it anymore. Picasso accomplished its goal of moving the ecosystem out of the painful image loaders of 2011/2012 to the fluent and extensible ones we know today. But it's filled with technical debt and the legacy of poor design (at least, in hindsight) and is currently very, very stuck between a major refactor and redesign with no end in sight.

@kroegerama
Copy link

Wouldn't it be best to leave a note in the readme, that Picasso is effectively in an abandoned/archived state? I just read about "Picasso 3" in a medium article and thought to myself "Why not look into it? Could be a great alternative to coil."

A quick google search returned close to zero results, so I'm glad I could finally find your statement here @JakeWharton...

@JakeWharton
Copy link
Collaborator

JakeWharton commented Apr 26, 2021

Sure. I mean nobody wants to write that because no one intends to let a library languish. But really Picasso is in a stable state. It is not abandoned or archived. Tens of thousands of apps rely on it in production and if something serious comes up it will be looked at. It's just not undergoing active development.

@zach-klippenstein
Copy link
Author

Maybe it would be more helpful to include a brief comparison of Picasso to Coil/Glide/etc, to help people looking starting green-field projects make informed choices. Not sure if we could do that without it becoming a maintenance burden.

@zach-klippenstein
Copy link
Author

I just realized Accompanist did have Picasso support at one point, but it was pulled for reasons: google/accompanist#253

@jrodbx jrodbx added this to the 3.0 milestone Jan 3, 2022
@arriolac
Copy link
Contributor

arriolac commented Jun 9, 2022

Looks like #2295 added support for Compose 🎉

@Gizcerbes
Copy link

Gizcerbes commented Mar 2, 2024

The coil does micro freezing, so I decided to try using Picasso. It works fast now.
My solution:

object PicassoImage {

sealed interface AsyncPicassoState {
	data class Result(val bitmap: Bitmap, val painter: Painter)

	class Loading(val state: Boolean) : AsyncPicassoState
	class Success(val result: Result) : AsyncPicassoState
	class Error(val message: String) : AsyncPicassoState
}


@Composable
fun AsyncPicasso(
	modifier: Modifier = Modifier,
	picasso: Picasso = Picasso.get(),
	path: String,
	contentDescription: String?,
	alignment: Alignment = Alignment.Center,
	contentScale: ContentScale = ContentScale.Fit,
	alpha: Float = DefaultAlpha,
	colorFilter: ColorFilter? = null,
	onState: ((AsyncPicassoState) -> Unit)? = null
) {
	val painter by remember {
		mutableStateOf<Painter>(object : Painter() {
			override val intrinsicSize: Size get() = Size(1f, 1f)
			override fun DrawScope.onDraw() {}
		}).apply {
			val state = this
			CoroutineScope(Dispatchers.IO).launch {
				launch(Dispatchers.Main) { onState?.let { it(AsyncPicassoState.Loading(true)) } }
				try {
					val bitmap = picasso.load(path).get()
					val imageBitmap = bitmap.asImageBitmap()
					val painter = BitmapPainter(imageBitmap)
					state.value = painter
					launch(Dispatchers.Main) { onState?.let { it(AsyncPicassoState.Success(AsyncPicassoState.Result(bitmap, painter))) } }
				} catch (e: Throwable) {
					launch(Dispatchers.Main) { onState?.let { it(AsyncPicassoState.Error(e.message ?: e.javaClass.name)) } }
				}
				launch(Dispatchers.Main) { onState?.let { it(AsyncPicassoState.Loading(false)) } }
			}
		}
	}
	Image(
		painter = painter,
		contentDescription = contentDescription,
		modifier = modifier,
		alignment = alignment,
		contentScale = contentScale,
		alpha = alpha,
		colorFilter = colorFilter
	)

}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants