flutter_antplus
A powerful, high-performance Flutter plugin for connecting to ANT+ devices on Android using the official ANT-Android-SDKs. It allows seamless scanning, connection, and data acquisition from multiple fitness sensors like heart rate monitors, bike power meters, and speed/cadence sensors.
🚀 Features
- Heart Rate Monitor (HRM)
- Real-time Heart Rate (bpm) stream
- Bike Power Meter
- Real-time Power (watts)
- Real-time Cadence (rpm)
- Pedal Balance (%)
- Left & Right Pedal Smoothness (%)
- Left & Right Torque Effectiveness (%)
- Battery status tracking
- Cadence Sensor
- Real-time Cadence (rpm)
- Common API (
AntplusDeviceViewModel)- Universal start/stop scan interface
- State monitoring (
onDeviceStateChangeStream) - Subscriptions status tracking (
onRequestAccessResultStream)
- Plugin Logging
- Integrated debug logging through the
AntplusLoggingsingleton
- Integrated debug logging through the
🛠️ Requirements
-
Ant+ USB Stick Receiver Adapter like this
-
USB C to USB adapter like this
-
Install ANT Radio Service and ANT+ Plugins from the Play Store
🛠️ Android Setup
To run ANT+ communication on Android (especially on Android 11+ / API 30+), you must declare package visibility for the ANT+ background services.
Add the following inside the <manifest> tag (outside <application>) in your android/app/src/main/AndroidManifest.xml:
<queries>
<!-- Declares dependency on the ANT+ Plugin Service -->
<package android:name="com.dsi.ant.plugins.antplus" />
<!-- Declares dependency on the ANT Radio Service -->
<package android:name="com.dsi.ant.service.socket" />
<intent>
<action android:name="com.dsi.ant.plugins.antplus.queryalreadyconnecteddevices" />
</intent>
</queries>
⚡ Quick Start
A comprehensive example app can be found in example.
1. Initialize Logging (Optional)
It is recommended to initialize the logging interface at app startup:
import 'package:flutter_antplus/antplus_logging.dart';
void main() {
WidgetsFlutterBinding.ensureInitialized();
AntplusLogging.instance.initLog();
runApp(const MyApp());
}
2. Scanning and Connecting
Each device type has its own view model implementing the AntplusDeviceViewModel interface:
AntplusHeartrateViewModel.instanceAntplusBikepowerViewModel.instanceAntplusCadenceViewModel.instance
Start Scanning
final hrViewModel = AntplusHeartrateViewModel.instance;
// Listen to scan results
final scanSubscription = hrViewModel.onScanResultStream.listen((device) {
print('Found device: ${device.name} (Number: ${device.number})');
});
// Start the scan
await hrViewModel.startScan();
Connect to a Device
When a device is discovered, you can connect to it using its unique deviceNumber:
// Stop scan before connecting (best practice)
await hrViewModel.stopScan();
// Connect using device number
await hrViewModel.connect(device.number);
Monitor Device Connection State
final stateSubscription = hrViewModel.onDeviceStateChangeStream.listen((state) {
print('Device state changed to: ${state.name}');
// States: dead, closed, searching, tracking, processingRequest, unrecognized
});
📊 Reading Sensor Data
💓 Heart Rate
final hrSubscription = AntplusHeartrateViewModel.instance.onHeartRateDataStream.listen((bpm) {
print('Heart Rate: $bpm bpm');
});
🚴 Bike Power
The bike power view model offers multiple specialized telemetry streams:
final powerModel = AntplusBikepowerViewModel.instance;
// Real-time Power output in watts
powerModel.onPowerDataStream.listen((watts) {
print('Power: $watts W');
});
// Cadence from the power meter
powerModel.onCadenceDataStream.listen((rpm) {
print('Cadence: $rpm rpm');
});
// Left/Right Pedal Balance
powerModel.onBalanceDataStream.listen((balance) {
print('Balance: $balance%');
});
// Pedal Smoothness
powerModel.onPedalSmoothnessDataStream.listen((data) {
print('Smoothness - Left: ${data.leftOrCombinedPedalSmoothness}%, Right: ${data.rightPedalSmoothness}%');
});
// Torque Effectiveness
powerModel.onTorqueEffectivenessDataStream.listen((data) {
print('Torque - Left: ${data.leftTorqueEffectiveness}%, Right: ${data.rightTorqueEffectiveness}%');
});
// Battery status of the power meter sensor
powerModel.onBatteryStatusDataStream.listen((status) {
print('Battery Status: ${status.name}');
});
🔄 Cadence
final cadenceSubscription = AntplusCadenceViewModel.instance.onCadenceDataStream.listen((rpm) {
print('Cadence: $rpm rpm');
});
🛠️ For developers
Pigeon Code Generation
The pigeon definition files are located in the pigeons/ folder. If you edit these definitions, regenerate the pigeon platform channel implementations using the following commands:
# Bike Power
dart run pigeon --input pigeons/bikepower.dart
dart run pigeon --input pigeons/bikepower_event_channel.dart
# Heart Rate
dart run pigeon --input pigeons/heartrate.dart
dart run pigeon --input pigeons/heartrate_event_channel.dart
# Cadence
dart run pigeon --input pigeons/cadence.dart
dart run pigeon --input pigeons/cadence_event_channel.dart
# Logging
dart run pigeon --input pigeons/logging_event_channel.dart
# Device
dart run pigeon --input pigeons/device_event_channel.dart
📄 License
This project is licensed under the BSD-3-Clause License - see the LICENSE file for details.
Libraries
- antplus_logging
- flutter_antplus
- A Flutter plugin for ANT+ integration on Android, supporting Heart Rate Monitors, Bike Power Meters, and Cadence sensors.