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 / gridSize to 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) and Size(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 drawRoundRect and CornerRadius for a modern QR style.

Min SDK: 21 | Compose BOM: 2024.01.00+

Get New Tutorials by Email

No spam. Just clear, practical breakdowns you can apply right away.

Enjoy this tutorial?

Get new practical tech tutorials in your inbox.