2 months later, in an unlikely turn of events, our management decided to put a bet on Flutter. It was mid August and we had to come up with a plan for the next release. We sat together in a room “hacking” Flutter for 3 days and trying to see if we can somehow add it to an existing app — we’ve made a checklist of things we needed in our common flutter package (persistence, authentication, assets, platform channels etc.). We’ve followed the official docs (📅 August 2019) to create that package and added things we needed. Our next task was to create dummy apps for each platform using standard iOS and Android tools — once we had that, the final task was to include our common package in both dummy apps and make them work. At the end of the “hackathon” our findings were good enough to green light the project from the engineering side.

Greenfield vs Brownfield

When you start writing a Flutter app from scratch, the internet is full of samples. You literally can bootstrap an entire project with one command and have deploy targets ready for both iOS and Android. If you pair it with a dedicated solution like codemagic.io you’ll have your entire CI/CD infrastructure working in no time. It’s one of the biggest selling points of Flutter if you ask me.

Unfortunately this wasn’t the case for us. We have two different apps with different architectures, with different git repos and CI/CD workflows (both using bitrise.io at least). There isn’t that much information on how to proceed here and we had to figure out most of things on your own. Here’s how we did it.

We decided to develop Flutter code in almost complete separation from our existing code bases — we wanted a fresh start for our team rather than a constant battle against legacy code on both platforms. The intent was to get the most out of the Flutter framework and bridge mobile teams together. We’d figure out platform integration details later.

The Hidden Cost of The Flutter Blackbox

We had a request to add a new feature to our app — the so called Market Keywording. The new feature essentially enables pro users to review photos with poor discoverability, add extra keywords/metadata to them and in the end make so reviewed photos more sellable.

From engineering point of view, this feature was a couple of peripheral screens so it was a perfect first candidate to be implemented in Flutter.

“So, what’s our ETA on this?” — Ramzi, our CTO asked Having figured few things already during the prototyping time I boldly replied— “Three weeks, one month tops. You know, hot reload is great!”

It turned out the feature part was just a tip of the ice berg and there were few things hidden under the surface:

Photo on EyeEm by Tayfun Hendem

While I think I was pretty well versed in Flutter by mid-August, my team was just starting their journey so there was definitely a moderate learning curve ahead of them. It might take just a few moments to display “Hello world” but getting the feel of the language and getting app’s architecture right takes a bit more time.

Next, putting Android & iOS teams together to work on one single codebase resulted in some initial clashes, sometimes on personal level. Over time, however, we managed to turn this harsh start into adjusted rules, improved communication, better tooling and code guidelines.

Design System

Before we started the work on the new feature, we realised it would be nice to have a design system in place.

This became an extra project on its own — we wanted to stay true to the platform while owning every pixel. We’ve built a unified adaptive widget system on top of Cupertino and Material packages so that moving forward our code would look the same on both platforms but act differently. We also wrote a companion catalogue app for it which essentially allows browsing available components, icons, themes and even switching between the two platforms while being on the same device.

Our design system is an effort to get most out of Dart/Flutter

Our design team chose Figma to spec out this new system. Figma is pretty easy to use but comes with few pain points. We could export assets as SVGs and use them in Flutter but this was slow and manual process. While UI specs looked spectacular, they weren’t in sync with JIRA nor our code so sometimes there were surprise changes without prior notification.

CI/CD Infrastructure

Let’s not forget we had to build our Flutter CI/CD infrastructure from scratch. We had no DevOps really to offload this work — what played in our favour was the preexisting Bitrise CI as it already had a first class support for Flutter built in since the beginning of 2019 — this helped us save some time.

We decided to go with a mono repo for all the sub projects as Flutter makes it quite easy to reference relative path packages. We have separate packages for features and then example feature apps for those packages. This makes development cycle faster and more focused.

With Flutter CI working, a new opportunity presented itself to setup proper tests and code checks. We wanted to do this one right from the start. We started adding unit and widget tests to our design system — it might appear a bit boring but figured this would be a good place as well to learn them once we move to more business logic. Flutter widget and UI tests play really well with coverage reports which we started collecting using codecov.io — this allows us to have an eagle view of what our tests are not doing yet. We adopted dart formatting and lint checks from the Flutter team. We played a bit with instrumentation tests as well but in the end found them a bit flaky and immature to spend more time on — didn’t even manage to get them running on our CI.

With Flutter it’s easy to generate a coverage report and then integrate it with tools like codecov.io

Flutter as an Add-on

Android & iOS integration were one of things we checked first and they looked good on the surface —that is gradle/aar and CocoaPods support. I know the docs said the feature is in preview but so was Gmail for years, anyway…

For Android the exported aar is a folder with a set version 1.0 which you cannot change. If you want to use this aar in CI environment or publish to a private repository you need to figure it out yourself —it took me 3 days to get it working with our primary Android CI workflow. That wasn’t the bad part. iOS took more than 2 weeks, mental breakdown, unicorn blood 🦄 and it’s still not quite there yet.

Hopefully the above paragraph will be outdated soon as some smart people at Google are working on making everything better 👇

Be sure to check Matt Carroll’s talk once DroidconSF makes it available online. It was way more informative than official docs. Really hoping to see the same talk for iOS soon.

The Release

Once we finally got to implementing the feature itself we were already far behind the original schedule. The management started to act nervous but in the end we caught up to speed and managed to make a coherent release on both platforms at the same time. First time ever 🎉

EyeEm v 8.1 on Android — entry point from the native part, the rest is Flutter

We’re happy to be one of the first companies out there to add Flutter to an existing product and we’re thrilled to see where this takes us next. Personally I think we’re in a better place now. With Flutter we’ve merged two teams and doubled our engineering power— we can now focus more on the features our users want and less on the platforms. We’re looking forward to bringing our cohesive design system to the rest of our app and maybe beyond mobile 😉