From Zero to MVP in 3 Months With Flutter

I’ve recently been approached by a young start-up because they needed an app for their delivery business. Time to market was crucial and the app was supposed to be available for both Android and iOS right from the beginning. Having done native development for both platforms in the past, I knew that this wasn’t going to work this time. At least not if we wanted to keep the cost down to a reasonable level. The app had to be cross-platform, meaning one code base for both platforms. Here’s why I chose Flutter and how it turned out.

Cross-Platform

Flutter caught my attention by providing several benefits, one of which being that it compiles to machine code while coming with its own render engine. This promises a consistent cross-platform user experience as well as good performance. In theory, neither Ionic nor React Native can compete with this.

Dart vs. Swift, Kotlin, and TypeScript

Flutter projects are written in Dart, a language more or less unique to Flutter development. Dart is a pretty modern language, too, and it fits mobile development quite well. The only crucial thing it was lacking at the beginning of this project was sound null-safety, as it was only available on Dart’s beta channel and a lot of packages did not yet support it. As of this writing, null-safety for Dart is stable and a lot of third-party packages have already adopted it.

UI and UX

Have a look at these screenshots of our MVP app:

Screenshots of our MVP

You can see a custom app bar, buttons, text, images, a navigation bar, and a Google Maps integration with custom map markers.

The thing that seems messy at first and takes some getting used to is that in Flutter everything is a widget. There are widgets for transformations, for animations, for user interaction, as well as for layout and Material components. This results in a hugely hierarchical widget tree that can only be managed efficiently with the help of good tooling. Fortunately, Flutter has great integrations for common IDEs. They help a lot with refactoring and rearranging the widget tree.

IDE integration for Flutter

While I’m still not sure whether I like the widget approach in terms of the code it creates, it has proven to be very powerful and hassle-free in practice. I never felt that I had to compromise significantly layout-wise which in turn allowed for the MVP to already look and feel quite polished with reasonable effort.

Business Logic

The list of possible state management approaches for Flutter is long and even includes familiar names like Redux. After some consideration I decided to use BLoC. Writing BLoC code is pretty verbose and it helps to use a code generation package like freezed in order to create immutable classes for states and events. Keeping your business logic inside multiple Business Logic Components helps keeping things predictable and testable.

Data modeling is more or less straight forward and can be improved by also using the freezed package to create immutable classes as well as json_serializable if you need to work with JSON data.

For working with REST APIs, retrofit is a great package that generates a lot of the code you would otherwise have to write yourself.

As you can see, code generators are used extensively to aid with the business logic side. They can take a lot of repetitive and tedious tasks off your hands.

Quality Assurance

One the one hand, you can spend a lot of time writing automated tests and that’s usually time well spent. On the other hand, it’s not always easy for your client to see why their money should be spent on writing tests instead of adding new features. Fortunately, adding tests to our Flutter application was simple enough to achieve a level of testing I’m comfortable with without going overboard in terms of cost.

BLoCs can easily be tested using the bloc_test package. Best case scenario: You test all your BLoCs and since they’re deterministic you tested all of your business logic.

On top of testing BLoCs, UI testing can be achieved with little effort using the golden_toolkit package. It allows you to render parts of your UI (your widget tree) to so called Goldens. These define how your UI is supposed to look given a specific state. If anything in that UI is going to change you’ll catch that with the Golden test. If the change is intended you simply update your Golden. That way you can be sure your UI doesn’t change without you noticing it.

Together, BLoC tests and Golden tests are pretty powerful when it comes to regression testing.

Communication With the Host Platform

Building and Deployment

This has proven quite efficient so far, as automatic builds include running tests and distribute new versions quickly to internal testers. For releases it’s just a matter of taking the desired builds and uploading them to the stores.

Conclusion

Of course, it’s not all sunshine. In order to move forward quickly, I had to use quite a lot of external packages. This has so far prevented me from migrating the codebase to the newly introduced null-safety feature mentioned before. Keeping track of all the dependencies will become increasingly challenging as the project grows and this will need to be addressed in more detail at some point.

For anyone wondering, the app is called easi.delivery and is available in both the Apple App Store and Google’s Play Store.

Thanks for the read, I hope you enjoyed it. If you have any questions or would like to know more about Flutter and how it can help with app development feel free to leave a comment!

You may also contact me on my company’s website or on LinkedIn.

Software Developer and Entrepreneur at productionbuild.de