Daily step counter in Flutter
Track your user's step count using the device's pedometer in Flutter

Search for a command to run...
Track your user's step count using the device's pedometer in Flutter

No comments yet. Be the first to comment.
As Flutter developers, we love the efficiency of writing code once and deploying it everywhere. But when it comes to the very final step of launching or updating an app, generating App Store and Play

If you're looking for a streamlined way to distribute your Android test builds using Firebase App Distribution (FAD) with the modern Android App Bundle (.aab) format, you might want to pause. While the promise of using AABs via FAD—leveraging the Goo...

Last month, I set myself a new challenge: to build and release a small app from scratch within a month. This decision came after a realization I had while working on my previous project. That project had so many uncertainties and things left to do th...

Using Flutter's CustomPaint to create futuristic looking widgets.

Last year, I wrote two articles about theming in Flutter, sharing my frustrations with ThemeData (part 1 & part 2). While ThemeData is powerful, its extensive properties can be overwhelming, especially when many of them are rarely used. After experim...

On the time of writing, I'm working on a Flutter game with which I try to motivate the player to walk daily. By walking they will earn resources that they'll be able to use in the game. The more they'll walk, the more resources they can earn.
I need a way to track the step count for each day, as the progression should reset at midnight.
Just tracking the steps might seem easy, but while building this, I ran into a case that made it a bit more tricky. At least I needed some time to get to a proper solution.
Because the app should show the amount of steps from the current day, I need to keep track of the current day. So when the user is actively using the app and it becomes midnight, thus a new day starts, the app should reflect this so the player is not longer able to claim rewards from the previous day.
I started looking on pub.dev for a plugin that I could use to track steps. There are quite some plugins build by the community and most of them seem to be forks of each other, which made it really hard to find the 'right' one.
Eventually I went for pedometer_2, simply because I liked the documentation best.
It turned out that both platforms that I wish to support (iOS and Android), do not have the same capabilities.
- Get real time step count since a date (Stream) (Only on IOS)
*Alternative for Android explained under
Getting real time step count since a certain date (start of today) sounded exactly what I needed, luckily there is an explanation for an alternative on Android!
To get access to the step counter information you need to get permissions from the user. I will not cover it in this article as the documentation explains it very well here.
Ideally we would want to use the .stepCountStreamFrom(...) method because it allows us to specify the period. But as the documentation clearly states, this is not supported on Android.
Luckily, the documentation mentions an alternative solution.
- (Android alternative)*Use a combination of the*
getStepCountandstepCountStream. Example in theExample App
The .getStepCount used in the example app, supports a from and to argument, on both Android and iOS.
The .stepCountStream used in the example streams the amount of steps registered since the last system boot. When the user is actively walking, the stream will continuously emit new values.
By combining the two, and sprinkling a bit of rxdart magic, we can create the stream that we need.
final stepCounterService = StepCounterService();
class StepCounterService {
StepCounterService() {
// We listen to stepCountStream so we get new data
// when the user is walking.
// But we don't use the value as it gives us the total
// steps since last device boot
Pedometer().stepCountStream.listen((totalSteps) async {
// Now we determine the periode for which we wish to
// get the amount of steps, which is today.
final now = DateTime.now();
final startOfDay = DateTime(now.year, now.month, now.day);
final endOfDay = startOfDay.add(Duration(days: 1));
// Now we use this information to use the getStepCount
// method to get the steps for today.
final stepCount = Pedometer().getStepCount(
from: startOfDay,
end: endOfDay,
);
// And then we add this stepCount to the
// _stepCounterController.
_stepCounterController.add(stepCount);
});
}
// We use a BehaviorSubject because it holds the last value
// and emits it whenever a new listener subscribes.
// This makes a Stream more useful to be used in the UI.
final _stepCounterController = BehaviorSubject.seeded(0);
final Stream<int> get todaysStepCount => _stepCounterController;
}
Great, we now get the amount of steps for today which is updated whenever the player walks.
However...
In the above code, we get the step count of today. But what if it is no longer today?
Our service initializes the stream when the app starts. It uses the current day, but when we reach midnight, we want the step count of the next day.
In other words, the periode should be dynamic.
The rxdart package has some very useful utilities that we can use.
Let's add a dynamic way of managing the date to our service.
final stepCounterService = StepCounterService();
class StepCounterService {
StepCounterService() {
// We use CombineLatestStream, also from rxdart, to combine
// two streams. The listener will be called whenever
// one of the streams changes.
CombineLatestStream.combine2(
Pedometer().stepCountStream(),
_todayController,
(totalSteps, date) => date, // We only need the date
).listen((date) async {
final startOfDay = DateTime(date.year, date.month, date.day);
final endOfDay = startOfDay.add(Duration(days: 1));
final stepCount = Pedometer().getStepCount(
from: startOfDay,
end: endOfDay,
);
_stepCounterController.add(stepCount);
});
}
// We can use this to force the date to update.
void updateDate() => _todayController.add(DateTime.now());
final _stepCounterController = BehaviorSubject.seeded(0);
final Stream<int> get todaysStepCount => _stepCounterController;
// We use this to keep track of the day
final _todayController = BehaviorSubject.seeded(DateTime.now());
}
We've now made the date for which the step count is requested, dynamic. By using stepCounterService.updateDate(), we're able to force the service to use the current date.
Now that we have a way to update the date, we need to determine when to call it.
When the app is opened, it will already initialize with the current date. So we should call our method when we reach the next day. For this we could use a background task, or even easier, a Timer .
void main() {
// Schedule the first
_scheduleUpdate();
}
void _scheduleUpdate() {
// Force the service to update the date.
stepCounterService.forceUpdate();
// Calculate how long it takes till tomorrow.
final now = DateTime.now();
final tomorrow = now.add(Duration(days: 1));
final startOfTomorrow = DateTime(
tomorrow.year,
tomorrow.month,
tomorrow.day,
);
final tillMidnight = now.difference(startOfTomorrow);
// Schedule this function again at the start of tomorrow.
Timer(tillMidnight, _scheduleUpdate);
}
Now the _scheduleUpdate function will be called once at app startup, and then it will schedule itself again for every next midnight.
With some rxdart magic and some well timed (pun intended) function calls, we're able to overcome the platform limitations on Android and are able to get the daily step count as a stream.