Usage
Haze is implemented through two Compose Modifiers: Modifier.haze and Modifier.hazeChild.
The most basic usage would be something like:
val hazeState = remember { HazeState() }
Box {
LazyColumn(
modifier = Modifier
.fillMaxSize()
// Pass it the HazeState we stored above
.haze(state = hazeState)
) {
// todo
}
LargeTopAppBar(
// Need to make app bar transparent to see the content behind
colors = TopAppBarDefaults.largeTopAppBarColors(Color.Transparent),
modifier = Modifier
// We use hazeChild on anything where we want the background
// blurred.
.hazeChild(state = hazeState)
.fillMaxWidth(),
)
}
StylingΒΆ
Haze has support for customizing the resulting effect, which is performed via the HazeStyle class, or the lambda block provided to hazeChild
.
Styles can be provided in a number of different ways:
- LocalHazeStyle composition local.
- The style parameter on Modifier.hazeChild.
- By setting the relevant property in the optional HazeChildScope lambda
block
, passed into Modifier.hazeChild.
HazeChildScopeΒΆ
We now have a parameter on Modifier.hazeChild
which allow you to provide a lambda block, for controlling all of Haze's styling parameters. It is similar to concept to Modifier.graphicsLayer { ... }
.
It's useful for when you need to update styling parameters, using values derived from other state. Here's an example which fades the effect as the user scrolls:
FooAppBar(
...
modifier = Modifier
.hazeChild(state = hazeState) {
alpha = if (listState.firstVisibleItemIndex == 0) {
listState.layoutInfo.visibleItemsInfo.first().let {
(it.offset / it.size.height.toFloat()).absoluteValue
}
} else {
alpha = 1f
}
},
)
Styling resolutionΒΆ
As we a few different ways to set styling properties, it's important to know how the final values are resolved.
Each styling property (such as blurRadius
) is resolved seperately, and the order of precedence for each property is as follows, in order:
- Value set in HazeChildScope, if specified.
- Value set in style provided to hazeChild (or HazeChildScope.style), if specified.
- Value set in the LocalHazeStyle composition local.
Styling propertiesΒΆ
Blur RadiusΒΆ
The blur radius controls how strong the blur effect is. This defaults to 20.dp
but can be customized as needed. Larger values might be needed to keep foreground control (such as text) legible and accessible.
TintΒΆ
A tint effect is applied, primarily to maintain contrast and legibility. By default we use the provided background color at 70% opacity. You may wish to use a different color or opacity. You provide multiple tints, which will be applied in sequence.
NoiseΒΆ
Some visual noise is applied, to provide some tactility. This is completely optional, and defaults to a value of 0.15f
(15% strength). You can disable this by providing 0f
.
Progressive (aka gradient) blursΒΆ
Progressive blurs allow you to provide a visual effect where the blur radius is varied over a dimension. You may have seen this effect used on iOS.
Progressive blurs can be enabled by setting the progressive
property on HazeChildScope. The API is very similar to the Brush gradient APIs, so it should feel familiar.
LargeTopAppBar(
// ...
modifier = Modifier.hazeChild(hazeState) {
progressive = HazeProgressive.verticalGradient(startIntensity = 1f, endIntensity = 0f)
}
)
Performance of Progressive
Please be aware that using progressive blurring does come with a performance cost. Please see the Performance page for up-to-date benchmarks.
As a quick summary: on Android SDK 33+ and other platforms, the cost is about 25% more than non-progressive. On Android SDK 32 it is about 2x. If performance is critical, you may wish to look at the masking functionality below.
MaskingΒΆ
You can provide any Brush
, which will be used as a mask when the final effect is drawn.
LargeTopAppBar(
// ...
modifier = Modifier.hazeChild(hazeState) {
mask = Brush.verticalGradient(...)
}
)
Mask vs Progressive
When you provide a gradient brush as a mask, the effect is visually similar to a gradient blur. The difference is that the effect is faded through opacity only, and may not feel as refined. However, it is much faster than progressive blurring, having a negligible cost.