Rating: 4.52 / 5 Based on 44 reviews
As the number of smart devices grows, so does the need to control them. For multiple use cases, expecting a device to be connected to the internet while there is a need to exercise control would be highly limiting and thus infeasible. In these cases, using Bluetooth Low Energy (also known as Bluetooth LE, or BLE) seems to be the best choice due to its low power needs, ubiquity in our phones, and the lack of a need to be connected to a broader network. As such, the demand for Bluetooth apps has also been growing.
By reading this article, you will find out how to begin developing a Bluetooth Low Energy app in Flutter.
Generally speaking, Bluetooth is a frequency-hopping wireless technology that transmits data packets within the 2.4 GHz band to interconnect nearby devices. Bluetooth Low Energy is a variant of Bluetooth that also operates in the 2.4 GHz band, but unlike standard Bluetooth, it maxes out at just 1 Mbps while consuming just 0.01 to 0.5 watts. It's one-third of the speed of standard Bluetooth. As a result, its power consumption is much lower.
Lower power consumption is possible because BLE remains in sleep mode unless a connection initiates. The actual connection times only last a few milliseconds, unlike standard Bluetooth, which connects for a few seconds or a few hours at a time. Bluetooth's primary goal is to continuously stream data applications, while Bluetooth Low Energy is designed for apps that don't need to exchange large amounts of data.
Before writing any Bluetooth-related code, choosing and understanding an underlying library is necessary.
Currently, there are three BLE libraries present on the pub that might look promising at first glance: flutter_blue, flutter_ble_lib, and flutter_reactive_ble.
They all have nearly the same feature sets but differ greatly in terms of actual usability.
1. Flutter_blue
The last commit on GitHub was 9 months ago (as of December 2021), with 500 open issues and multiple major bugs. The library does not seem to be maintained, so choosing this library would hinder the development of your app greatly.
2. Flutter_BLE_lib
The library is almost complete and features an optional device emulator, which greatly helps with testing. Almost all of the features work flawlessly, but as with all software, some minor bugs are present. Sadly, while the library is still maintained, its development progresses at a glacial pace, and the library is still not null-safe. Choosing this library would not be optimal unless you really needed fully automatic Bluetooth testing in your development process.
3. Flutter_reactive_BLE
This library seems like the best choice since it supports everything the other libraries did, sans the emulator, and is actively maintained. The risk that it will be abandoned in the near future is low, as it is being developed by the Philips Hue team.
Now that we've chosen a library, knowledge of the underlying protocol is necessary. The BLE standard is well documented, but it is also highly complicated. The following explanation tries to abstract away as much as possible to get you to understand enough to be able to communicate with your device without having to understand all of the specifics.
In pretty much all situations, your phone will function as a BLE master, while the peripherals you connect the phone with will be BLE slaves. A master can connect to multiple peripherals, while the peripheral can only be connected to a single master.
To do anything with a peripheral, you must first connect to it. Most of the time, you will need to start scanning for BLE devices, and either automatically connect to a device selected by a criterion or let the user connect to their desired device.
When you scan for peripherals, you will receive their IDs, names, and a list of services they provide.
Note that during the connection, you have an option to negotiate an MTU with the device, which changes the maximum size of the data you can send and receive.
Bluetooth Low Energy peripheral has to have a GATT (the Generic Attribute Profile) client, which defines the way that two Bluetooth Low Energy devices transfer data back and forth and provide access to Services and Characteristics.
Services are groups of characteristics identifiable by GUIDs, and Characteristics are the nodes we, as app developers, will care about. Note that characteristics are always grouped in a service.
Characteristics are the objects you read, write, or listen to (using notify or indicate).
They are identifiable by a GUID and always possess at least one descriptor. Descriptors are used to store metadata about the characteristic. If the bit corresponding to Read is set to 1 in a descriptor, the characteristic is readable. The same follows for Write, Notify, and so on. Most libraries will expose methods like characteristic.isReadable() to abstract away the need to use the descriptor bitfields.
If a characteristic is writeable with the response (where the response is limited to an acknowledgment response), calling an equivalent of characteristic.writeWithResponse(value) will change the corresponding value on the slave and give you the success status. The operations for reading and writing without response are analogous.
If you need to react to characteristic value changes in your app, you will need to subscribe to the characteristic with an equivalent of characteristic.subscribe(). This will tell the peripheral that it should send you data on characteristic value changes.
To react to value changes, listen to the relevant dart:Stream.
Bluetooth Low Energy provides the ability to listen to Notifications and Indications, which only differ in that a Notification is unacknowledged while an Indication is. These also have their corresponding isNotifiable/isIndicatable flags in the characteristic descriptor.
Once you understand the possible operations, you can do everything you would do with any other data source, except you're operating on byte data. You might want to write a repository class, which will convert byte data to well-typed data models for ease of use.
This knowledge, paired with reading the documentation of your library of choice, should be sufficient for all communications with a Bluetooth Low Energy peripheral in a perfect world. Sadly, mistakes happen, and the underlying device BLE modules might not work as smoothly as we would like them to. Where should one begin to look for the sources of problems once they start appearing?
A good place to start is connection instability. Because Bluetooth Low Energy antennas are low-powered, a connection can easily be lost at any moment. While writing your logic code, keep in mind that any single BLE operation can very easily fail at any time.
A common source might be a misunderstanding between your peripheral firmware provider. Use a separate Bluetooth Low Energy app to verify that all flags necessary for operation are present and check that all GUIDs match.
Libraries themselves might also be sources of problems. For example, flutter_BLE_lib often fails on fast concurrent writes and completes the future responsible for connecting to the device a bit too early. While debugging, you might want to add some delay before and after Bluetooth operations.
I hope that this article will help you write your Bluetooth Low Energy Flutter app. It’s definitely worth using BLE technology because of its low cost, ease of deployment, and positive impact on the long battery life of consumer electronics that can be connected to Internet-based applications.
If you plan to build an application that uses BLE, LeanCode developers can help you with its implementation. See our Case Study about the mobile application STERYLIS - an Internet-connected device management app, and STERYLIS Pure - a Bluetooth companion app to the Client’s sterilizing devices.
You might also be interested in reading our article about Feature-Based Flutter App Architecture.