Flutter:如何暂停和重新启动“Stream”侦听器?

Posted

技术标签:

【中文标题】Flutter:如何暂停和重新启动“Stream”侦听器?【英文标题】:Flutter: How to pause and restart a `Stream` listener? 【发布时间】:2021-06-08 10:55:58 【问题描述】:

我正在开发一个 Flutter 应用程序来读取 QR 码。我正在使用qr_code_scanner: ^0.3.5 库。下面是我的代码。

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();
  

我正在使用这个应用程序逐个扫描产品,就像收银员使用条形码扫描仪在超级标记中所做的那样。

问题是,这个扫描器正在监听 stream 并且它一直在运行。请注意_onQRViewCreated 方法。因此,在我们将相机移至下一个二维码之前,同一个二维码已被多次读取。

如何确保两次扫描之间存在延迟?例如,当我扫描一个二维码时,我必须再等待 2 秒才能扫描下一个二维码。

如果我在两次扫描之间创建延迟的想法是错误的,我也愿意接受其他想法。

【问题讨论】:

【参考方案1】:

你可以使用

controller.scannedDataStream.first

停止监听流中的其他事件。

另一种解决方案是设置如下内部状态属性:

bool QrBeingProcessed = false;

当您扫描第一个 qr 时,将其设置为 true,直到您完成。

【讨论】:

【参考方案2】:

另一种方法是使用 DateTime 来检查当前扫描与上次扫描之间的时间差是否低于指定量(例如 3 秒)。如果是这样,我们不会将状态设置为新的 scanData。实际上,这会在每次 QR 码扫描之间产生 3 秒的延迟。请参阅下面的代码。

     controller.scannedDataStream.listen((scanData) 
      final currentScan = DateTime.now();
      if (lastScan == null || currentScan.difference(lastScan!) > const Duration(seconds: 3)) 
      
        lastScan = currentScan;
        print(scanData.code);
  
        setState(() 
          result = scanData;
          print('the qr just read was ' + scanData.code);
        );
     
  );

【讨论】:

以上是关于Flutter:如何暂停和重新启动“Stream”侦听器?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 jquery transit 中暂停和重新启动 css3 转换

启动stream2时如何停止stream1

如何在应用重新启动之间保存和恢复 Flutter ListView 的滚动位置?

Flutter:如何设置 Stream 和 Provider

在循环中暂停并重新启动 setTimeout

Flutter状态管理——单Stream和广播Stream