Search code examples
androidflutterdartbluetoothplatform-specific

Bluetooth devices found in an Android app, but not in a platform specific code in Flutter


I'm trying to scan, connect and receive data from a Bluetooth module. Everything works fine if I just use an android application. I can scan and find all nearby devices, connect to anyone (I'm only interested in my Bluetooth module) and I am able to read the test data that's being sent from the Bluetooth module.

The problem is that the application is being developed using Flutter. I used the same code from my Android application and linked it with Dart though the EventsChannel, but now I can only see fewer Bluetooth devices in the Flutter app and none of them is the Bluetooth Module I'm interested in. I'm new to Flutter and the platform specific coding, I can't understand why the same code behaves differently in different apps on same the hardware.

I've tested my code on Samsung S4 and S8 phones and the result is the same.

This is the code for the EventChannel for the discovery part:

new EventChannel(flutterEngine.getDartExecutor(), DISCOVER_CHANNEL).setStreamHandler(
        new EventChannel.StreamHandler() {
          @Override
          public void onListen(Object args, EventChannel.EventSink events) {
            Log.w(TAG, "Registering receiver");
            discoverReceiver = DiscoverReceiver(events);
            IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
            registerReceiver(discoverReceiver, filter);
          }

          @Override
          public void onCancel(Object args) {
            Log.w(TAG, "Unregistering receiver");
            unregisterReceiver(discoverReceiver);
            discoverReceiver = null;
          }
        }
);

For now my discoverReceiver is a global BroadcastReceiver. Below is the code for the Broadcastreceiver:

private BroadcastReceiver DiscoverReceiver(final EventSink events) {
  return new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
      String action = intent.getAction();
      if (BluetoothDevice.ACTION_FOUND.equals(action)) {
        // Discovery has found a device. Get the BluetoothDevice
        // object and its info from the Intent.
        BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
        String deviceName = device.getName();

        if (deviceName == null){
          deviceName = "No Device Name";
        }

        events.success(deviceName);
        Log.w(TAG, "Sending " + deviceName);
      } 
    }
  };
}

**I used the (Log.w(TAG, "Sending " + deviceName);) statement to see if events were being lost/dropped. And below is how I receive it in Dart:

@override
  void initState() {
    super.initState();
    devices.add(selectDevice);
    discoverChannel.receiveBroadcastStream().listen(onEvent);
  }

  void onEvent(Object event) {
    setState(() {
      devices.add(event);
    });
  }

Below is the code in my Android app that can scan and find all devices in case you want to compare with the above:

private BroadcastReceiver DiscoverReceiver() {
    return new BroadcastReceiver() {
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            // Discovery has found a device. Get the BluetoothDevice
            // object and its info from the Intent.
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            String deviceName = device.getName();

            if (deviceName == null){
                deviceName = "No Device Name";
            }
            devicelist.add(device);
            devNames.add(deviceName);
            arrayAdapter.add(deviceName);
            arrayAdapter.notifyDataSetChanged();
            Log.w(TAG, "Sending " + deviceName);
            }
        }
    };
}

I'm not concerned with the last snippet but I just thought I'd show the complete flow. Snippet 2 is a copy of what I have in a stand-alone Android App and it scans and finds all devices, but once I use it in a Flutter App as native code for Android it stops finding the same number of devices, still finds some though and is very unreliable.

I have tried most of the Flutter bluetooth packages but none of them was what I was looking for and so I ended up going with Platform specific code, which worked fine until it was plugged to Flutter. I've read the documentation for Android development and the code above is mostly modified code from Android sample. I just can't figure out why the same code can find more devices as a stand-alone app versus using it as a native code for a flutter application if at the end it's being tested on the same hardware.

Any input will be appreciated!


Solution

  • Ok so I've finally found the solution. It is something to do with the Bluetooth's startDiscovery() running and doing its job properly but the events are captured a little later, something that the debugger would not be able to show.

    So in my case, all devices were "discovered" but the flutter app starts capturing the events later so it only shows the last 1 or 2 devices that were discovered during the discovery.

    I moved:

    discoverChannel.receiveBroadcastStream().listen(onEvent);
    

    From the initState() and into a function that is called after a button press, which makes sure everything has loaded before registering the broadcast receiver on the native side and the broadcast stream receiver on Dart's.

    I'm still not sure exactly how to express this, but it's about the timing of registering the BroadcastReceiver on the native side and the receiveBroadcastStream on Dart's side.

    Now it starts the discovery and captures the events properly, which shows the same number of devices found in an Android stand-alone app.

    Hope this helps anyone who might face this odd issue in the future.