What You Will Build
A QR code display and scanner interface that draws a QR code pattern using Compose Canvas, complete with a scan button and instructional text. This teaches you pixel-level drawing with Canvas, which is essential for custom charts, barcode renderers, and game graphics.
Why This Pattern Matters
Canvas drawing in Compose is the equivalent of custom View.onDraw() in the old View system. Learning Canvas lets you build anything that standard composables cannot: custom progress indicators, signature pads, chart libraries, and data visualization components.
Step 1: Define the QR Pattern Matrix
A QR code is essentially a grid of black and white cells. We represent it as a 2D list of 0s and 1s:
val pattern = listOf(
listOf(1,1,1,0,1,0,1,1,1,0),
listOf(1,0,1,0,0,1,1,0,1,0),
listOf(1,1,1,0,1,1,1,1,1,0),
listOf(0,0,0,0,1,0,0,0,0,0),
listOf(1,0,1,1,0,1,0,1,1,1),
listOf(0,1,0,0,1,0,1,0,0,1),
listOf(1,1,1,0,0,0,1,1,1,0),
listOf(1,0,1,0,1,1,1,0,1,0),
listOf(1,1,1,0,1,0,1,1,1,1),
listOf(0,0,0,0,0,1,0,0,0,0)
)
Step 2: Draw the QR Code with Canvas
Card(
Modifier.size(250.dp),
shape = RoundedCornerShape(20.dp)
) {
Box(
Modifier.fillMaxSize().background(Color.White),
contentAlignment = Alignment.Center
) {
Canvas(Modifier.size(180.dp)) {
val cellSize = size.width / 10
pattern.forEachIndexed { y, row ->
row.forEachIndexed { x, v ->
if (v == 1) {
drawRect(
Color.Black,
topLeft = Offset(x * cellSize, y * cellSize),
size = Size(cellSize, cellSize)
)
}
}
}
}
}
}
Step 3: Add Scanner Controls
Column(
modifier = Modifier.fillMaxSize().padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text("QR Code Scanner",
style = MaterialTheme.typography.headlineSmall,
fontWeight = FontWeight.Bold)
Spacer(Modifier.height(32.dp))
// QR Canvas card goes here
Spacer(Modifier.height(16.dp))
Text("Scan to connect",
color = MaterialTheme.colorScheme.onSurfaceVariant)
Spacer(Modifier.height(24.dp))
Button(onClick = {}, modifier = Modifier.fillMaxWidth()) {
Text("Scan QR Code")
}
}
Tips and Pitfalls
- Canvas size vs drawing coordinates: Use
size.width / gridSizeto compute cell dimensions relative to the Canvas, not hardcoded pixel values. - drawRect takes Offset and Size, not four corner coordinates. Use
Offset(x * cell, y * cell)andSize(cell, cell). - For actual QR scanning: Integrate Google ML Kit or ZXing for camera-based scanning. The Canvas approach here is for display only.
- Add rounded corners to cells with
drawRoundRectandCornerRadiusfor a modern QR style.
Min SDK: 21 | Compose BOM: 2024.01.00+