https://kotlinlang.org logo
#compose-android
Title
# compose-android
s

Shivam Verma

11/19/2023, 6:14 AM
Hi all 👋 I am writing a new modifier to enable blur on composables based on a value from shared preferences. Could some modifier experts help me with a review. Any gotchas I should be aware of ? 🧵
Copy code
fun Modifier.blur() = composed {
    val context = LocalContext.current
    val bitmap = ImageBitmap.imageResource(R.drawable.blur)
    val tintColor = MaterialTheme.colors.onPrimary

    val isBlurEnabled = BlurPreferences(context)
        .getFlow()
        .collectAsStateWithLifecycle(initialValue = false)

    drawWithContent {
        if (!isBlurEnabled.value) {
            drawContent()
        } else {
            drawImage(
                image = bitmap,
                colorFilter = ColorFilter.tint(tintColor, BlendMode.SrcIn),
                dstSize = IntSize(size.width.toInt(), size.height.toInt())
            )
        }
    }
}
a

Atul Gupta

11/19/2023, 8:45 AM
I think there is new way to write the modifier using Node system and
composed
is not that performant
1
Also shouldn;t
Copy code
val isBlurEnabled = BlurPreferences(context)
        .getFlow()
        .collectAsStateWithLifecycle(initialValue = false)
be withing the remember scope. PS: I am also new to compose so might be giving some wrong advice
a

ascii

11/19/2023, 11:32 AM
Compose already has
Modifier.blur
, but that's only for A12+. I'm assuming yours is intended to be used as a fallback?
As a general rule, don't use
composed
unless you absolutely need to. It's attractive to have a black box modifier that "just works", but you're trading ease of use for performance. Both
LocalContext
and
MaterialTheme.colors
can be passed in from the calling composable, and you don't need to use the
imageResource
composable. The only thing that remains, then, is your blur pref flow. Where is it coming from? How often does the user change prefs that you need to react to changes? Is this modifier used in many places? If it absolutely must be a state, you may be able to hoist it up and re-use it throughout the app.
e

efemoney

11/19/2023, 11:54 AM
`Modifier.Node`s can read composition locals by extending
CompositionLocalConsumerModifierNode
, so no need to pass those in. They also have a
coroutineScope
so that you can launch coroutines that read your flow. I really hope your preferences flow is being produced on a background thread if not you’d be reading preferences on the main thread (which is where compose and there fore the coroutine scope runs as of today) If you extend DrawModifierNode (or something like that) you can do draw operations like you have so that there should be no need for
composed
modifier at all
👍 1
s

Shivam Verma

11/19/2023, 6:05 PM
> but you're trading ease of use for performance. @ascii Could you please elaborate on what exactly is bad for performance in this design. Or are you referring to the fact that
composed
is bad for creating modifiers. @ others, thanks for your tips about
Modifier.Node
, I'll try that out 🙏