battery_kit
On-demand battery level and charging state for DartNative apps on iOS and Android.
- iOS reads
UIDevice; Android reads the stickyACTION_BATTERY_CHANGEDbroadcast. Both are called over FFI in a single native round trip. - No extra permissions, no background work, no persistent listeners.
- An unavailable level is reported as
null, never silently as0.
Install
battery_kit is published on
dartpub.dev. Add a plain version
dependency to your app's pubspec.yaml; it resolves the same way the
DartNative framework itself does:
dependencies:
battery_kit: ^0.1.0
Then fetch it with:
dn pub get
The generated dartnative_plugin_registrant.dart loads the FFI symbols; call
DartNativePluginRegistrant.registerAll() in main() as usual.
Android requires minSdk 26 or higher; iOS requires iOS 15 or higher.
Usage
import 'package:battery_kit/battery_kit.dart';
final battery = Battery();
print(await battery.level); // 0..100, throws BatteryUnavailableException
print(await battery.state); // BatteryState.charging, ...
print(await battery.isCharging); // false for full / notCharging
// One read for both values:
final info = await battery.info;
print(info.level); // null when the OS cannot report a percentage
Public API
| Member | Description |
|---|---|
Battery.level |
Future<int> from 0 to 100. Throws BatteryUnavailableException when unknown. |
Battery.state |
Future<BatteryState>: unknown, charging, discharging, full, notCharging. |
Battery.isCharging |
Future<bool>; full and notCharging are false. |
Battery.info |
Future<BatteryInfo> with level (int?) and state from the same OS reading. |
notCharging is Android's "connected but not charging" state. iOS never
reports it. Simulators and emulators may report an unknown level.
On iOS 17 and later the system rounds UIDevice.batteryLevel to the nearest
5%, so level can differ from the status bar by up to 2 points (for example
89% is reported as 90). This is an OS limitation, not something the plugin
can work around.
On iOS, battery monitoring is enabled only for the duration of the read and restored to its previous setting afterwards. Continuous monitoring is out of scope for this version.
How it works
Read in this order:
lib/src/battery.dart– public API and result typeslib/src/battery_ffi_bindings.dart– Dart side of the FFI callios/Classes/DNBattery.swift– iOS implementationandroid/src/main/kotlin/io/github/saitojo1106/battery_kit/DNBatteryBridge.kt– Android implementationandroid/src/main/cpp/dn_battery.cpp– JNI glue between Dart and Kotlin
The native side returns one integer, state * 256 + (level + 1). A zero low
byte means the level is unavailable. No strings or callbacks cross the FFI
boundary.
Example
example/ is a dn create app that depends on this plugin via path: ../.
cd example
dn pub get
dn run -d <device-id>
The screen shows the current level and state with a refresh button.
Tested on
| Platform | Device | Result |
|---|---|---|
| Android 16 | Pixel 8a | Level and state match the OS, including full and notCharging. |
| iOS 18.7 | iPhone 16e | Level matches the OS within the 5% rounding above; charging and discharging follow the cable. |
Verified 2026-09-15 with DartNative SDK 3.45.0. Run dart test for the
decoding tests.
License
MIT. See LICENSE.