在 Flutter 中监听 Stream 并只向 DB 写入一次

Posted

技术标签:

【中文标题】在 Flutter 中监听 Stream 并只向 DB 写入一次【英文标题】:Listen to Stream and write only once to DB in Flutter 【发布时间】:2020-12-12 12:54:39 【问题描述】:

我正在开发一个 QR 码阅读器应用程序并使用外部 QR 阅读器包 (https://pub.dev/packages/qr_code_scanner)。 它侦听 Stream 并返回 QR 数据。但是当我将数据写入 sqlite 数据库时,它会多次写入相同的数据,因为它不会停止监听 Stream。我不认为从流中取消订阅是一个好主意,因为在从 url 启动或关闭对话框返回后,我仍然需要流来收听。如果我错了,请纠正我并提出解决方案,谢谢。

void _onQRViewCreated(QRViewController controller) 
    this.controller = controller;
    controller.scannedDataStream.listen((scanData) async 
      
      setState(() 
        qrData = scanData;
      );
  
      if (await canLaunch(qrData)) 
        var status = await launch(qrData);

        if(status)
          final data = QrModel(
            content: scanData,
            date: DateFormat.yMMMd().add_jm().format(DateTime.now()),
          );
          await dbProvider.addData(data);
        

       else 

        if (!alertBoxOpen) 

          final data = QrModel(
            content: scanData,
            date: DateFormat.yMMMd().add_jm().format(DateTime.now()),
          );
          await dbProvider.addData(data);

          setState(() => alertBoxOpen = true);
          
          showDialog(
              context: context,
              builder: (context) 
                return AlertDialog(
                  content: Text(qrData),
                  actions: [
                    MaterialButton(
                        child: Text('OK',
                            style: TextStyle(
                                fontSize: 18.0, fontWeight: FontWeight.bold)),
                        onPressed: () 
                          setState(() => alertBoxOpen = false);
                          Navigator.of(context).pop();
                        ),
                  ],
                );
              );
        
      
    );
    
  

【问题讨论】:

【参考方案1】:

您在流上是否有一个订阅,还是流发出多个相同的值?如果是后者我建议你试试rxdart 包。它是 Dart 的 ReactiveX 实现。我在反应式编程方面没有太多经验,但是根据我的经验,当我使用 rxdart 中的BehaviorSubject 时,它不会在添加相同的值时通知订阅者。因此,您可以做的一件事是将 QR 流中的每个值添加到 BehaviorSubject,然后订阅(收听)行为主题以查询数据库。

【讨论】:

由于我对 rxdart 不熟悉,能否请您详细说明如何实现此解决方案? @Dulanka 在浏览 rxdart 文档后,我发现了 distinct 方法 here。它的实现使用了 Dart 的 Stream 的 distinct 方法。所以,你可能不需要 rxdart。在收听之前,只需在流中致电.distinct()。但这仅在流发出多个相同值时才有效。如果有多个订阅,则不会 感谢您的努力!这个功能真的很有帮助。但用户无法再次扫描相同的 QR,因为流不允许相同的值。 @Dulanka 用户在扫描不同的二维码后应该能够扫描相同的二维码。毕竟,您说过您不希望同一事件连续发生两次或多次。你确定你只订阅一次吗?【参考方案2】:

我为此实施了一个解决方法。我将从流返回的值 scanData 分配给变量 qrData 并添加到 DB if qrData != scanData

void _onQRViewCreated(QRViewController controller) 
    this.controller = controller;

    controller.scannedDataStream.listen((scanData) async 
      if (await canLaunch(scanData)) 
        await launch(scanData);

        if (qrData != scanData) 
          setState(() 
            qrData = scanData;
          );
          final data = QrModel(
            content: scanData,
            date: DateFormat.yMMMd().add_jm().format(DateTime.now()),
          );
          await dbProvider.addData(data);
        
       else 
        if (!alertBoxOpen) 
          if (qrData != scanData) 
            setState(() 
              qrData = scanData;
            );
            final data = QrModel(
              content: scanData,
              date: DateFormat.yMMMd().add_jm().format(DateTime.now()),
            );
            await dbProvider.addData(data);
          

          setState(() => alertBoxOpen = true);

          showDialog(
              context: context,
              builder: (context) 
                return AlertDialog(
                  content: Text(qrData),
                  actions: [
                    MaterialButton(
                        child: Text('OK',
                            style: TextStyle(
                                fontSize: 18.0, fontWeight: FontWeight.bold)),
                        onPressed: () 
                          setState(() => alertBoxOpen = false);
                          Navigator.of(context).pop();
                        ),
                  ],
                );
              );
        
      
    );
  

【讨论】:

以上是关于在 Flutter 中监听 Stream 并只向 DB 写入一次的主要内容,如果未能解决你的问题,请参考以下文章

Flutter - 从 Stream 接收然后修改数据

Flutter 中的应用级别或全局 Steam 监听

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

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

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

Flutter:在 Stream 类中访问 SharedPreferences Provider / ChangeNotifier