accurate_step_counter 1.2.0 copy "accurate_step_counter: ^1.2.0" to clipboard
accurate_step_counter: ^1.2.0 copied to clipboard

PlatformAndroid

A highly accurate step counter plugin using accelerometer-based detection with low-pass filtering and peak detection. Supports foreground, background, and terminated state tracking.

Accurate Step Counter #

pub package License: MIT

A highly accurate Flutter plugin for step counting using native Android TYPE_STEP_DETECTOR sensor with accelerometer fallback. Zero external dependencies. Designed for reliability across foreground, background, and terminated app states.

✨ Features #

Feature Description
🎯 Native Detection Uses Android's hardware-optimized TYPE_STEP_DETECTOR sensor
πŸ”„ Accelerometer Fallback Software algorithm for devices without step detector
πŸ“¦ Zero Dependencies Only requires Flutter SDK
πŸ”‹ Battery Efficient Event-driven, not polling-based
πŸ“± All App States Foreground, background, and terminated state support
βš™οΈ Configurable Presets for walking/running + custom parameters

πŸ“± Platform Support #

Platform Status
Android βœ… Full support (API 19+)
iOS ❌ Not supported

Note: This is an Android-only package. It won't crash on iOS but step detection won't work.

πŸš€ Quick Start #

1. Install #

dependencies:
  accurate_step_counter: ^1.2.0

2. Add Permissions #

In android/app/src/main/AndroidManifest.xml:

<uses-permission android:name="android.permission.ACTIVITY_RECOGNITION"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_HEALTH"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>

3. Request Runtime Permission #

import 'package:permission_handler/permission_handler.dart';

// Request activity recognition (required)
await Permission.activityRecognition.request();

// Request notification (for Android 13+ foreground service)
await Permission.notification.request();

4. Start Counting! #

import 'package:accurate_step_counter/accurate_step_counter.dart';

final stepCounter = AccurateStepCounter();

// Listen to step events
stepCounter.stepEventStream.listen((event) {
  print('Steps: ${event.stepCount}');
});

// Start counting
await stepCounter.start();

// Stop when done
await stepCounter.stop();

// Clean up
await stepCounter.dispose();

πŸ“– Complete Example #

import 'package:flutter/material.dart';
import 'package:accurate_step_counter/accurate_step_counter.dart';
import 'dart:async';

class StepCounterScreen extends StatefulWidget {
  @override
  State<StepCounterScreen> createState() => _StepCounterScreenState();
}

class _StepCounterScreenState extends State<StepCounterScreen> {
  final _stepCounter = AccurateStepCounter();
  StreamSubscription<StepCountEvent>? _subscription;
  int _steps = 0;
  bool _isRunning = false;

  @override
  void initState() {
    super.initState();
    _subscription = _stepCounter.stepEventStream.listen((event) {
      setState(() => _steps = event.stepCount);
    });
  }

  Future<void> _toggleTracking() async {
    if (_isRunning) {
      await _stepCounter.stop();
    } else {
      await _stepCounter.start();
    }
    setState(() => _isRunning = !_isRunning);
  }

  @override
  void dispose() {
    _subscription?.cancel();
    _stepCounter.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Step Counter')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Text('$_steps', style: const TextStyle(fontSize: 80, fontWeight: FontWeight.bold)),
            const Text('steps', style: TextStyle(fontSize: 24, color: Colors.grey)),
            const SizedBox(height: 40),
            ElevatedButton.icon(
              onPressed: _toggleTracking,
              icon: Icon(_isRunning ? Icons.stop : Icons.play_arrow),
              label: Text(_isRunning ? 'Stop' : 'Start'),
            ),
            TextButton(
              onPressed: () {
                _stepCounter.reset();
                setState(() => _steps = 0);
              },
              child: const Text('Reset'),
            ),
          ],
        ),
      ),
    );
  }
}

βš™οΈ Configuration #

Presets #

// For walking (default)
await stepCounter.start(config: StepDetectorConfig.walking());

// For running (more sensitive, faster detection)
await stepCounter.start(config: StepDetectorConfig.running());

// Sensitive mode (may have false positives)
await stepCounter.start(config: StepDetectorConfig.sensitive());

// Conservative mode (fewer false positives)
await stepCounter.start(config: StepDetectorConfig.conservative());

Custom Configuration #

await stepCounter.start(
  config: StepDetectorConfig(
    threshold: 1.2,                // Movement threshold (higher = less sensitive)
    filterAlpha: 0.85,             // Smoothing factor (0.0 - 1.0)
    minTimeBetweenStepsMs: 250,    // Minimum ms between steps
    enableOsLevelSync: true,       // Sync with OS step counter
    
    // Foreground service options (Android ≀10)
    useForegroundServiceOnOldDevices: true,
    foregroundNotificationTitle: 'Step Tracker',
    foregroundNotificationText: 'Counting your steps...',
  ),
);

Configuration Parameters #

Parameter Default Description
threshold 1.0 Movement threshold for step detection
filterAlpha 0.8 Low-pass filter smoothing (0.0-1.0)
minTimeBetweenStepsMs 200 Minimum time between steps
enableOsLevelSync true Sync with OS step counter
useForegroundServiceOnOldDevices true Use foreground service on Android ≀10
foregroundNotificationTitle "Step Counter" Notification title
foregroundNotificationText "Tracking your steps..." Notification text

πŸ”§ API Reference #

AccurateStepCounter #

final stepCounter = AccurateStepCounter();

// Properties
stepCounter.stepEventStream       // Stream<StepCountEvent>
stepCounter.currentStepCount      // int
stepCounter.isStarted             // bool
stepCounter.isUsingForegroundService  // bool
stepCounter.currentConfig         // StepDetectorConfig?

// Methods
await stepCounter.start({config});    // Start detection
await stepCounter.stop();             // Stop detection
stepCounter.reset();                  // Reset count to zero
await stepCounter.dispose();          // Clean up resources

// Check sensor type
final isHardware = await stepCounter.isUsingNativeDetector();

// Terminated state sync (automatic, but can be manual)
stepCounter.onTerminatedStepsDetected = (steps, startTime, endTime) {
  print('Synced $steps missed steps');
};

StepCountEvent #

final event = StepCountEvent(stepCount: 100, timestamp: DateTime.now());

event.stepCount   // int - Total steps since start()
event.timestamp   // DateTime - When step was detected

πŸ—οΈ Architecture #

Overall Flow #

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                         Flutter App                              β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  AccurateStepCounter                                            β”‚
β”‚       β”œβ”€β”€ stepEventStream (real-time steps)                     β”‚
β”‚       β”œβ”€β”€ currentStepCount                                      β”‚
β”‚       └── onTerminatedStepsDetected (missed steps callback)     β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  NativeStepDetector (Dart)                                      β”‚
β”‚       β”œβ”€β”€ MethodChannel (commands)                              β”‚
β”‚       └── EventChannel (step events)                            β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                           β”‚
                    Platform Channel
                           β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     Android Native (Kotlin)                      β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  AccurateStepCounterPlugin                                      β”‚
β”‚       β”œβ”€β”€ NativeStepDetector.kt (sensor handling)               β”‚
β”‚       β”œβ”€β”€ StepCounterForegroundService.kt (Android ≀10)         β”‚
β”‚       └── SharedPreferences (state persistence)                 β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚  Android Sensors                                                β”‚
β”‚       β”œβ”€β”€ TYPE_STEP_DETECTOR (primary - hardware)               β”‚
β”‚       └── TYPE_ACCELEROMETER (fallback - software)              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Step Detection Priority #

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚            Check: TYPE_STEP_DETECTOR            β”‚
β”‚              (Hardware Sensor)                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚
           β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
           β”‚     Available?        β”‚
           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                       β”‚
          β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
          β”‚                         β”‚
    β”Œβ”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”            β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”
    β”‚    YES    β”‚            β”‚     NO      β”‚
    β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜            β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜
          β”‚                         β”‚
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Hardware Step    β”‚    β”‚  Accelerometer    β”‚
β”‚  Detection        β”‚    β”‚  + Algorithm      β”‚
β”‚                   β”‚    β”‚                   β”‚
β”‚  β€’ Best accuracy  β”‚    β”‚  β€’ Low-pass filterβ”‚
β”‚  β€’ Battery saving β”‚    β”‚  β€’ Peak detection β”‚
β”‚  β€’ Event-driven   β”‚    β”‚  β€’ Configurable   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

πŸ“± App State Coverage #

How Each State is Handled #

App State Android 11+ (API 30+) Android ≀10 (API ≀29)
🟒 Foreground Native TYPE_STEP_DETECTOR Native TYPE_STEP_DETECTOR
🟑 Background Native detection continues Foreground Service keeps counting
πŸ”΄ Terminated OS sync on app relaunch Foreground Service prevents termination
πŸ“’ Notification ❌ None (not needed) βœ… Shows (required by Android)

Important: The persistent notification only appears on Android 10 and below. On Android 11+, no notification is shown because the native step detector works without needing a foreground service.

Detailed State Behavior #

🟒 Foreground State

App Active β†’ NativeStepDetector β†’ TYPE_STEP_DETECTOR β†’ EventChannel β†’ Flutter UI
  • Real-time step counting with immediate updates
  • Hardware-optimized detection
  • Full access to all sensors

🟑 Background State

Android 11+:

App Minimized β†’ Native detection continues β†’ Steps buffered β†’ UI updates when resumed

Android ≀10:

App Minimized β†’ Foreground Service starts β†’ Persistent notification shown
                    ↓
              Keeps CPU active via WakeLock
                    ↓
              Steps counted continuously
                    ↓
              Results polled every 500ms

πŸ”΄ Terminated State

Android 11+:

App Killed β†’ OS continues counting via TYPE_STEP_COUNTER
                    ↓
             App Relaunched
                    ↓
       Compare saved count with current OS count
                    ↓
       Calculate missed steps
                    ↓
       Trigger onTerminatedStepsDetected callback

Android ≀10:

Foreground Service prevents true termination
                    ↓
       Service continues counting even if Activity destroyed
                    ↓
       No steps are ever missed

Terminated State Sync (Android 11+) #

// Automatic sync happens on start(), but you can handle it:
stepCounter.onTerminatedStepsDetected = (missedSteps, startTime, endTime) {
  print('You walked $missedSteps steps while app was closed!');
  print('From: $startTime to $endTime');
  
  // Optionally save to database or sync to server
  saveToDatabase(missedSteps, startTime, endTime);
};

πŸ”‹ Battery & Performance #

Metric Value
Detection Method Event-driven (not polling)
CPU Usage Minimal (~1-2%)
Battery Impact Low (uses hardware sensor)
Memory ~2-5 MB
Foreground Service Battery Moderate (only Android ≀10)

πŸ› Debugging #

View Logs #

# All plugin logs
adb logcat -s AccurateStepCounter NativeStepDetector StepSync

# Only step events
adb logcat -s NativeStepDetector

Check Sensor Availability #

final isHardware = await stepCounter.isUsingNativeDetector();
print('Using hardware step detector: $isHardware');

❓ Troubleshooting #

Issue Solution
Steps not detected Check ACTIVITY_RECOGNITION permission is granted
Inaccurate counts Try adjusting threshold parameter
Stops in background Enable foreground service or check battery optimization
No notification (Android ≀10) Grant notification permission

πŸ“„ License #

MIT License - see LICENSE


Made with ❀️ for the Flutter community

1
likes
140
points
206
downloads

Publisher

verified publisherrahulsha.com.np

Weekly Downloads

A highly accurate step counter plugin using accelerometer-based detection with low-pass filtering and peak detection. Supports foreground, background, and terminated state tracking.

Repository (GitHub)
View/report issues

Documentation

Documentation
API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on accurate_step_counter

Packages that implement accurate_step_counter