# How to read a QR Code or barcode

> Scan a QR/barcode with the camera using BarcodePicker, or decode an Image with CameraBarcode.

Source: https://crossfly.dev/docs/cookbook/device/ler-codigo-de-barras — CrossFly documentation. Full docs as one file: https://crossfly.dev/llms-full.txt

# How to read a QR Code or barcode

**Purpose.** Open the camera and read a **QR Code** or **barcode** live with
`BarcodePicker`, or decode a code from an already-captured `Image` with `CameraBarcode`.

## APIs used
- `BarcodePicker(String title, BarcodeFormat... formats)` — **abstract** live-reading window; you **extend** it and implement `onBarcode(BarcodeFormat, String)`. Empty `formats` = accepts all.
- `abstract void onBarcode(BarcodeFormat format, String code)` — called for each newly decoded code that passes the format filter.
- `BarcodePicker.formats1D` / `formats2D` (static arrays) — ready-made format sets; `formats2D` contains `QR_CODE`.
- `BarcodePicker.beep` / `BarcodePicker.vibrate` (static `boolean`) — feedback on decoding (default `true`).
- `String getAnswer()` — last decoded code, or `null` if cancelled.
- `CameraBarcode.readBarcode(Image img)` → `com.google.zxing.Result` (or `null`) — decodes from a static image. **NOT thread-safe.**

> **Important divergence.** `BarcodePicker` **is `abstract`** and uses the `onBarcode(...)` method, not a
> `RunnableWith`-type callback. Instantiate a subclass (anonymous or named). The picker already emits
> `beep()`/`vibrate(20)` on decoding and **suppresses repeated codes** (only calls `onBarcode` when the
> text changes). It does **not** close by itself when it reads — call `hide()` inside `onBarcode` if you want to
> stop at the first read. Tapping the "X" (bottom-right corner) cancels.

## Complete example (copy, paste, and run)

```java
package com.exemplo;

import com.google.zxing.*;                  // BarcodeFormat
import dev.crossfly.ui.*;                   // Window, Component, Button, Label
import dev.crossfly.ui.layout.*;            // VerticalLayout
import dev.crossfly.ui.picker.*;            // BarcodePicker

/** Runnable main screen. Point the launcher (classname) and run: gradlew :lwjgl3:run */
public class MainWindow extends Window
{
   @Override
   public void onShow() throws Exception
   {
      super.onShow();
      setLayout(new VerticalLayout());

      Label resultado = new Label("");
      add(LAY_AUTO, resultado);

      Button acao = new Button("Ler QR Code");
      acao.onAction(c ->
      {
         // LIVE reading — QR Code only; closes on the first read
         BarcodePicker picker = new BarcodePicker("Aponte para o QR", BarcodePicker.formats2D)
         {
            protected void onBarcode(BarcodeFormat format, String code)
            {
               // Called OUTSIDE the paint thread → return to the UI with onUI(...)
               Component.onUI(() ->
               {
                  resultado.setText("Lido: " + code);
                  hide();            // closes the scanner on the first read (optional)
               });
            }
         };
         picker.show();
      });
      add(LAY_AUTO, acao);
   }
}
```

> The snippet below shows **only the core**. To run it, place it in the `MainWindow` skeleton above.

## Minimal snippet

```java
// verified at source: BarcodePicker.java (constructor, onBarcode, formats2D, getAnswer); CameraBarcode.java (readBarcode)
import com.google.zxing.*;                 // BarcodeFormat, Result
import dev.crossfly.ui.picker.*;           // BarcodePicker
import dev.crossfly.ui.*;                  // Component (onUI)

// 1) LIVE reading — QR Code only; closes on the first read
BarcodePicker picker = new BarcodePicker("Aponte para o QR", BarcodePicker.formats2D) {
   protected void onBarcode(BarcodeFormat format, String code) {
      // Called OUTSIDE the paint thread → return to the UI with onUI(...)
      Component.onUI(() -> {
         processar(code);   // e.g.: fill a field, navigate, etc.
         hide();            // closes the scanner on the first read (optional)
      });
   }
};
picker.show();             // leave the formats empty in the constructor to accept all formats

// 2) Decode from a static Image (one camera/thread at a time — not thread-safe)
Result r = CameraBarcode.readBarcode(minhaImagem);
if (r != null) {
   String texto = r.getText();
   BarcodeFormat fmt = r.getBarcodeFormat();
}
```

## Go deeper
- Camera guide: [Device camera](/docs/data-and-services/device-camera) · Widget: [BarcodePicker](/docs/ui/widgets/barcodepicker)
- Sibling recipe: [tirar-foto.md](/docs/cookbook/device/tirar-foto)
