Flutter awesome_notifications - Bad state: Stream has already been listened to

2.5k views Asked by At

I am using awesome_notifications as my notification plugin; however, I get the error "Bad state: Stream has already been listened to." whenever I call notifInitialize() in initState(). This happens every time I hot reload the application from fresh.

  @override
  void initState() {
    notifInitialize();
    super.initState();
  }

This is how I am writing my function. Is there something wrong with how I set up my listeners?

  void notifInitialize() {
    notificationIcon = true;
    AwesomeNotifications().initialize(
      // set the icon to null if you want to use the default app icon
      'resource://drawable/app_icon',
      [
        NotificationChannel(
          channelKey: 'basic_channel',
          channelName: 'Basic notifications',
          channelDescription: 'Notification channel for basic tests',
          defaultColor: Colors.teal,
          importance: NotificationImportance.High,
          channelShowBadge: true,
        ),
        NotificationChannel(
          channelKey: 'scheduled_channel',
          channelName: 'Scheduled Notifications',
          channelDescription: 'Scheduled Description',
          defaultColor: Colors.teal,
          locked: true,
          importance: NotificationImportance.High,
        ),
      ],
    );
    AwesomeNotifications().isNotificationAllowed().then((isAllowed) {
      if (!isAllowed) {
        AwesomeNotifications().requestPermissionToSendNotifications();
      }
    });

    AwesomeNotifications().createdStream.listen((notification) {
      ScaffoldMessenger.of(context).showSnackBar(SnackBar(
          content: Text(
        'Notification Created on ${notification.channelKey}',
      )));
    });

    AwesomeNotifications().actionStream.listen((notification) {
      if (notification.channelKey == 'basic_channel' && Platform.isIOS) {
        AwesomeNotifications().getGlobalBadgeCounter().then((value) {
          print(value);
          AwesomeNotifications().setGlobalBadgeCounter(value - 1);
          print(value);
        });
      }
      Navigator.pushAndRemoveUntil(
          context,
          MaterialPageRoute(builder: (_) => ValidateService()),
          //(route) => route.isFirst);
          (route) => false);
    });
  }```
1

There are 1 answers

4
Brendan On

I noticed some issues with your code.

  • You're not disposing/cancelling the stream in your dispose() method
  • You do not have a state to check if you have already have an existing subscription to prevent Bad state: Stream has already been listened to..

For the first case, you can use the StreamSubscription class to save an instance of your subscription to state to enable you cancel it in your widget's dispose method.

StreamSubscription<ReceivedAction>? _actionStreamSubscription;

@override
void initState() {
  super.initState();

  ...

  listen();

  ...
}

void listen() async {
  // You can choose to cancel any exiting subscriptions
  await _actionStreamSubscription?.cancel();

  // assign the stream subscription
  _actionStreamSubscription = AwesomeNotifications().actionStream.listen((message) {
    // handle stuff here
  });
}
@override
void dispose() async {
  Future.delayed(Duration.zero, () async {
    await _actionStreamSubscription?.cancel();
  });
  super.dispose();
}

The above should work if you have a dispose() method to safely cancel existing subscriptions when the tree rebuilds.

But in a situation where you do not have a dispose() method, you could use a boolean field to determine if a subscription to the stream exists.

// somewhere in your code, assign a bool to false
bool subscribedActionStream = false;

// Just before your call to subscribe, check if not already subscribed.
if (!subscribedActionStream) {
  AwesomeNotifications().actionStream.listen((message) {
    // handle stuff here
  });
  subscribedActionStream = true;
}

Whichever you decide to use should work fine.