Flutter_blue: Getting multiple ( duplicate ) characteristics Notification data in Flutter_Blue 0.6

Created on 18 Jul 2019  ·  50Comments  ·  Source: pauldemarco/flutter_blue

In 0.6 v with Flutter 1.7.8 , following issue observed

  • On app restart, successfully connected to the device with set notification = true on a characteristics
  • after disconnecting ble communication from device got 'disconnect' event in notification listener
    myDevice.state.listen((deviceState) { ..... });
  • without resetting the app , if again connected to the device with set notification = true on characteristics Then it is observed that we get duplicate notification value always.
    (read_characteristics.value.listen((){.......})
  • This duplication increases if we repeat the above steps ( without resetting app )
    e.g. if we connect to device 5 times without resetting app then get 10 times duplicate data every time. This happens on debug as well as release apk

Please let us know how to handle this issue? what is the way to handle disconnect event to avoid duplicate notifications?

Thanks ,
Gaurav

Most helpful comment

@Fleximex @GauravPatni @andersunloc @khainke @MrPello @votruk @jtrovato Before I publish another version, please try out the fixes by adding the github repo to your pubspec like so:

dependencies:
  flutter_blue:
    git:
      url: git://github.com/pauldemarco/flutter_blue.git

All 50 comments

Do you mean that a single stream subscription c.value.listen() receives each event 2 times? So basically like this?

device.state.listen((state){
 print('connection state: $state');
});
await device.connect();
// ...
// output: connection state: BluetoothDeviceState.connected
await c.setNotifyValue(true);
c.value.listen((v){
 print('data: $v');
};
// output: data: 
// ...
// output: data: some data
// ...
await device.disconnect();
// output: connection state: BluetoothDeviceState.disconnected
await device.connect();
// output: connection state: BluetoothDeviceState.connected;
// ....
// output data: some data (2)
// output data: some data (2)

Or are you creating 2 seperate stream subscriptions and wonder why the first one does not get canceled on a disconnect?

Thanks @khainke for your reply .
I have canceled the subscription in my disconnect function. But still getting the same result

deviceStateSub = myDevice.state.listen((deviceState) {
        if (deviceState == BluetoothDeviceState.connected) {

          print("-----------Device Connected---------");
          discoverServices(); // Function to request Services & find required service & char

        }
        if (deviceState == BluetoothDeviceState.disconnected) {

              disconnect();  // function to handle disconnect event 
              myDevice = null;
              print("-----------Device Disconnected---------");

          }

      });
 readCharSub = readCharacteristics.value.listen((onData) {

                print("RX....");  
                if (onData.isNotEmpty) bleDataHandler(onData);
/* With every new device connection , 
getting same number of duplicate "RX..." & call to "bleDataHandler"
*/
              });

  disconnect() async {

    print("Disconnecting.....");
    await readCharSub.cancel();
    await deviceStateSub.cancel();
    print("Subscription Cancelled");
    await myDevice?.disconnect();

  }

// After every device disconnect event, getting "Disconnecting....." & "Subscription Cancelled" log .
I/flutter (14653): BLE : Send Int-> [36, 11, 8, 18, 7, 19, 17, 59, 1, 111, 36]
D/BluetoothGatt(14653): writeCharacteristic() - uuid: 6e82ed35-563f-428b-bbaa-99a1be2c2102
D/BluetoothGatt(14653): onCharacteristicWrite() - Device=00:0D:6F:A7:2F:04 UUID=6e82ed35-563f-428b-bbaa-99a1be2c2102 Status=0
D/FlutterBluePlugin(14653): [onCharacteristicWrite] uuid: 6e82ed35-563f-428b-bbaa-99a1be2c2102 status: 0
I/flutter (14653): Get Status
D/BluetoothGatt(14653): onNotify() - Device=00:0D:6F:A7:2F:04 UUID=ae836c7b-c244-4030-9162-6fa409c11ee4
D/FlutterBluePlugin(14653): [onCharacteristicChanged] uuid: ae836c7b-c244-4030-9162-6fa409c11ee4
D/BluetoothGatt(14653): onNotify() - Device=00:0D:6F:A7:2F:04 UUID=ae836c7b-c244-4030-9162-6fa409c11ee4
D/FlutterBluePlugin(14653): [onCharacteristicChanged] uuid: ae836c7b-c244-4030-9162-6fa409c11ee4
D/BluetoothGatt(14653): onNotify() - Device=00:0D:6F:A7:2F:04 UUID=ae836c7b-c244-4030-9162-6fa409c11ee4
D/FlutterBluePlugin(14653): [onCharacteristicChanged] uuid: ae836c7b-c244-4030-9162-6fa409c11ee4
D/BluetoothGatt(14653): onNotify() - Device=00:0D:6F:A7:2F:04 UUID=ae836c7b-c244-4030-9162-6fa409c11ee4
D/FlutterBluePlugin(14653): [onCharacteristicChanged] uuid: ae836c7b-c244-4030-9162-6fa409c11ee4
I/flutter (14653): RX....
I/flutter (14653): BLE :Frame Received Int (28)-> [36, 9, 0, 8, 0, 179, 1, 242, 36]
I/flutter (14653): RX....
I/flutter (14653): BLE :Frame Received Int (29)-> [36, 9, 0, 8, 0, 179, 1, 242, 36]
I/flutter (14653): RX....
I/flutter (14653): BLE :Frame Received Int (30)-> [36, 9, 0, 8, 0, 179, 1, 242, 36]
I/flutter (14653): RX....
I/flutter (14653): BLE :Frame Received Int (31)-> [36, 9, 0, 8, 0, 179, 1, 242, 36]

Please let me know is there any modification required while canceling Stream?

(you're not waiting for your disconnect() method to complete, mydevice gets set to null before await mydevice?.disconnect is called), but this should have no effect on the characteristic stream subscription and their cancelation,
looks like a bug to me, cant verify it right now, but for many ppl flutter_blue v.0.6.0+1 is not rly usable, i did go back to the fork of v.0.5 from bpillon, see https://github.com/pauldemarco/flutter_blue/issues/305#issuecomment-510031941

@khainke @pauldemarco

I removed myDevice = null
But still the same issue.

@khainke @pauldemarco

One important observation for this issue :

  • To investigate this duplication issue, I modified my code as follows --
 bleServices= await myDevice.discoverServices();
for (BluetoothService service in bleServices) {
        if (service.uuid.toString() == myUUID.toString()) {
          print("Service Match --${service.uuid}");
          for (BluetoothCharacteristic char in service.characteristics) {

            if (char.uuid.toString() == myReadCharUUID.toString()) {

              readCharacteristics = char; // save in Global variable
              handleReadChar(readCharacteristics); 

            }
            else if (char.uuid.toString() == myWriteCharUUID.toString()) {
              writeCharacteristics = char;
              print("Char Write Found --${writeCharacteristics.uuid}");
            }
          }

        }
      }

  handleReadChar(BluetoothCharacteristic readchar) async {
    print("Read Char Handler");
    await readchar.setNotifyValue(true);
    print("Char Read Found --${readCharacteristics.uuid}");
    if (globalCounter == 1) {
      print("One");
      rxOne(readCharacteristics);
      globalCounter ++;
    } else {
      print("Two");
      rxTwo(readCharacteristics);
    }
  }

  rxOne(BluetoothCharacteristic char) {
    readCharSubOne = char.value.listen((onData) {
      print("RX - One ....");
      if (onData.isNotEmpty) blocGlobal.bleDataHandler(onData);
    });
  }

  rxTwo(BluetoothCharacteristic char) {
    readCharSubOne = char.value.listen((onData) {
      print("RX - Two ....");
      if (onData.isNotEmpty) blocGlobal.bleDataHandler(onData);
    });
  }

  disconnect() async {
    print("Disconnecting.....");

    await deviceStateSub?.cancel();
    await readCharSubOne?.cancel();
    await readCharSubTwo?.cancel();
    print("Subscription Cancelled");
    await myDevice?.disconnect();

    print("-----------Device Disconnected---------");
  }
  • When connected to device first time, 'rxOne' is called. So for every Rx event "RX - One ...." is printing
  • note that this time only one reply received. which is expected.
I/flutter (11802): checking....
I/flutter (11802): Service Match --8e4cb84e-0638-47f5-951f-6f530064b0e0
I/flutter (11802): Read Char Handler
D/BluetoothGatt(11802): setCharacteristicNotification() - uuid: ae836c7b-c244-4030-9162-6fa409c11ee4 enable: true
D/BluetoothGatt(11802): writeDescriptor() - uuid: 00002902-0000-1000-8000-00805f9b34fb
I/flutter (11802): Char Write Found --6e82ed35-563f-428b-bbaa-99a1be2c2102
D/BluetoothGatt(11802): onDescriptorWrite() - Device=00:0D:6F:A7:2F:04 UUID=ae836c7b-c244-4030-9162-6fa409c11ee4
D/FlutterBluePlugin(11802): [onDescriptorWrite] uuid: 00002902-0000-1000-8000-00805f9b34fb status: 0
I/flutter (11802): Char Read Found --ae836c7b-c244-4030-9162-6fa409c11ee4
I/flutter (11802): One
I/flutter (11802): RX - One ....

I/flutter (11802): BLE : Send Int-> 36, 11, 8, 19, 7, 19, 10, 36, 24, 106, 36]
D/BluetoothGatt(11802): writeCharacteristic() - uuid: 6e82ed35-563f-428b-bbaa-99a1be2c2102
D/BluetoothGatt(11802): onCharacteristicWrite() - Device=00:0D:6F:A7:2F:04 UUID=6e82ed35-563f-428b-bbaa-99a1be2c2102 Status=0
D/FlutterBluePlugin(11802): [onCharacteristicWrite] uuid: 6e82ed35-563f-428b-bbaa-99a1be2c2102 status: 0
I/flutter (11802): Get Status
D/BluetoothGatt(11802): onNotify() - Device=00:0D:6F:A7:2F:04 UUID=ae836c7b-c244-4030-9162-6fa409c11ee4
D/FlutterBluePlugin(11802): [onCharacteristicChanged] uuid: ae836c7b-c244-4030-9162-6fa409c11ee4
I/flutter (11802): RX - One ....
I/flutter (11802): BLE :Frame Received Int (0)-> [36, 9, 0, 8, 0, 0, 0, 65, 36]

I/flutter (11802): BLE : Send Int-> 36, 11, 8, 19, 7, 19, 10, 36, 29, 106, 36]
D/BluetoothGatt(11802): writeCharacteristic() - uuid: 6e82ed35-563f-428b-bbaa-99a1be2c2102
D/BluetoothGatt(11802): onCharacteristicWrite() - Device=00:0D:6F:A7:2F:04 UUID=6e82ed35-563f-428b-bbaa-99a1be2c2102 Status=0
D/FlutterBluePlugin(11802): [onCharacteristicWrite] uuid: 6e82ed35-563f-428b-bbaa-99a1be2c2102 status: 0
I/flutter (11802): Get Status
D/BluetoothGatt(11802): onNotify() - Device=00:0D:6F:A7:2F:04 UUID=ae836c7b-c244-4030-9162-6fa409c11ee4
D/FlutterBluePlugin(11802): [onCharacteristicChanged] uuid: ae836c7b-c244-4030-9162-6fa409c11ee4
I/flutter (11802): RX - One ....
I/flutter (11802): BLE :Frame Received Int (1)-> [36, 9, 0, 8, 0, 0, 0, 65, 36]



D/BluetoothGatt(11802): onClientConnectionState() - status=0 clientIf=5 device=00:0D:6F:A7:2F:04
D/FlutterBluePlugin(11802): [onConnectionStateChange] status: 0 newState: 0

I/flutter (11802): Disconnecting.....
I/flutter (11802): Subscription Cancelled
D/BluetoothGatt(11802): cancelOpen() - device: 00:0D:6F:A7:2F:04
I/flutter (11802): -----------Device Disconnected---------
  • While keeping the app open & reconnecting the Bluetooth device, 'rxTwo' is called. So for every Rx event "RX - Two ...." is printing
  • Note that, For this time, getting two responses BUT BOTH ARE "RX - Two ....". This shows that previous subscription 'readCharSubOne ' is canceled successfully.
  • Also confirmed from device log that it received only one request & sends only one response.
I/flutter (11802): checking....
I/flutter (11802): Service Match --8e4cb84e-0638-47f5-951f-6f530064b0e0
I/flutter (11802): Read Char Handler
I/flutter (11802): Char Write Found --6e82ed35-563f-428b-bbaa-99a1be2c2102
D/BluetoothGatt(11802): setCharacteristicNotification() - uuid: ae836c7b-c244-4030-9162-6fa409c11ee4 enable: true
D/BluetoothGatt(11802): writeDescriptor() - uuid: 00002902-0000-1000-8000-00805f9b34fb
D/BluetoothGatt(11802): onDescriptorWrite() - Device=00:0D:6F:A7:2F:04 UUID=ae836c7b-c244-4030-9162-6fa409c11ee4
D/FlutterBluePlugin(11802): [onDescriptorWrite] uuid: 00002902-0000-1000-8000-00805f9b34fb status: 0
I/flutter (11802): Char Read Found --ae836c7b-c244-4030-9162-6fa409c11ee4
I/flutter (11802): Two
I/flutter (11802): RX - Two ....

I/flutter (11802): BLE : Send Int-> [36, 11, 8, 19, 7, 19, 10, 37, 42, 107, 36]
D/BluetoothGatt(11802): writeCharacteristic() - uuid: 6e82ed35-563f-428b-bbaa-99a1be2c2102
D/BluetoothGatt(11802): onCharacteristicWrite() - Device=00:0D:6F:A7:2F:04 UUID=6e82ed35-563f-428b-bbaa-99a1be2c2102 Status=0
D/FlutterBluePlugin(11802): [onCharacteristicWrite] uuid: 6e82ed35-563f-428b-bbaa-99a1be2c2102 status: 0
I/flutter (11802): Get Status
D/BluetoothGatt(11802): onClientConnectionState() - status=0 clientIf=5 device=00:0D:6F:A7:2F:04
D/FlutterBluePlugin(11802): [onConnectionStateChange] status: 0 newState: 2
D/BluetoothGatt(11802): onNotify() - Device=00:0D:6F:A7:2F:04 UUID=ae836c7b-c244-4030-9162-6fa409c11ee4
D/FlutterBluePlugin(11802): [onCharacteristicChanged] uuid: ae836c7b-c244-4030-9162-6fa409c11ee4
D/BluetoothGatt(11802): onNotify() - Device=00:0D:6F:A7:2F:04 UUID=ae836c7b-c244-4030-9162-6fa409c11ee4
D/FlutterBluePlugin(11802): [onCharacteristicChanged] uuid: ae836c7b-c244-4030-9162-6fa409c11ee4
I/flutter (11802): -----------Device Connected---------
I/flutter (11802): RX - Two ....
I/flutter (11802): BLE :Frame Received Int (2)-> [36, 9, 0, 8, 0, 0, 0, 65, 36]
I/flutter (11802): RX - Two ....
I/flutter (11802): BLE :Frame Received Int (3)-> [36, 9, 0, 8, 0, 0, 0, 65, 36]

  • Also, note that this issue resolves if we disconnect Bluetooth from the phone without closing the app

@pauldemarco Please help to resolve this

Thanks,
Gaurav

@pauldemarco Please look into this issue ...

at least reply whether this is the flutter_blue package issue or I am doing something wrong? So that I can proceed accordingly .....

with this issue, I am not able to proceed with my development.

@khainke

Thanks,
Gaurav

@pauldemarco Please look into this issue ...

Thanks,
Gaurav

@pauldemarco Please look into this issue ...

Its creating big issues ........

@GauravPatni I've looked into the issue and I'm unable to replicate what you are seeing.
Do you mind me asking what model phone are you using?
Which type of device are you communicating with?
(I'll try to recreate as much of your scenario on my end)

Hi @pauldemarco
please refer following details

Phone model: Android 4.4.4 MI HM NOTE 1 LTE
Device: It's a BLE 4.0 MCU which we are using in our product. we can program ble parameters if required.

Note that I am using an old Android phone to make the app compatible with the majority of the phone.

Thanks,
Gaurav

Hey guys,

I am also seeing this issue. Below is my output from subscribing to a characteristic and this is what happens when my device is notifying me with data:

didUpdateValueForCharacteristic 92062D0D-158A-8DF9-DF1D-5C6C73BE261B uuid: 0000321a-1212-efde-1523-785fef13d123 value: <00030114 01060800 00000000 00000002 02040000> uuid: 0000321a-1212-efde-1523-785fef13d123 value: <00030114 01060800 00000000 00000002 02040000> didUpdateValueForCharacteristic 92062D0D-158A-8DF9-DF1D-5C6C73BE261B uuid: 0000321a-1212-efde-1523-785fef13d123 value: <00000301 02340004 01021000 05000194 06060400> uuid: 0000321a-1212-efde-1523-785fef13d123 value: <00000301 02340004 01021000 05000194 06060400> didUpdateValueForCharacteristic 92062D0D-158A-8DF9-DF1D-5C6C73BE261B uuid: 0000321a-1212-efde-1523-785fef13d123 value: <00404007 00010008 00011009 01020000 0a010a3d> uuid: 0000321a-1212-efde-1523-785fef13d123 value: <00404007 00010008 00011009 01020000 0a010a3d> didUpdateValueForCharacteristic 92062D0D-158A-8DF9-DF1D-5C6C73BE261B uuid: 0000321a-1212-efde-1523-785fef13d123 value: <00000000 00000000 000b0102 00000c09 08303030> uuid: 0000321a-1212-efde-1523-785fef13d123 value: <00000000 00000000 000b0102 00000c09 08303030> didUpdateValueForCharacteristic 92062D0D-158A-8DF9-DF1D-5C6C73BE261B uuid: 0000321a-1212-efde-1523-785fef13d123 value: <30350000 000d0908 302e3034 2e303100 0e090800> uuid: 0000321a-1212-efde-1523-785fef13d123 value: <30350000 000d0908 302e3034 2e303100 0e090800>

Note that I am using an iPhone 7 and on iOS 12.3

@GauravPatni
Very simple. You call the listen on readCharSubOne twice. Your rxTwo method should have readCharSubTwo instead of readCharSubOne. Your error highlighted:

rxOne(BluetoothCharacteristic char) {
    readCharSubOne = char.value.listen((onData) {
      print("RX - One ....");
      if (onData.isNotEmpty) blocGlobal.bleDataHandler(onData);
    });
  }

  rxTwo(BluetoothCharacteristic char) {
    readCharSubOne = char.value.listen((onData) {    //<----- wrong StreamSubscription used
      print("RX - Two ....");
      if (onData.isNotEmpty) blocGlobal.bleDataHandler(onData);
    });
  }

@jtrovato
Can you make sure you don't call the notify on the BluetoothCharacteristic multiple times. Also make sure that on a disconnect you remove the subscription. Not only on your project side but also with a call to the Bluetooth stack:
valueChangedSubscriptions[c.uuid]?.cancel();

The code below shows how I set a notify on a BluetoothCharacteristic. Note that I have had the same problem as you. The if statement if (!valueChangedSubscriptions.containsKey(c.uuid)) should make sure you can't call the notify twice, but sometimes it didn't for me. So even though if valueChangedSubscriptions.containsKey(c.uuid) returns false, I now cancel my valueChangedSubscriptions just to be sure.

setNotification(BluetoothCharacteristic c) async {
   // Cancel subscription
   await c.setNotifyValue(false);
   valueChangedSubscriptions[c.uuid]?.cancel();
   valueChangedSubscriptions.remove(c.uuid);

   if (!valueChangedSubscriptions.containsKey(c.uuid)) {
      await c.setNotifyValue(true);
      final sub = c.value.listen((d) {
         print("Notified with the following message: " + _handleMessage(d));
      });
      valueChangedSubscriptions[c.uuid] = sub;
   }
}

I also found this! Exactly as described above, the second subscription gets duplicate data!

Android 8.1.0 on an SM-G390F Galaxy XCover 4

Same code runs perfectly on iOS, and on an Android 5.1.1 / SMG388F Galaxy XCover 3

@Fleximex ,

In my code 'globalCounter' manages call to 'rxOne' & 'rxTwo'. So there is only one notification listener called.( As only one log prints either print("One"); or print("Two") );

Also , even after fixing typo of 'readCharSubOne ' problem remains the same.

Note that All subscriptions are cancelled on disconnect event vis 'disconnect()' .

 if (globalCounter == 1) {
      print("One");
      rxOne(readCharacteristics);
      globalCounter ++;
    } else {
      print("Two");
      rxTwo(readCharacteristics);
    }
  }
  rxOne(BluetoothCharacteristic char) {
    readCharSubOne = char.value.listen((onData) {
      print("RX - One ....");
      if (onData.isNotEmpty) blocGlobal.bleDataHandler(onData);
    });
  }

  rxTwo(BluetoothCharacteristic char) {
    readCharSubTwo = char.value.listen((onData) {
      print("RX - Two ....");
      if (onData.isNotEmpty) blocGlobal.bleDataHandler(onData);
    });
  }


As others are also facing the same issue it means its a bug in the package.

@GauravPatni @pauldemarco
I see what your problem is now. This is indeed a bug in the framework. I can replicate this.

So what happens is that when you connect to a device and you set the notify on a BluetoothCharacteristic it works as expected. If the device notifies on that characteristic you receive 1 notify message. But after you disconnect and connect again, and even though you canceled the notify, if you notify a characteristic and the devices notifies: you get two messages.
The cancel on the previous notify does seem to get through to the Bluetooth stack, as you don't get that specific notify anymore (I debugged it by printing a predefined id (Integer) attached to the setNotify call). But the notify you called after connecting again now fires 2 messages even though the Bluetooth stack seems to deliver only 1 to the Flutter Blue framework. So in the Flutter Blue framework there seems to remain a listener or event handler which should have been removed/cancelled which, even though it now concerns a different notify (or also even a different characteristic), fires the event for the new notify.

I think the notify on BluetoothCharacteristic and the listen on BluetoothDeviceState need some sort of overhaul. As it's too easy to mess up either one of these resulting in 2 or more notifies or device state events where there should be 1.

Additional problem for me
_EDIT: Cut a long paragraph because it's not completely relevant to this issue._

Long story short. I try to automatically reconnect to the BluetoothDevice and that causes other problems which I try to fix which causes other problems, etc.

But I stop here because this feels way too hacky for something that shouldn't be a problem in the first place. I need to be able to rely on the Flutter Blue framework to properly return the correct amount of notifies and BluetoothDeviceState events.

Another interesting observation, if the data is to long and split up in chunks, i receive all the pieces in order, and then alle the pieces a second time.

so A, is split up into A1,A2,A3,

so then I receive

A1
A2
A3
A1
A2
A3

and not

A1
A1
A2
A2
A3
A3

Have the same issue and it is very frustrating. The only solution I came up with is discarding the same subsequent values. It can be easily done with Observable(characteristic.value).distinct() operator.

It is not an ideal solution of course and it can potentially create additional bugs, but as workaround, it's ok for me. More or less...

Seeing what I think is the same thing but with a twist. I get extra BluetoothDeviceState updates when I write to a characteristic after connecting and disconnecting and then reconnecting to the device.

Looking in the log I found this.

  1. Connect to device (and start notifications on a characteristic).

D/BluetoothGatt(27088): onClientConnectionState() - status=0 clientIf=5 device=D9:B6:B6:D2:B7:47

  1. Disconnect from device:

D/BluetoothGatt(27088): onClientConnectionState() - status=8 clientIf=5 device=D9:B6:B6:D2:B7:47

  1. Connect to device again (and start notifications on a characteristic).

D/BluetoothGatt(27088): onClientConnectionState() - status=0 clientIf=7 device=D9:B6:B6:D2:B7:47

Finally writing a value to the characteristic will then trigger the false extra BluetoothDeviceState update, in the log there is an update with the old clientIf=5 value.

D/BluetoothGatt(27088): onClientConnectionState() - status=0 clientIf=5 device=D9:B6:B6:D2:B7:47

Unclear what it means but it's something strange going on.

Still testing! Made an algorithm that managed to filter the second pass (with duplicates). At the next connect i now get 3x every message :-D

Pretty constantly with a Galaxy S8 + Android 9

Here is the underlying android issue:

https://stackoverflow.com/questions/33274009/how-to-prevent-bluetoothgattcallback-from-being-executed-multiple-times-at-a-tim

In FlutterBluePlugin.java, line 219 + 221, we call:

gattServer = device.connectGatt(activity, options.getAndroidAutoConnect(), mGattCallback...

..and that mGattCallback is not cleared well if we reconnect to the same device twice.

So in FlutterBluePlugin.java, we simply need to change

222: gattServer.disconnect();

(which only disconnects from device, but does not remove listeners)

to

222: gattServer.disconnect();
223: gattServer.close();  // also removes listeners!

I'm testing it now, and all issues have disappeared. This will probably explain a lot of strange issues with duplicate messages!

@pauldemarco This can be solved with one single line of code :-D

I am currently trying to replicate the issue following @Fleximex using an Android phone and the nRF52 dev kit, no luck. Can someone supply me with an example project where they are seeing this?

@andersunloc The .close() method does get called on the gattServer, after the DISCONNECTED state has been received in onConnectionStateChange: FlutterBluePlugin.java#L707

Perhaps certain implementations of Android do not guarantee a callback of DISCONNECTED state and this method is never called. Can you verify you see a DISCONNECTED state when you attempt to disconnect?

Update: I am able to replicate the issue, fix coming soon...

For reference, this is how to replicate the issue using the example app:

  1. Using nRF Connect: Setup a simple battery service with battery characteristic that can notify with CCCD, and start advertising.

  2. Using FlutterBlueExample: search for and connect to the device from step 1, then discover services by clicking the refresh trailing icon.

  3. Using FlutterBlueExample: Find the battery characteristic and enable notification.

  4. Using nRF Connect: Disconnect from the phone.

  5. Using FlutterBlueExample: click the Connect button.

  6. Using nRF Connect: Start advertising again, notice that we reconnect.

  7. Using FlutterBlueExample: re-discover services and subscribe to battery characteristic notification

  8. Using nRF Connect: Change the value of the battery characteristic and notice that we receive duplicate notifications within the app.

The above steps can be repeated a number of times, each time resulting in another duplicate message.

Version 0.6.0+3 has been published with the fix, please try it out and let me know!

@pauldemarco ,
I am still facing the same issue :(

I will recheck my code & reply.

Meanwhile, request you all @Fleximex @jtrovato @andersunloc @votruk @khainke to give your feedback on Version 0.6.0+3.

@pauldemarco, @GauravPatni
Yep, still the same issue with 0.6.0+3

My add "gattServer.close();" fixed it in +2, and also fixes it in +3

@pauldemarco The issue is very sporadic, sometimes devices works for a long time, and then suddenly the error is back. Our Galaxy S8 / Android 9 is most reliable in having the issue.

We have 4 old/new test devices; XCover3, XCover4, Galaxy8, and asus_a002, and the "close" fix works reliably on all of them over many runs.

@pauldemarco I'm guessing your idea that 'disconnect' is not delivered is correct, remember the other bug where we get a disconnect delivered before the first connect?

Btw thanks for a great framework @pauldemarco , you have saved us for TONS of work!

Found this article about gatt: https://android.jlelse.eu/lessons-for-first-time-android-bluetooth-le-developers-i-learned-the-hard-way-fee07646624

Quote:
Calling disconnect right before close: calling bluetoothDevice.disconnect() right before bluetoothDevice.close() is redundant and can result in issues in some devices. Try using only close() instead.

@andersunloc I have a Galaxy S8 running Android 9 and I am unable to reproduce the bug over many tries with the steps outlined here: https://github.com/pauldemarco/flutter_blue/issues/317#issuecomment-522155514

Can you supply me with an example project or code where you are seeing this?

Not sure if I'm able to pull out the ble code only, and we are talking to pretty specific hardware. However, it is the 'onCharacteristicChanged' in 'FlutterBluePlugin.java:752' that is triggered multiple times when I have connected to a device, then disconnected, then connected. I both read and write in between. I call flutter_blue in the following order:

//Step by step, we wait for stuff to complete, and we have a lot of error handling.

_onStateChangedSub = flutter_blue.state.listen(...)
_scanStateSub = flutter_blue.isScanning.listen(...)

_scanResultSub = flutter_blue.scan().listen(...)

//when device is found

await _scanResultSub?.cancel();
await flutter_blue.stopScan();

_connStateSub = device.state.listen(...)
device.connect(autoConnect: false)

//when device state = connected

device.discoverServices()

//find _txChar and _rxChar from discovered characteristics

_rxChar.setNotifyValue(true);

_rxSub = _rxChar.value.listen(...)
//this is where we get double data on second run.

//then we do a lot of write + receive on _rxChar/_txChar back and forth

writeWithoutResponse = _txChar.properties.writeWithoutResponse;
_txChar.write(chunk, withoutResponse: writeWithoutResponse);

//and then (this might be crucial) we wait until det DEVICE disconnects!
//so we get 'BluetoothDeviceState.disconnected', we dont disconnect ourself.

//then we do
_rxSub.cancel()
_connStateSub.cancel()
device.disconnect()

_onStateChangedSub.cancel()
_scanStateSub.cancel()

//Now if we repeated all of this, we get double data, triple data, etc...

@pauldemarco I think this is it:

After looking some more at the java code :-)

If the device disconnects, then the 'onConnectionStateChange' is called.

Since "we" have not called disconnect yet, this will not be called, since the server is still inside of mGattServers

if (!mGattServers.containsKey(gatt.getDevice().getAddress())) {
    gatt.close();
}

Later when I then call "disconnect" as a follow up, it will be removed from mGattServers, but the 'onConnectionStateChange' event will not be triggered again, so "close" will not be called that time either.

Basically: if the device disconnects, "close" is never called.

Could that be it?

One thing to add to the discussion. When I manually reconnect I don't have the issue. What I mean by manually is that I press the disconnect button, wait for these messages

D/BluetoothGatt(29319): close()
D/BluetoothGatt(29319): unregisterApp() - mClientIf=8

to show up in the logs and then pressing the connect button again.

However if I reconnect automatically the above logs have not appeared yet and I have the get the duplicate notify issue. When I disconnect while having the duplicate notify issue, the logs show this:

D/BluetoothGatt(29319): close()
D/BluetoothGatt(29319): unregisterApp() - mClientIf=8
D/BluetoothGatt(29319): close()
D/BluetoothGatt(29319): unregisterApp() - mClientIf=20

As you can see the close gets called two times which should not happen. And apparently the app is registered multiple times with the Bluetooth Gatt Server.

So another solution could be waiting for the close to finish. So maybe the Flutter Blue instance could have a

Future<bool> get serverConnected => _channel.invokeMethod( METHOD CALLING SERVER STATUS ).then<bool>((d) => d);

method. So then before you connect, you can check for this value to be false.

Thanks for the follow up @fleximex and @andersunloc.
I will attempt to replicate this again tomorrow.

@fleximex you mention that the reconnect must happen on its own, can you elaborate on the steps to take to make this happen?

@pauldemarco
The automatic reconnect is something I do on my project side. I simply disconnect and after all code has executed I call connect again on the BluetoothDevice. If it does not connect in the timeout I set, the onTimeout callback I use on the connect method, calls connect again.
But because there is no Future or Stream (for closing the Gatt Server) for me to wait for it is probably not closed when the connect method is called again.

with regard to @andersunloc and @Fleximex comments, I can confirm, that the issue still occurs for me, when the device disconnects without a call to device.disconnect, for example when the device disconnects due to a power out (on the device side) or an out of range problem. The next connect is bugged for me, interesting parts from the log:

# connect 

D/BluetoothManager(18415): getConnectedDevices
D/BluetoothGatt(18415): connect() - device: #, auto: false
D/BluetoothAdapter(18415): isSecureModeEnabled
D/BluetoothGatt(18415): registerApp()
D/BluetoothGatt(18415): registerApp() - UUID=#
D/BluetoothGatt(18415): onClientRegistered() - status=0 clientIf=14
D/BluetoothGatt(18415): onClientConnectionState() - status=0 clientIf=14 device=#
D/FlutterBluePlugin(18415): [onConnectionStateChange] status: 0 newState: 2
D/BluetoothGatt(18415): discoverServices() - device: #
D/BluetoothGatt(18415): onConnectionUpdated() - Device=# interval=6 latency=0 timeout=500 status=0
D/BluetoothGatt(18415): onSearchComplete() = Device=# Status=0
D/FlutterBluePlugin(18415): [onServicesDiscovered] count: 4 status: 0
D/BluetoothGatt(18415): setCharacteristicNotification() - uuid: # enable: true
D/BluetoothGatt(18415): onConnectionUpdated() - Device=# interval=39 latency=0 timeout=500 status=0

# some writes and reads - normal (omitted)

# Device got disconnected here with device.disconnect - normal

D/BluetoothGatt(18415): cancelOpen() - device: #
D/BluetoothGatt(18415): onClientConnectionState() - status=0 clientIf=14 device=#
D/FlutterBluePlugin(18415): [onConnectionStateChange] status: 0 newState: 0
D/BluetoothGatt(18415): close()
D/BluetoothGatt(18415): unregisterApp() - mClientIf=14

# new connect - normal

D/BluetoothManager(18415): getConnectedDevices
D/BluetoothGatt(18415): connect() - device: #, auto: false
D/BluetoothAdapter(18415): isSecureModeEnabled
D/BluetoothGatt(18415): registerApp()
D/BluetoothGatt(18415): registerApp() - UUID=#
D/BluetoothGatt(18415): onClientRegistered() - status=0 clientIf=14
D/BluetoothGatt(18415): onClientConnectionState() - status=0 clientIf=14 device=#
D/FlutterBluePlugin(18415): [onConnectionStateChange] status: 0 newState: 2
D/BluetoothGatt(18415): discoverServices() - device: #
D/BluetoothGatt(18415): onConnectionUpdated() - Device=# interval=6 latency=0 timeout=500 status=0
D/BluetoothGatt(18415): onSearchComplete() = Device=# Status=0
D/FlutterBluePlugin(18415): [onServicesDiscovered] count: 4 status: 0
D/BluetoothGatt(18415): setCharacteristicNotification() - uuid: # enable: true
D/BluetoothGatt(18415): onConnectionUpdated() - Device=# interval=39 latency=0 timeout=500 status=0

# some writes and reads - normal (omitted)

# device gets disconnected by power out

D/BluetoothGatt(18415): onClientConnectionState() - status=8 clientIf=14 device=#
D/FlutterBluePlugin(18415): [onConnectionStateChange] status: 8 newState: 0
D/BluetoothGatt(18415): cancelOpen() - device: #

# notice: no close called

# now new connect - now it is bugged: multiple connection state updates and characteristic updates

D/BluetoothManager(18415): getConnectedDevices
D/BluetoothGatt(18415): connect() - device: #, auto: false
D/BluetoothAdapter(18415): isSecureModeEnabled
D/BluetoothGatt(18415): registerApp()
D/BluetoothGatt(18415): registerApp() - UUID=a3e29011-8c3f-48b0-94e1-5ce7a586b1de
D/BluetoothGatt(18415): onClientRegistered() - status=0 clientIf=16
D/BluetoothGatt(18415): onClientConnectionState() - status=0 clientIf=16 device=#
D/FlutterBluePlugin(18415): [onConnectionStateChange] status: 0 newState: 2
D/BluetoothGatt(18415): discoverServices() - device: #
D/BluetoothGatt(18415): onConnectionUpdated() - Device=# interval=6 latency=0 timeout=500 status=0
D/BluetoothGatt(18415): onSearchComplete() = Device=# Status=0
D/FlutterBluePlugin(18415): [onServicesDiscovered] count: 4 status: 0
D/BluetoothGatt(18415): setCharacteristicNotification() - uuid: # enable: true
D/BluetoothGatt(18415): onConnectionUpdated() - Device=# interval=39 latency=0 timeout=500 status=0
D/FlutterBluePlugin(18415): [onDescriptorWrite] uuid: # status: 0
D/FlutterBluePlugin(18415): [onCharacteristicWrite] uuid: # status: 0
D/FlutterBluePlugin(18415): [onCharacteristicWrite] uuid: # status: 0
D/FlutterBluePlugin(18415): [onCharacteristicWrite] uuid: # status: 0
D/BluetoothGatt(18415): onClientConnectionState() - status=0 clientIf=14 device=#
D/FlutterBluePlugin(18415): [onConnectionStateChange] status: 0 newState: 2
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicWrite] uuid: # status: 0
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicWrite] uuid: # status: 0
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/ViewRootImpl@ee1581[MainActivity](18415): ViewPostIme pointer 0
D/ViewRootImpl@ee1581[MainActivity](18415): ViewPostIme pointer 1
D/FlutterBluePlugin(18415): [onCharacteristicWrite] uuid: # status: 0
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicWrite] uuid: # status: 0
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicChanged] uuid: #
D/FlutterBluePlugin(18415): [onCharacteristicWrite] uuid: # status: 0

# now a call to device.disconnect

D/BluetoothGatt(18415): cancelOpen() - device: #
D/BluetoothGatt(18415): onClientConnectionState() - status=0 clientIf=16 device=#
D/FlutterBluePlugin(18415): [onConnectionStateChange] status: 0 newState: 0
D/BluetoothGatt(18415): close()
D/BluetoothGatt(18415): unregisterApp() - mClientIf=16
D/BluetoothGatt(18415): onClientConnectionState() - status=22 clientIf=14 device=#
D/FlutterBluePlugin(18415): [onConnectionStateChange] status: 22 newState: 0

# 2nd close call here (follows here immediately, without any other interaction with device or flutter blue)

D/BluetoothGatt(18415): close()
D/BluetoothGatt(18415): unregisterApp() - mClientIf=14

# now again a connect - now its fine

D/BluetoothManager(18415): getConnectedDevices
D/BluetoothGatt(18415): connect() - device: #, auto: false
D/BluetoothAdapter(18415): isSecureModeEnabled
D/BluetoothGatt(18415): registerApp()
D/BluetoothGatt(18415): registerApp() - UUID=#
D/BluetoothGatt(18415): onClientRegistered() - status=0 clientIf=14
D/BluetoothGatt(18415): onClientConnectionState() - status=0 clientIf=14 device=#
D/FlutterBluePlugin(18415): [onConnectionStateChange] status: 0 newState: 2
D/BluetoothGatt(18415): discoverServices() - device: #
D/BluetoothGatt(18415): onConnectionUpdated() - Device=# interval=6 latency=0 timeout=500 status=0
D/BluetoothGatt(18415): onSearchComplete() = Device=# Status=0
D/FlutterBluePlugin(18415): [onServicesDiscovered] count: 4 status: 0
D/BluetoothGatt(18415): setCharacteristicNotification() - uuid: # enable: true
D/BluetoothGatt(18415): onConnectionUpdated() - Device=# interval=39 latency=0 timeout=500 status=0

@khainke
If a device loses power or is out of range I get a disconnect event because I listen to the DeviceState stream. Your Flutter Device polls the connection to your Bluetooth Device every fixed period. For me the disconnect event is triggered between 2-4 seconds after the Bluetooth Device loses power.

When I disconnect I always call await device.disconnect();
I also do this when the disconnect is not initiated by the user. So for when the Bluetooth Device I am connected to suddenly turns off I get a disconnect event and then I call a custom disconnect method in which I clean my code and make the device.disconnect call.

So device.disconnect does not (always) fix the issue. It really seems to be all about the BluetoothGatt not closing before connecting again.

When I disconnect I always call await device.disconnect();
I also do this when the disconnect is not initiated by the user. So for when the Bluetooth Device I am connected to suddenly turns off I get a disconnect event and then I call a custom disconnect method in which I clean my code and make the device.disconnect call.

We do it probaby very similar, i also call a disconnect and cleanup after a disconnected event, the log i posted just tries to highlight, that your observation also occurs at my side (but with me not using automatic reconnect, but rather on a connection loss and new connect). The control flow in the log is:
connect -> normal -> disconnect call -> disconnected event + cleanup ->
connect -> normal -> power out -> (some time) -> disconnected event + cleanup (here no gatt close call) ->
connect -> bugged -> disconnect call -> disconnected event + cleanup (multiple close outputs) ->
connect -> now sometimes normal

I agree that the problem probably lies in the BluetoothGatt not getting closed on a disconnected event, but rather only at a disconnected event after a disconnect call.

As @andersunloc has stated here, this issue occurs if device.disconnect() is called on a device that has already reached a DISCONNECTED state, either by a power loss, or triggered externally by the peripheral (nRF Connect).

I am able to replicate this with the following steps:

  1. Modify the example code to have static DISCONNECT and CONNECT buttons in the AppBar of DeviceScreen.

  2. Connect to a peripheral, discover services, and subscribe to notifications on a characteristic.

  3. Using nRF Connect, disconnect from the device

  4. Click Disconnect on the flutter app.

  5. Perform step 2 and notice duplicate notifications.

I'll have another fix coming soon.

I will also be opening another issue to consider renaming device.disconnect() to device.close()/device.cancelConnection(), since in BLE a device that is under active use may drop into and out of a disconnected state, and the current naming could lead to some confusion.

@Fleximex @GauravPatni @andersunloc @khainke @MrPello @votruk @jtrovato Before I publish another version, please try out the fixes by adding the github repo to your pubspec like so:

dependencies:
  flutter_blue:
    git:
      url: git://github.com/pauldemarco/flutter_blue.git

Fix works on my side, @andersunloc well done spotting the code error, @pauldemarco thanks for the fix, and thanks to all that helped to resolve the issue.

It seems to work for me. After I disconnect and I immediately connect again. Between the disconnected event and the connect call the GattServer is now closed where it would normally not (or if I didn't connect after disconnect it would take a few seconds):

I/flutter (19589): EVENT:  BluetoothDeviceState.disconnected
D/BluetoothManager(19589): getConnectionState()
D/BluetoothManager(19589): getConnectedDevices
D/BluetoothGatt(19589): cancelOpen() - device: E9:79:D3:F5:CF:82
D/BluetoothGatt(19589): close()
D/BluetoothGatt(19589): unregisterApp() - mClientIf=11
D/BluetoothManager(19589): getConnectedDevices
D/BluetoothGatt(19589): connect() - device: E9:79:D3:F5:CF:82, auto: false

No duplicate notifies! Hooray 👍

@pauldemarco Seems to work for me as well!

@pauldemarco I cannot reproduce the bug! Great work, thanks!

@pauldemarco No duplicate notification issue :+1:
Thanks @andersunloc for your efforts :1st_place_medal:

@pauldemarco if you are going to release the new version, please try to add a feature for https://github.com/pauldemarco/flutter_blue/issues/349#issue-482808285

Or any other bug/pull request :)

Do you have support for flutter_blue 0.5.0? @pauldemarco

Before I publish another version, please try out the fixes by adding the github repo to your pubspec

Can you please publish a new version? I think they tested it and it works. @pauldemarco

Hi, there!
Still have duplicated characteristics with v0.6.3+1 and v0.7 as well.
I've spent a lot of time on this issue. I need help if you are welcome.

Android (Pixel 3a XL, 10)

Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel dev, v1.16.1, on Mac OS X 10.15.4 19E266, locale en-UA)
[✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
[✓] Xcode - develop for iOS and macOS (Xcode 11.3.1)
[✓] Chrome - develop for the web
[✓] Android Studio (version 3.6)
[✓] Connected device (4 available)
• No issues found!

_### Steps to reproduce:_

1)Scan for devices...

[AIRBOX] BluetoothState = BluetoothState.on
[AIRBOX] isScanning = true
D/BluetoothAdapter( 4005): isLeEnabled(): ON
D/BluetoothLeScanner( 4005): onScannerRegistered() - status=0 scannerId=9 mScannerId=0
D/BluetoothAdapter( 4005): isLeEnabled(): ON
[AIRBOX] isScanning = false

2)Find target device and connect...

D/BluetoothGatt( 6988): connect() - device: #, auto: false
D/BluetoothGatt( 6988): registerApp()
D/BluetoothGatt( 6988): registerApp() - UUID=#
D/BluetoothGatt( 6988): onClientRegistered() - status=0 clientIf=9
D/BluetoothGatt( 6988): onClientConnectionState() - status=0 clientIf=9 device=#
D/FlutterBluePlugin( 6988): [onConnectionStateChange] status: 0 newState: 2
[AIRBOX] STATE BluetoothDeviceState.connected
D/BluetoothGatt( 6988): discoverServices() - device: #
[AIRBOX] MTU size is 20
D/BluetoothGatt( 6988): onConnectionUpdated() - Device=# interval=6 latency=0 timeout=500 status=0
D/BluetoothGatt( 6988): onSearchComplete() = Device=# Status=0
D/FlutterBluePlugin( 6988): [onServicesDiscovered] count: 3 status: 0
D/BluetoothGatt( 6988): configureMTU() - device: # mtu: 185
D/BluetoothGatt( 6988): onConnectionUpdated() - Device=# interval=36 latency=0 timeout=500 status=0
D/BluetoothGatt( 6988): onConfigureMTU() - Device=# mtu=185 status=0
D/FlutterBluePlugin( 6988): [onMtuChanged] mtu: 185 status: 0
D/BluetoothGatt( 6988): setCharacteristicNotification() - uuid: # enable: true
D/FlutterBluePlugin( 6988): [onDescriptorWrite] uuid: # status: 0

3)Write/read data...

[AIRBOX] sent: Command.mqtt_get
D/FlutterBluePlugin( 6988): [onCharacteristicWrite] uuid: # status: 0
D/FlutterBluePlugin( 6988): [onCharacteristicChanged] uuid: #
[AIRBOX] consume: Command.mqtt_get
[AIRBOX] {"status":"ok","command":"mqtt_get","payload":{"uri":"","user":"","path":"","port":0}}

4)Disconnect...

D/BluetoothGatt( 6988): cancelOpen() - device: #
D/BluetoothGatt( 6988): onClientConnectionState() - status=0 clientIf=9 device=#
D/FlutterBluePlugin( 6988): [onConnectionStateChange] status: 0 newState: 0
D/BluetoothGatt( 6988): close()
D/BluetoothGatt( 6988): unregisterApp() - mClientIf=9
[AIRBOX] STATE BluetoothDeviceState.disconnected

And when I repeat steps 2) & 3), - I have duplicated data..

[AIRBOX] sent: Command.mqtt_get
D/FlutterBluePlugin( 6988): [onCharacteristicWrite] uuid: # status: 0
D/FlutterBluePlugin( 6988): [onCharacteristicChanged] uuid: #
[AIRBOX] consume: Command.mqtt_get
[AIRBOX] {"status":"ok","command":"mqtt_get","payload":{"uri":"","user":"","path":"","port":0}}
[AIRBOX] {"status":"ok","command":"mqtt_get","payload":{"uri":"","user":"","path":"","port":0}}

connect() method:

void connect(BluetoothDevice device) async {
    await _device.connect(autoConnect: false);

    _stateSubscription = _device.state.listen((state) async {
      if (state == BluetoothDeviceState.disconnected) {
        await _stateSubscription.cancel();
        _stateSubscription = null;
      }

      if (state == BluetoothDeviceState.connected) {
        _mtuSubscription = _device.mtu.listen((mtu) async {
          await notifyCharacteristic.setNotifyValue(true);
        });

        _servicesSubscription = _device.services.listen((services) async {
          var service = services.first;
          notifyCharacteristic = service.characteristics.firstWhere((c) => c.uuid.toString().toUpperCase() == TX_NOTIFY_CHARACTERISTIC_UUID);
          writeCharacteristic = service.characteristics.firstWhere((c) => c.uuid.toString().toUpperCase() == RX_WRITE_CHARACTERISTIC_UUID);

          _notifySubscription = notifyCharacteristic.value.listen((value) {
            //Proceed with data...
          });
          _device.requestMtu(PREFERRED_MTU_SIZE);
        });

        _device.discoverServices();
      }
    });
  }

disconnect() method

Future<void> disconnect() {
    return _device.disconnect().then((value) => _clearResources());
  }

Future<void> _clearResources() async {
    await _mtuSubscription.cancel();
    _mtuSubscription = null;
    await _servicesSubscription.cancel();
    _servicesSubscription = null;
    await _notifySubscription.cancel();
    _notifySubscription = null;
    notifyChannel = null;
    writeChannel = null;
  }

@pauldemarco

@Vizhan I've created a new issue for this since from my initial look, it's likely a different cause.
https://github.com/pauldemarco/flutter_blue/issues/525

I have the same problem in 0.8 version and I made a workaround for this:

...
List<String> listenList = [];
...

void initState() {
    super.initState();
    listenList = [];

  }
....
if (!listenList.contains(device.device.id.toString())) {
        listenList.add(device.device.id.toString());
        c.value.listen( value => {
        ....
        // Do listening stuff
       }
}
Was this page helpful?
0 / 5 - 0 ratings