BACtrack Plugin

A Flutter plugin that wraps the BACtrack SDK for both iOS and Android. This plugin will allow you to collect breathalyzer samples from a BACtrack Bluetooth breathalyzer.

BACtrack Screen Shot


The following features of the BACtrack SDK are supported at this time by the plugin.

  • :white_check_mark: Support for connecting to nearest breathalyzer (with or without a timeout)
  • :white_check_mark: Mannually disconnect from the breathalyzer
  • :white_check_mark: Take a BAC reading from the breathalyzer including the countdown, blowing, analysis, and results phases.
  • :white_check_mark: Get battery voltage level of breathalyzer
  • :white_check_mark: Permissions are automatically managed by the plugin

:warning: Note: this plugin is written in Kotlin and Swift.

Getting Started

You are required to obtain an API key in order to use the BACtrack SDK to connect to a BACtrack breathalyzer. API keys are freely available by registering on the BACtrack developer site.


The following permissions must be declared in the manifest section of your application's manifest file in order to use the BACtrack SDK.


    <!-- ... -->

    <uses-permission android:name="android.permission.BLUETOOTH" />
    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />


On iOS, you only need to declare the Bluetooth permission in your Info.plist file in order to use the BACtrack SDK.

    <!-- ... -->
    <string>This application needs access to Bluetooth for breathalyzer readings</string>
    <!-- ... -->


The first thing to do is to get an instance of the plugin.

try {
  final apiKey = insertYourApiKeyHere;
  _plugin = await FlutterBactrack.instance(apiKey);
  // Init succeeded!
} on PlatformException catch (e) {
  // Init failed!

Note: If you are concerned about the security of your API key, you may want to retrieve it from your server rather than put it in your source code. Once you have received it from your server, you can easily store it securely on your device for future runs using the flutter_secure_storage plugin.

BACtrack Status Notifications

The BACtrack SDK is designed to return all notifications through a callback interface. For Flutter, this has been abstracted to a Dart Stream. The plugin's statusStream will return all of the notifications from the SDK in the form of a BACtrackStatus object. This object wraps a BACtrackState enum that identifies the callback that was invoked as well as a message String containing the argument, if any, that was passed to the native callback function. Check the API documentation for a list of which states contain messages.

Taking a Breathalyzer Sample

The following code will connect to the nearest breathalyzer and start a sample collection once it has received the BACtrackState.connected state.

try {
    .where((BACtrackStatus status) => status.state == BACtrackState.connected)
      (BACtrackStatus status) {
         _plugin.startCountdown().then((bool result) => /* true if success in starting countdowwn */);

} on PlatformException catch (e) {
  // errors reported here

The above code sets up a listener on the plugin's statusStream that filters out everything except the connected message. Then it calls the plugin to connect to the nearest breathalyzer. Once the connected status shows up on the statusStream, the plugin's startCountdown method is called to begin the process of taking a BAC sample.

That example is a bit simplistic. In reality you would want to listen for more of the status updates that can be emitted by the statusStream in response to a connectToNearestBreathalyer() call. For example, any of the following status states maybe be emitted when you connect to a breathalyzer:

  • BACtrackState.apiKeyAuthorized
  • BACtrackState.apiKeyDeclined
  • BACtrackState.didConnect
  • BACtrackState.connected
  • BACtrackState.connectionTimeout

Check the API documentation for details on which status updates can be emitted in response to each of the plugin's method calls.

:warning: Note: It is possible to receive a BACtrackState.disconnected status at any time after you have received a BACtrackState.connected status.

Future Work

The plugin does not currently support manual scanning for breathalyzers so that you can manually choose which one to connect. This functionality is planned for a future release of the plugin.

Getting Help

If you have any questions or problems with the plugin, please file an issue at the GitHub repository. I don't work on this full time, but I'll try to offer whatever help I can.

:beetle: :ant: PRs are welcome! :honeybee: :bug: