You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flutter中Awesome Notifications报Stream重复监听错误的解决求助

解决Flutter中退出登录后重新登录出现Stream重复监听错误的问题

问题原因

AwesomeNotifications().actionStream是单订阅流,同一时间只能有一个订阅者。退出登录后重新进入首页时,首页的initState会再次调用listen方法尝试订阅同一个流,就会触发Bad state: Stream has already been listened to.错误。

解决方案

方案1:在页面销毁时取消订阅(推荐)

利用StatefulWidget的生命周期,保存订阅对象,在页面销毁时取消订阅,确保下次进入首页能重新正常监听。

修改首页代码:

StreamSubscription? _notificationSubscription;

@override
void initState() {
  super.initState();
  // 保存订阅对象
  _notificationSubscription = AwesomeNotifications().actionStream.listen((notification) async {
    if (notification.channelKey == 'scheduled_channel') {
      var payload = notification.payload['payload'];
      var value = await FirebaseFirestore.instance
          .collection(widget.user.uid)
          .doc(payload)
          .get();
      // 加入空安全判断,避免navigatorKey为空报错
      navigatorKey.currentState?.push(PageRouteBuilder(
          pageBuilder: (_, __, ___) => DetailPage(
              user: widget.user,
              i: 0,
              docname: payload,
              color: value.data()['color'].toString(),
              createdDate: int.parse((value.data()['date'].toString())),
              documentId: value.data()['documentId'].toString(),)));
    }
  });
}

@override
void dispose() {
  // 页面销毁时取消订阅
  _notificationSubscription?.cancel();
  super.dispose();
}

方案2:将单订阅流转为广播流

如果需要多个页面同时监听通知流,可以使用asBroadcastStream()将其转为支持多订阅的广播流:

@override
void initState() {
  super.initState();
  AwesomeNotifications().actionStream.asBroadcastStream().listen((notification) async {
    // 原监听逻辑不变
    if (notification.channelKey == 'scheduled_channel') {
      var payload = notification.payload['payload'];
      var value = await FirebaseFirestore.instance
          .collection(widget.user.uid)
          .doc(payload)
          .get();
      navigatorKey.currentState?.push(PageRouteBuilder(
          pageBuilder: (_, __, ___) => DetailPage(
              user: widget.user,
              i: 0,
              docname: payload,
              color: value.data()['color'].toString(),
              createdDate: int.parse((value.data()['date'].toString())),
              documentId: value.data()['documentId'].toString(),)));
    }
  });
}

注意:广播流会给所有订阅者发送事件,若多个页面同时监听,需避免重复处理逻辑。

方案3:退出登录时全局取消订阅

如果需要在退出登录时统一处理,可以维护一个全局的订阅对象,在退出时取消:

  1. 定义全局变量(或在单例类中):
StreamSubscription? globalNotificationSubscription;
  1. 首页initState中赋值:
@override
void initState() {
  super.initState();
  globalNotificationSubscription = AwesomeNotifications().actionStream.listen((notification) async {
    // 原监听逻辑不变
  });
}
  1. 修改退出登录代码:
await FirebaseAuth.instance.signOut();
// 取消全局订阅并置空
globalNotificationSubscription?.cancel();
globalNotificationSubscription = null;
if (!mounted) return;
Navigator.pushNamedAndRemoveUntil(context, "/login", (Route<dynamic> route) => false);

总结

优先选择方案1,它遵循Flutter组件生命周期,每个页面独立管理订阅,逻辑清晰且避免全局状态维护带来的潜在问题。

内容的提问来源于stack exchange,提问作者Mat Koffman

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 22:20:41