In this post I will show you a simple example of using a Canvas element in Android Compose to create a dynamically created background for your UI.
The reason for creating this is partly due to me not being great with graphic design and I like to mess around with the Canvas to learn more while working on a project. This may not be very recommended by I figured its a small script with low overhead on performance vs loading and cropping an image.
This composable utilizes the path tool in canvas to draw. I will post two different elements I created but the principle is the same. One used the cubicTo method to draw wavy lines while the other draws straight lines.
The process is that on each load / recomposition, the paths are redrawn with random locations. This is what makes it dynamic, it will always change and never be the same. The downside is that there are some paths created that may not look too great, its a gamble.
Code
For wavy lines, use the below snippet
@Composable
fun DynamicHeader(
modifier: Modifier,
primaryColor: Color,
secondaryColor : Color,
blendColor : Color
) {
Canvas(modifier = modifier.graphicsLayer {
}) {
val list = mutableListOf(Path())
repeat(4) {
val path = Path()
val start = Random.nextFloat()
val top = Random.nextFloat() * size.height/2
val bottom = Random.nextFloat() * size.height/2
val flip = Random.nextInt(10)
val midone = Random.nextFloat()
val midtwo = Random.nextFloat()
path.moveTo(0f, 0f)
path.lineTo(x = 0f, y = size.height * start)
if(flip % 2 == 0){
path.cubicTo(size.width * midone, size.height * start - top, size.width * midtwo, size.height * start + bottom, size.width, size.height * start)
} else {
path.cubicTo(size.width * midone, size.height * start + top, size.width * midtwo, size.height * start - bottom, size.width, size.height * start)
}
path.lineTo(size.width, 0f)
path.close()
list.add(path)
}
list.forEach { path ->
drawPath(
path = path,
style = Fill,
brush = Brush.verticalGradient(
0.0f to primaryColor.copy(alpha = Random.nextFloat()),
0.5f to secondaryColor.copy(alpha = Random.nextFloat()),
1.0f to blendColor.copy(alpha = Random.nextFloat())
)
)
}
}
}
For straight lines, use the below snippet
@Composable
fun DynamicHeader(
modifier: Modifier,
primaryColor: Color,
secondaryColor : Color,
blendColor : Color
) {
Canvas(modifier = modifier.graphicsLayer {
}) {
val list = mutableListOf(Path())
repeat(4) {
val path = Path()
path.moveTo(x = 0f, y = 0f)
path.lineTo(x = 0f, y = size.height * Random.nextFloat())
path.lineTo(x = size.width, y = size.height * Random.nextFloat())
path.lineTo(x = size.width, y = 0f)
path.close()
list.add(path)
}
list.forEach { path ->
drawPath(
path = path,
style = Fill,
brush = Brush.verticalGradient(
0.0f to primaryColor.copy(alpha = Random.nextFloat()),
0.5f to secondaryColor.copy(alpha = Random.nextFloat()),
1.0f to blendColor.copy(alpha = Random.nextFloat())
)
)
}
}
}
Simply choose the one you want and call the element like this below. I have a button that will update a Boolean to recompose the elements.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
var test by remember { mutableStateOf(true) }
SampleDynamicHeaderTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
if(test) {
DynamicHeader(
modifier = Modifier.fillMaxSize(),
MaterialTheme.colorScheme.primary,
MaterialTheme.colorScheme.tertiary,
Color.Gray
)
} else {
DynamicHeader(
modifier = Modifier.fillMaxSize(),
MaterialTheme.colorScheme.primary,
MaterialTheme.colorScheme.tertiary,
Color.Gray
)
}
Box(modifier = Modifier.fillMaxSize().padding(innerPadding), contentAlignment = Alignment.Center){
Button(onClick = { test = !test }) {
Text( text = "Update")
}
}
}
}
}
}
}
Hope this is useful.