plugin_scanner_qr 0.2.0
plugin_scanner_qr: ^0.2.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.2.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.
startContinuousScanagora responde com o erroUNSUPPORTED. O modo contínuo abria uma Activity de tela cheia que cobria toda a UI Flutter, deixando invisíveis overlays eshowDialog. UseQrScannerPreview+QrScannerController. Os métodos globaispauseScanning/resumeScanning/toggleFlash/switchCamera/stopContinuousScancontinuam funcionando, mas delegam para a últimaQrScannerPreviewcriada — 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 |
isAttached / isRunning / isScanning |
Estado |
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-scanning17.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, apenasuseFrontCamera,orientationeenableTorchchegam ao nativo. Os parâmetrosenableAutoFocus,zoomLevel,enableZoom,brightness,contrasteexposureCompensationsão aceitos e ignorados — ainda não foram conectados à API do CameraX. QrScanResult.operator ==compara apenasvalue, ignorandoscannedAt.- 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
QrScannerPreviewsimultâneas fazem a segunda emitirCAMERA_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