How I created a Gamification experience with Flutter in 4 days

Previously I have written several posts about the entire development process of my Business Ideas app and how I managed to reach 100k downloads in the Play Store with the idea of ​​sharing everything I am learning and giving something back to the great community that has helped me so much.

But something that had not been able to achieve, or rather, did not know how to improve was the issue of user retention.

According to various sources on the internet, a healthy retention is between 30% and 60% in D1 (Day 1, that is, 24 hours after installation), however my application has only maintained 14% in D1. Well under the recommended minimum.

There are many ways to improve your retention rate that I will not repeat here because there is already a lot of information about it on the web. So for my app I chose to include a gamification experience, because I feel it is very much in line with the nature of the app itself.

Honestly before starting I had no idea what or how I would do it, I thought it would take me weeks if not months to develop everything necessary, so it was my surprise to complete all the code, even the graphics in just 4 days .

Clarifying ideas

The first thing I did was create a list of the tasks I had to work on. It was as follows:

Simple right?

After meditating (outside of the computer) I was able to sit down and create a list of actions that would trigger the acquisition of each badge.

Sure there is a lot of theory, studies and analysis on gamification with processes, milestones, terminology and others, but I did not want this update to take months so I decided to work only on what I could create from my app without taking much effort and time.

At the time of starting I thought about this more as a proof of concept than a great project and to be honest everything with this app has been like this: small iterations and experiments “to see what happens”.

Once I had my to-do list, each thing was solved as I went along. The key was to use the simplest solution to what was needed.

I’m sharing this because sometimes we want to go straight to the code and it is best to clarify our ideas and think things over beforehand.

Now the cool thing comes…

Saving user achievements

Currently I use Hive to save certain user preferences such as language, night mode and favorites since it is much faster and more efficient than SharedPreferences, so I decided to use this same package to create a “box” (As they call their non-relational database in Hive) where all achievements are stored.

It is worth mentioning that in all my projects I create a “Common “ class with all the common functions to use within the app.

For example, to know if the user has night mode activated I call Common.getNightMode which returns true or false depending on the case. To save the same preference I call Common.saveNightMode and pass it a true or false parameter as you can see in the following snippet:

class Common {
static void saveNightMode (bool value) {
var box ='myBox');
box.put('nightMode', value);
static bool getNightMode () {
var box ='myBox');
return box.get('nightMode');

The previous code gives us an idea i how to define our box where we can save and read the achievements achieved by the user, but to make it clearer, let’s suppose that we want to store all the achievements in a box called achievements :

class Common {...  static void saveAchievement (String name, bool value) {
var box ='achievements');
box.put(name, value);
static bool getAchievement (name) {
var box ='achievements');
return box.get(name);

Then to save each achievement you would only have to call the saveAchievement function as follows:

Common.saveAchievement('achievement_1', true);Common.saveAchievement('achievement_2, true);...

And to verify if the user has obtained the achievement we would only have to verify that the value of Common.getAchievement(name) is true .

It is important to mention that to avoid errors you must start Hive and open the box before starting any read or write operation. They recommend doing it from the main () method as seen in the following code:

Future<Null> main() async {
await Hive.initFlutter ();
await Hive.openBox('achievements');

In any case, it is recommended that you take a look at the Hive documentation, which for me is one of the simplest that I have been able to find and super easy to understand, even for beginners like me.

Showing the reading progress

Another thing I did and I really liked was saving the reading progress of each content, it was a challenge and at the same time very satisfying to achieve. All content articles now have a yellow progress bar.

For this, what I did was create a variable of type ValueNotifier called _currentScrollPosition to store in memory the current position of the scroll (dah!), then I put the SingleChildScrollView inside a NotificationListener where I call the function _scrollListener () to update the value of _currentScrollPosition .

It should be noted that I only update the scroll position if its value is greater than the previously saved position, as can be seen in: if (progress> _currentScrollPosition.value)… In this way I prevent the progress bar from decreasing if the user scrolls up. ValueNotifier<double> _currentScrollPosition = ValueNotifier<double>(0);_scrollListener(value) {
_currentScrollPosition.value = value;
print("SCROLL POSITION: " + _currentScrollPosition.value.toString());
...return Scaffold(
body: NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification scrollInfo) {
double progress = scrollInfo.metrics.pixels / scrollInfo.metrics.maxScrollExtent;
if (scrollInfo is ScrollUpdateNotification) {
if (progress > _currentScrollPosition.value) {
return true;
child: SingleChildScrollView(

Then to show the progress bar above all the content I made a little cheat and put a LinearProgressIndicator in the FloatingActionButton inside a ValueListenableBuilder that receives the _currentScrollPosition that we saw above:

...return Scaffold(
floatingActionButtonLocation: FloatingActionButtonLocation.centerTop,
floatingActionButton: ValueListenableBuilder<double>(
valueListenable: _currentScrollPosition,
builder: (context, snapshot, _){
return LinearProgressIndicator(
backgroundColor: Colors.black12,
valueColor: new AlwaysStoppedAnimation<Color>(Colors.yellow),
value: _currentScrollPosition.value,

One more thing …

Another thing I discovered was how to make an image appear in grayscale, for this we put our graphic inside a ColorFiltered widget . The widget receives the Boolean parameter earned with which we decide whether to show the graph in color if it is true or in grayscale if it is false, as can be seen in the following code:

const ColorFilter greyscaleFilter = ColorFilter.matrix(
);return ColorFiltered(
colorFilter: this.widget.earned ? ColorFilter.mode(
: greyscaleFilter,
child: widget(...)

From here on everything else was design details and some creativity to add the badges with their descriptions.

With the above, we already have the basics to save the user’s achievements, it depends on the creativity of each person, but especially on the functionality of the app, to see what achievements can be included in the gamification experience.

You can test everything mentioned before in the version v2.3.0 of the app at
Let me know what you think.

If you liked this post, let me know in the comments. Feel free to clap your hands and share it however you want :)

Apasionada de la vida. Fan de StarWars, anime, comics, Marvel, Legofan. Panamá 🇵🇦