How to implement Linear Gradient with any angle in jetpack compose

Mukhtar Bimurat
3 min readMar 22, 2022

--

Let’s say, we want to draw a linear gradient with a 20-degree angle as a background. In CSS it is easy to do using:

background: linear-gradient(70deg, #3690EA, #94B3FF);

In Android, it wasn’t. I guess even impossible without using SVG, canvas, or custom view. But with jetpack compose it becomes effortless. Currently, LinearGradient doesn’t offer angle control and we will add it using Brush.

If you don’t know what gradient line is and how it’s is drawn under the hood, I suggest reading this article. Also, it has a cool playground.

So, in jetpack compose LinearGradient doesn’t offer angle control. But we can implement abstract ShaderBrush where we can create Shader lazily when rectangle size is available. And that is the key advantage - we can do any size-related calculation (stop points) then use it while creating a shader.

package androidx.compose.ui.graphics/**
* Brush implementation that wraps and applies a the provided shader to a [Paint]
* The shader can be lazily created based on a given size, or provided directly as a parameter
*/
@Immutable
abstract class ShaderBrush() : Brush() {

abstract fun createShader(size: Size): Shader
...
}

After we use the brush for any background, canvas, etc…

package androidx.compose.foundationfun Modifier.background(
brush: Brush,
shape: Shape = RectangleShape,
/*@FloatRange(from = 0.0, to = 1.0)*/
alpha: Float = 1.0f
)

For gradients, we will leverage already existing LinearGradientShader by providing from, to parameters, as coordinates of gradient start (S) and end (E) points .

fun LinearGradientShader(
from: Offset,
to: Offset,
colors: List<Color>,
colorStops: List<Float>? = null,
tileMode: TileMode = TileMode.Clamp
)

To calculate coordinates of S(Sx, Sy), E(Ex, Ey) points we, I’ve drawn a simple figure using geogebra:

ABCD - rectangle
SE - gradient line
O - center of the rectangle (or center of gradient line)
∠EOG - angle of the gradient

Note: As gradient angle, I used ∠EOG - cartesian angle system rather than ∠EOH - CSS gradient angle used in the playground. As ∠EOG = 90 -∠EOH we can also use the following code for CSS angle input.

Using the following math:

Ex = Ox + OE * cos(∠EOG)
Ey = Oy - OE * cos(∠EOG)
Sx = Ox - OE * cos(∠EOG)
Sy = Oy + OE * cos(∠EOG)
OE = OB * abs( cos(∠EOG - ∠BOG) )∠BOG = acos(OG / OB) = acos(DC / DB) # arc cosOB = sqrt(BC^2 + DC^2) / 2

the final code, with slight modifications to handle different quadrants, looks like this:

Finally, how gradient looks on different angles (with custom stops):

You can find the entire project on GitHub, feel free to play with the repo and let me know what you think :).

Thank you!

--

--