Random messages in Dart intl

As people, we are always craving novelty, things that we are familiar with makes us feel cozy but also do not attract as as much they were right on the beginning. This why your app should constantly engage the user. How to achieve this? You can for example have multiple messages for same operations.

In HabitChallenge there are two features that have multiple localized messages:

  • local notifications,
  • streak detection.

Each notification will have different message in form of a question, asking if you already did your habit.

For streak detection it is a bit more complex. Each streak message depends on the streak length and has not only random message, but also header and text that you can share with your friends.

Nevertheless both require a way of providing pseudo random message in one of the supported languages. Plus some of the languages have more options than others (yes, Polish users are less likely to see the same message in row).

In react-native times, this was easy! Since i18n-js relays on JSON configuration file, I was able to get extract translated raw list, then it was easy to check its size and finally generate random number within given range.

It is not as easy in Flutter. Mainly because initially I decided to go with the standard localization library intl, and learned half way through that I cannot use the same approach as I had in react-native with it.

You may ask why? First of all intl does not use JSON to store translations, but specialized ARB files. Main advantage of ARB is that it is supported by software that is used by professional translators.

The downside, is that only data structure that it supports is a map and you can’t access it directly. This makes things a bit harder… but there is more!

intl, does not use ARB files directly, they are only used as a transportation layer. With intl you can extract all messages from your app into ARB file, give this file to translation team (if you are lucky to have one). Then get back one ARB file per language and import them back to your app as dart source files.

Yes, there is some black voodoo magic with code generation in between. When you get ARB files, you need to run them through intl command line tools, to generate dart file with translation.

With code generation there are also some limitations. You can use placeholders, but you can’t call any methods on them. Instead of passing an object and extracting its properties, you need to pass each property separately as a parameter 😐

Enough talking, lets jump into the code. Or at least lets start talking code 😉

As I mentioned before, intl have support for maps… not exactly map data structure, but at least we can think about it as a map. I am talking here about Intl.select() where you can select one option base on provided key. Lets try to use it:

Continue reading

From React-Native to Flutter in 531 Commits

Here is a recording of my FlutterLDN talk about ReactNative and Flutter.

First commit for the HabitChallenge app was created on 27th of July 2017. Back then there were no other choices than to go with react-native and JavaScript.

Since then I did 3220 commits and 244 releases. App was downloaded over 140k times on Google Play and AppStore.

I am considering this a huge success, taking into account that HabitChallenge is my first mobile app and I did virtually no marketing for it!

Long story short on 7th of August 2019 I have decided to start from scratch with Flutter taking all my learning from react-native and writing every single widget and feature once again.

After 531 commits (yes, this is the actual number of commits from git history) on 24th of December, HabitChallenge 5.0 was released on Google Play!

In the talk I am comparing side by side the same widgets implemented in react-native and Flutter. This is a unique real world comparison of two technologies that lets you achieve the same effect… but with different cost.

Which one do you think is more efficient? React-Native? Flutter? You will know my conclusion after watching the talk on YouTube!

Ensure High Code Quality in Flutter

Being one-man-army working on an app has some advantages. There is no deliberation about code formatting, libraries or frameworks used in the project. This is why migrating from react-native to Flutter was an easy decision, I just didn’t need to convince anyone apart from myself ;).

Of course all of those pros are also the cons. There is one that could prevent me from making bad decisions, checking in a bad code or braking old features while implementing new one. There is no one to deliberate about algorithmic, implementation and architectural choices in code review… you get the point?

There are some ways how some of those down sides may be overcome. For example writing unit and functional/integration tests is one of them. Code formatter and analyzer are another two.

Thankfully Flutter comes with all of the bells and whistles. There is flutter format, to automatically format your code, and flutter analyze to analyze code and enforce quality. Even more, both tools comes with awesome pre-build configurations that are used by Flutter team.

Unfortunately there is nothing that would automatically write test for you ;). But fortunately there are high quality and performant test frameworks that lets test your code on:

  • unit,
  • widget (functionality),
  • widget (presentation),
  • integration

levels.

One thing is to have the tools, but another is to use them… When you are part of a bigger team, someone will configure a build and CI/CD system for you, that probably will be running in the cloud. Or you can use something like CodeMagic… But in my situation I try to limit external services that I use with HabitChallenge.

One thing that I loved in my react-native times, was husky project. That lets you easily define and manage git hooks, especially pre-commit hook. This way I was able to run google typescript and unit tests before each commit.

Back in August 2019, there was nothing that may be compared to husky in dart/flutter ecosystem. So I decided to go with a general solution, the pre-commit project. Lets see how we can leverage pre-commit to ensure our code is properly formatted, doesn’t break any analyzer rules and do not break existing unit tests.

Continue reading

How I endedup writing my first mobile app

At the time of writing it is already nine months since I’ve started my adventure with mobile app development and ReactNative in particular.

Before that I’ve wrote an simple Android “stopwatch with reporting over email” app for Megaron. I’ve also wrote full blown iOS app in Swift for my startup, that didn’t went live, although the app was decent! (Side note: as it usually is with tech startups run by tech people we didn’t found a market for it).

As you already know, HabitChallenge is not really my “first” mobile app, but it is first that went live and is actually used by plenty of people around the world.

The story starts back in July 2016, when I’ve broke my tibia bone… That means at least 3 months of lying down in the bed… no walking (at least not without crutches), no running, no going out… My first idea (actually second one, first was not to bother about it and live my normal life… how foolish I was) was “Let’s use this time productively and make as much as possible from it!”. That’s how I started thinking about my first publicly available mobile app.

Why mobile app? There were plenty of reasons:

  • I wanted to learn whole process from idea inception, through implementation, publishing the app, marketing it… and observing how it grows,
  • I always wanted to help other people, create something that would be usable for others,
  • Wanted to learn new technologies and explore this part of software development that I was not yet exposed to,
  • AppStore and Google Play are now huge markets, why not to participate in them,
  • Passive income is a big thing now, and mobile apps sounds like one of the possibilities.

OK, so it would be an mobile app… which platform? iOS or Android? Of course the answer is “both”. Since I was already familiar with development for both of them I know what to expect… and didn’t wanted to play with XML layout files for Android and Storyboard or XIB (which internally are also XML) for iOS; writing UI code manually was also not an option. Lucky on ClojureD conference I’ve heard about ReactNative and that was my weapon of choice 😉

I’ve already said why and how… but “what” is still an open question. It shouldn’t be something big, nor buyer-seller type of app. It should some kind of utility app… or productivity…

Back then I was using HabitLoop to track my habits. It was not the nicest looking app, but it is open sourced has decent set of features and only available on Android. “Yeah! Let’s write yet another habit tracking app!” was my thought… “But it should have some ‘twist’, so that it won’t be a simple clone…”.

At that point I’ve started listing features and picking what will be included in first release (aka. MVP). In about two days I had listed everything, write it down, decided and commit for it.

The first version of HabitChallenge app had:

  • awesome UI and UX (that means I need to find someone to do it for me),
  • habit that can repeat on particular day of week,
  • habit can repeat multiple times per day (eg. drink 7 glasses of water),
  • habit can have multiple reminders,
  • no ads,
  • free version with limits for number of habits, reminders and repetition during the day,
  • in app purchase for the Pro version.

HabitChallenge featured image

Estimated time: 4 weeks, actual time spend: 9 weeks. The first version of HabitChallenge was available for install on AppStore and Google Play on 13th of October 2016.

Just recently HabitChallenge was installed over 10 000 times on Google Play (it took about 5 months to get there). It is used all over the world (although it only supports only Polish and English) and the “twist”, the “killer feature” is still not implemented yet… and I’m still working it 😉

If you manage to read so far… that’s the end 🙂 Please give HabitChallenge a try and if you like it give it 5 starts on Google Play and AppStore 🙂