Flutter: How to pause and restart a `Stream` listener?

1,443

You can use

controller.scannedDataStream.first

that stops listening to other events from the stream.

Another solution would be to set an internal state property like below:

bool QrBeingProcessed = false;

and when you scan the first qr set it to true, till you're done.

Share:
1,443
PeakGen
Author by

PeakGen

CTO

Updated on December 28, 2022

Comments

  • PeakGen
    PeakGen over 1 year

    I am developing a flutter app to read QR Codes. I am using qr_code_scanner: ^0.3.5 library. Below is my code.

    import 'dart:io';
    
    import 'package:flutter/material.dart';
    import 'package:google_fonts/google_fonts.dart';
    import 'package:qr_code_scanner/qr_code_scanner.dart';
    
    class ScanQRCodeScreen extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            backgroundColor: Colors.white,
            appBar: AppBar(
              title: Text("Scan QR Code"),
            ),
            body: _ScanQRCodeUI());
      }
    }
    
    class _ScanQRCodeUI extends StatefulWidget {
      @override
      State<StatefulWidget> createState() {
        return _ScanQRCodeUIState();
      }
    }
    
    class _ScanQRCodeUIState extends State<_ScanQRCodeUI> {
      final GlobalKey qrKey = GlobalKey(debugLabel: 'QR');
      Barcode result;
      QRViewController controller;
    
      // In order to get hot reload to work we need to pause the camera if the platform
      // is android, or resume the camera if the platform is iOS.
      @override
      void reassemble() {
        super.reassemble();
        if (Platform.isAndroid) {
          controller.pauseCamera();
        } else if (Platform.isIOS) {
          controller.resumeCamera();
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return Column(
          children: [
            Expanded(flex: 4, child: _buildQrView(context)),
            Expanded(flex: 1, child: _dataDisplayUI())
          ],
        );
      }
    
      Widget _buildQrView(BuildContext context) {
        // For this example we check how width or tall the device is and change the scanArea and overlay accordingly.
        var scanArea = (MediaQuery.of(context).size.width < 400 ||
                MediaQuery.of(context).size.height < 400)
            ? 200.0
            : 400.0;
        // To ensure the Scanner view is properly sizes after rotation
        // we need to listen for Flutter SizeChanged notification and update controller
        return QRView(
          key: qrKey,
          onQRViewCreated: _onQRViewCreated,
          overlay: QrScannerOverlayShape(
              borderColor: Colors.red,
              borderRadius: 10,
              borderLength: 30,
              borderWidth: 10,
              cutOutSize: scanArea),
        );
      }
    
      void _onQRViewCreated(QRViewController controller) {
        setState(() {
          this.controller = controller;
        });
    
        controller.scannedDataStream.listen((scanData) async {
          print("Hello0");
          setState(() {
            result = scanData;
            print(result.code);
          });
    
          // await controller.pauseCamera();
        });
      }
    
      Widget _dataDisplayUI() {
        const yellowColor = const Color(0xffEDE132);
    
        return Column(
          children: [
            Row(
              children: [
                Expanded(
                    flex: 7,
                    child: Container(
                      margin:
                          EdgeInsets.only(top: 30, bottom: 30, left: 10, right: 10),
                      child:
                          Text("You have added 12 products. Click here to publish.",
                              style: GoogleFonts.poppins(
                                  textStyle: TextStyle(
                                color: Colors.black,
                                fontWeight: FontWeight.normal,
                                fontSize: 14,
                              ))),
                    )),
                Expanded(
                    flex: 3,
                    child: Container(
                      width: 60,
                      height: 60,
                      child: Center(
                          child: Text("12",
                              style: GoogleFonts.poppins(
                                  textStyle: TextStyle(
                                color: Colors.black,
                                fontWeight: FontWeight.bold,
                                fontSize: 21,
                              )))),
                      decoration:
                          BoxDecoration(shape: BoxShape.circle, color: yellowColor),
                    ))
              ],
            )
          ],
        );
      }
    
      @override
      void dispose() {
        controller?.dispose();
        super.dispose();
      }
    }
    

    I am using this app to scan products one by one, just like how the cashier does in a super marker using a barcode scanner.

    The issue is, this scanner is listening to a stream and it is keep on running. Pay your attention to the _onQRViewCreated method. As a result, the same QR is being read multiple times before we even move the camera to the next QR code.

    How can I make sure there is a delay between 2 scans? For an example, when I scan for a QR Code, I have to wait for another 2 seconds to scan the next QR.

    If my idea of creating a delay between 2 scans is wrong, I am open for other ideas as well.