Query Android usage statistics from Flutter: app usage and events, configuration
changes, app standby buckets, on-device storage usage, and per-app network data.
The plugin wraps Android's UsageStatsManager, NetworkStatsManager,
StorageStatsManager, and PackageManager behind a single UsageStats facade.
This package is Android only. iOS does not expose comparable per-app usage data to third-party apps, so the plugin declares no iOS implementation.
The bundled example app showcases every API:
Add usage_stats to your pubspec.yaml:
dependencies:
usage_stats: ^2.0.0The plugin targets API level 23 (Android 6.0) as a minimum. Most queries need the
special PACKAGE_USAGE_STATS permission, which the user grants from system
settings rather than through a runtime dialog.
Add the permission to your android/app/src/main/AndroidManifest.xml, inside the
manifest element:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
<application ...>Send the user to the system "Usage access" screen and check whether access has been granted:
import 'package:usage_stats/usage_stats.dart';
// Opens the system Usage Access settings screen (only if not yet granted).
await UsageStats.grantUsagePermission();
// Opens the same screen unconditionally, e.g. for a "Manage access" button.
await UsageStats.openUsageAccessSettings();
// Returns true once the user has enabled access for your app.
bool? granted = await UsageStats.checkUsagePermission();final endDate = DateTime.now();
final startDate = endDate.subtract(const Duration(days: 1));
// Raw usage events (foreground/background transitions, screen on/off, …).
List<EventUsageInfo> events = await UsageStats.queryEvents(startDate, endDate);
// Per-app foreground usage.
List<UsageInfo> usage = await UsageStats.queryUsageStats(startDate, endDate);
// Per-app usage aggregated by package, keyed by package name.
Map<String, UsageInfo> aggregated =
await UsageStats.queryAndAggregateUsageStats(startDate, endDate);
// Configuration (locale/orientation/…) statistics.
List<ConfigurationInfo> configs =
await UsageStats.queryConfiguration(startDate, endDate);
// Aggregated event statistics (Android 9 / API 28+).
List<EventInfo> eventStats =
await UsageStats.queryEventStats(startDate, endDate);queryUsageStats and queryEventStats accept an optional intervalType that
maps to UsageStatsManager.INTERVAL_*. It defaults to IntervalType.best.
final monthly = await UsageStats.queryUsageStats(
startDate,
endDate,
intervalType: IntervalType.monthly,
);// Per-app network usage for all apps.
List<NetworkInfo> network = await UsageStats.queryNetworkUsageStats(
startDate,
endDate,
networkType: NetworkType.all, // or NetworkType.wifi / NetworkType.mobile
);
// Network usage for a single package.
NetworkInfo info = await UsageStats.queryNetworkUsageStatsByPackage(
startDate,
endDate,
packageName: 'com.example.app',
);Network totals are summed across the Wi-Fi and cellular transports. When a VPN is active the platform may attribute traffic to the VPN app as well as the originating app, which can make per-app totals appear higher than expected.
// App, data, and cache bytes for a package (Android 8 / API 26+).
StorageInfo? storage = await UsageStats.queryStorageStats('com.example.app');
print(storage?.totalBytes);Querying your own package needs no permission; querying other packages requires
PACKAGE_USAGE_STATS.
// Label, system flag, version, and install times for one package.
AppInfo? app = await UsageStats.getAppInfo('com.example.app');
// Every installed app the caller can see (set includeSystem: false to skip
// system apps).
List<AppInfo> installed =
await UsageStats.queryInstalledApps(includeSystem: false);
// PNG launcher icon bytes.
Uint8List? icon = await UsageStats.getAppIcon('com.example.app');On Android 11+ (API 30) package-visibility rules apply. Without a <queries>
entry in your manifest, queryInstalledApps returns only visible packages. The
plugin deliberately does not request QUERY_ALL_PACKAGES, since it is a
Play-policy sensitive permission.
// This app's standby bucket (no permission required, Android 9 / API 28+).
int? bucket = await UsageStats.getAppStandbyBucket();
// Whether a package is currently idle (Android 6 / API 23+).
bool? idle = await UsageStats.isAppInactive('com.example.app');
// This app's own events without the usage-access prompt (Android 9 / API 28+).
List<EventUsageInfo> ownEvents =
await UsageStats.queryEventsForSelf(startDate, endDate);For backwards compatibility the model fields are returned as strings, mirroring the raw platform values. Every model also exposes typed getters so you do not have to parse them yourself:
final info = usage.first;
info.totalTimeInForegroundMs; // int? (milliseconds)
info.lastTimeUsedDate; // DateTime?
events.first.eventTypeDescription; // e.g. 'ACTIVITY_RESUMED'
network.first.rxTotalBytesValue; // int?totalTimeInForeground is reported in milliseconds.
| Method | Min API | Permission |
|---|---|---|
queryUsageStats, queryEvents, queryConfiguration, queryAndAggregateUsageStats |
23 | PACKAGE_USAGE_STATS |
queryEventStats |
28 | PACKAGE_USAGE_STATS |
queryNetworkUsageStats, queryNetworkUsageStatsByPackage |
23 | PACKAGE_USAGE_STATS |
queryStorageStats |
26 | own package: none; others: PACKAGE_USAGE_STATS |
getAppStandbyBucket, queryEventsForSelf |
28 | none |
isAppInactive |
23 | PACKAGE_USAGE_STATS for other packages |
getAppInfo, queryInstalledApps, getAppIcon |
21 | none (subject to package visibility) |
API-gated methods degrade gracefully on older devices, returning null or an
empty list rather than throwing.
The Dart code is linted with flutter analyze against
flutter_lints. The Android Kotlin
sources are checked with detekt and
ktlint:
./tool/lint_kotlin.sh


