plugin_scanner_qr 0.3.0 copy "plugin_scanner_qr: ^0.3.0" to clipboard
plugin_scanner_qr: ^0.3.0 copied to clipboard

Plugin Flutter para leitura de QR Code no Android usando ML Kit e CameraX. Oferece leitura única em tela cheia e leitura contínua com preview embutido na árvore de widgets (PlatformView), com debounce [...]

plugin_scanner_qr #

Plugin Flutter para leitura de QR Code no Android usando ML Kit e CameraX.

Dois modos:

Modo Como aparece Use quando
Leitura única — scanQRCode() Activity de tela cheia; lê um código e volta Ler um QR pontualmente
Leitura contínua — QrScannerPreview PlatformView embutida na árvore de widgets Leitura o tempo todo, com UI Flutter por cima (overlay, diálogos, botões)

Plataformas: Android apenas.


Instalação #

dependencies:
  plugin_scanner_qr: ^0.3.0

Permissões (Android) #

O plugin já declara CAMERA e mescla no manifest do app — não é preciso repetir.

O plugin não solicita a permissão. Isso é deliberado: se o app também usa permission_handler, dois solicitantes concorrentes causam IllegalStateException: Can request only one set of permissions at a time. Solicite no app e use hasCameraPermission() para consultar:

if (!await plugin.hasCameraPermission()) {
  await Permission.camera.request();   // permission_handler
}

Se a permissão faltar, QrScannerPreview não fica preta em silêncio: emite um QrScannerError com isPermissionDenied == true e renderiza o permissionDeniedBuilder.


Uso rápido #

Leitura única #

import 'package:plugin_scanner_qr/plugin_scanner_qr.dart';

final plugin = PluginScannerQr();

final result = await plugin.scanQRCode();
print(result); // conteúdo do QR ou null se cancelado

Leitura contínua com preview embutido (recomendado) #

O preview é um widget normal, então dá para desenhar qualquer coisa por cima num Stack — inclusive showDialog.

class _MinhaTelaState extends State<MinhaTela> {
  late final _controller = QrScannerController(
    duplicateDebounce: const Duration(milliseconds: 1500),
    initialFacing: QrCameraFacing.front,
  );

  @override
  void initState() {
    super.initState();
    _controller.scans.listen(_onLeitura);
    _controller.errors.listen((e) => print('erro: ${e.code}'));
  }

  Future<void> _onLeitura(QrScanResult r) async {
    await _controller.pauseScanning();      // mantém a câmera aberta
    await _processar(r.value);
    await _controller.resumeScanning();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => Stack(
    children: [
      QrScannerPreview(controller: _controller),
      const Positioned(bottom: 24, left: 0, right: 0,
        child: Text('Aproxime o crachá', textAlign: TextAlign.center)),
    ],
  );
}

stop/start vs pauseScanning/resumeScanning

  • pauseScanning() / resumeScanning() — a câmera continua aberta, só a detecção para. É quase instantâneo. Use no padrão "leu → mostra diálogo → retoma".
  • stop() / start() — fecha e reabre o dispositivo de câmera. Use ao navegar para outra tela ou quando o app vai para background.

Migrando do ai_barcode

ai_barcode aqui
ScannerController(scannerResult: ...) QrScannerController + scans.listen(...)
scannerViewCreated: QrScannerPreview(onViewCreated: ...)
startCamera() / startCameraFront() start(facing: QrCameraFacing.back / .front)
startCameraPreview() resumeScanning()
stopCamera() stop()
stopCameraPreview() pauseScanning()
isStartCamera isRunning
PlatformAiBarcodeScannerWidget(...) QrScannerPreview(controller: ...)

Comandos chamados antes de a PlatformView existir são guardados e aplicados no attach — não é preciso esperar por isAttached.

Leitura contínua em baixo nível (controle total) #

// 1. Assine o stream ANTES de abrir a câmera
plugin.onBarcodeScanned.listen((result) {
  print(result.value);
});

// 2. Abra a câmera
await plugin.startContinuousScan(
  ContinuousScanOptions(
    duplicateDebounce: Duration(milliseconds: 1500),
    cooldown: Duration(seconds: 2),
  ),
);

// Controle manual:
await plugin.pauseScanning();
await plugin.resumeScanning();
await plugin.stopContinuousScan();

Removido na 0.2.0. startContinuousScan agora responde com o erro UNSUPPORTED. O modo contínuo abria uma Activity de tela cheia que cobria toda a UI Flutter, deixando invisíveis overlays e showDialog. Use QrScannerPreview + QrScannerController. Os métodos globais pauseScanning/resumeScanning/toggleFlash/switchCamera/stopContinuousScan continuam funcionando, mas delegam para a última QrScannerPreview criada — prefira os métodos por instância do controller.


QrScannerController #

Membro Descrição
scans Stream<QrScanResult> com as leituras
errors Stream<QrScannerError> (permissão, falha ao abrir a câmera)
start({facing}) Abre a câmera. Idempotente. false se faltar permissão
stop() Fecha o dispositivo de câmera
pauseScanning() Desliga só a detecção, câmera segue aberta
resumeScanning() Retoma a detecção (reabre a câmera se necessário)
setFacing() / switchCamera() Frontal ↔ traseira, com o preview no ar
setTorch(bool) Lanterna
resetDebounce() Permite reler imediatamente o mesmo código
setFocusMode(mode, {distance, interval}) Estratégia de foco (veja Calibração abaixo)
setExposureCompensation(int) Compensação de exposição, em índices
setLinearZoom(double) Zoom linear 0.0–1.0
getCameraCapabilities() O que o hardware suporta (foco manual, exposição, zoom…)
isAttached / isRunning / isScanning Estado

Calibração de câmera (kiosk/totem) #

Novidades da 0.3.0, pensadas para leitura em ponto fixo (ex.: relógio de ponto) com muita luz ambiente e aparelhos de autofoco lento (ex.: Galaxy A03s). Tudo é opcional; sem configurar nada, o comportamento é o padrão da câmera.

final controller = QrScannerController(
  // Resolução do frame analisado pelo ML Kit. Padrão: hd (1280x720).
  // O default antigo do CameraX (sd, 640x480) rendia poucos pixels por módulo
  // do QR — qualquer desfoque leve matava a leitura.
  analysisResolution: QrAnalysisResolution.hd,

  // continuous (padrão) | periodic | fixed
  focusMode: QrFocusMode.fixed,
  focusDistance: 4.0, // dioptrias = 1/metros → 4.0 ≈ 25 cm
);
Modo de foco O que faz Use quando
continuous Autofoco contínuo do CameraX (padrão) Uso geral
periodic Dispara foco no centro a cada autoFocusInterval (padrão 3 s) AF por contraste que "caça" e trava no plano errado
fixed Desliga o AF e trava a lente em focusDistance dioptrias Totem: distância de leitura sempre igual — o foco nunca mais caça

Referência rápida de dioptrias: 15 cm ≈ 6.7 · 25 cm ≈ 4.0 · 50 cm ≈ 2.0.

Se o aparelho não expõe foco manual, fixed cai silenciosamente em continuous (nada quebra) e setFocusMode() retorna false. Descubra o que o aparelho aceita com:

final caps = await controller.getCameraCapabilities();
print(caps); // focoManual, range de exposição, zoom, resolução negociada…
if (caps!.supportsManualFocus) {
  await controller.setFocusMode(QrFocusMode.fixed, distance: 4.0);
}

Complementos:

  • setExposureCompensation(-4) — índices negativos escurecem; útil contra reflexo de QR exibido em tela de celular. Range e passo em EV vêm nas capabilities; o valor é ajustado ao range automaticamente.
  • setLinearZoom(0.15) — QR ocupa mais pixels na distância típica do totem.
  • A resolução da análise é fixada na criação do preview (limitação do CameraX); para trocar, recrie o widget. O CameraX negocia a resolução suportada mais próxima — a efetiva aparece em caps.analysisWidth/Height.

O example traz um painel "Calibração da câmera" com tudo isso em sliders, para calibrar no aparelho real sem recompilar.

QrScannerPreview #

Parâmetro Padrão Descrição
controller — Um controller por preview
hybridComposition false false = TLHC (recomendado). Ligue só se houver artefatos
autoPauseOnBackground true Fecha a câmera em background e reabre ao voltar
onViewCreated — Equivale ao scannerViewCreated do ai_barcode
permissionDeniedBuilder — UI quando falta permissão de câmera
placeholder — Exibido enquanto a câmera não abriu

ContinuousScanOptions #

Parâmetro Tipo Padrão Descrição
duplicateDebounce Duration 1500 ms Suprime reemissões do mesmo valor no nativo
cooldown Duration Duration.zero Pausa automática após cada leitura
useFrontCamera bool false Câmera frontal
orientation String 'portrait' 'portrait' ou 'landscape'
enableTorch bool false Lanterna
showNativeFeedback bool false Toasts nativos de debug

ContinuousQrScanner #

Membro Descrição
start() Solicita permissão e abre a câmera
scans Stream<QrScanResult> com os resultados
pause() Pausa temporariamente a detecção
resume() Retoma após pausa
toggleFlash() Liga/desliga a lanterna
switchCamera() Alterna entre câmera frontal e traseira
dispose() Encerra o scanner e fecha a câmera

Leitura única com opções avançadas #

final result = await plugin.scanQRCodeWithOptions(
  useFrontCamera: false,
  orientation: 'portrait',
  enableTorch: false,
  enableAutoFocus: true,
  zoomLevel: 0.0,
);

Utilitários #

await plugin.isCameraAvailable();       // bool
await plugin.isFrontCameraAvailable();  // bool
await plugin.hasCameraPermission();     // bool — consulta pura, não solicita

Dependências nativas (Android) #

  • CameraX (androidx.camera:camera-* 1.3.4)
  • ML Kit Barcode Scanning (com.google.mlkit:barcode-scanning 17.2.0)

minSdk 21, compileSdk 35, Java/Kotlin jvmTarget 11.


Limitações conhecidas #

  • Apenas Android. iOS, Web e Desktop não são suportados.
  • Em scanQRCodeWithOptions, apenas useFrontCamera, orientation e enableTorch chegam ao nativo. Os parâmetros enableAutoFocus, zoomLevel, enableZoom, brightness, contrast e exposureCompensation são aceitos e ignorados — ainda não foram conectados à API do CameraX.
  • QrScanResult.operator == compara apenas value, ignorando scannedAt.
  • O preview usa PreviewView.ImplementationMode.COMPATIBLE (TextureView). PERFORMANCE (SurfaceView) não é usável dentro de uma PlatformView.
  • Uma câmera física por vez: duas QrScannerPreview simultâneas fazem a segunda emitir CAMERA_BIND_FAILED.

Estrutura do projeto #

lib/
├── plugin_scanner_qr.dart                    # Fachada pública
├── plugin_scanner_qr_platform_interface.dart # Contrato da plataforma
├── plugin_scanner_qr_method_channel.dart     # Implementação via channels
└── src/
    ├── qr_scanner_controller.dart             # Controller do preview embutido
    ├── qr_scanner_preview.dart                # Widget do preview (PlatformView)
    ├── qr_scan_result.dart                    # Modelo de resultado
    ├── continuous_scan_options.dart           # Opções (legado)
    └── continuous_qr_scanner.dart             # Helper de alto nível (legado)
android/
└── src/main/kotlin/com/example/plugin_scanner_qr/
    ├── PluginScannerQrPlugin.kt               # Registro, method channel, permissão
    ├── QrScannerCore.kt                       # CameraX + ML Kit, debounce/cooldown
    ├── QrPreviewView.kt                       # PlatformView + canais por instância
    ├── QrPreviewViewFactory.kt                # PlatformViewFactory
    ├── PreviewLifecycleOwner.kt               # LifecycleOwner por preview
    ├── PreviewRegistry.kt                     # Views vivas + espelho do host
    └── MlKitScannerActivity.kt                # Tela cheia, leitura única
0
likes
0
points
11
downloads

Publisher

unverified uploader

Weekly Downloads

Plugin Flutter para leitura de QR Code no Android usando ML Kit e CameraX. Oferece leitura única em tela cheia e leitura contínua com preview embutido na árvore de widgets (PlatformView), com debounce e cooldown nativos.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on plugin_scanner_qr

Packages that implement plugin_scanner_qr