EyeEm Fluttered

Łukasz Wiśniewski
Flutter Community
Published in
9 min readDec 9, 2019

Adding Flutter to an existing app — a practical example of how Flutter helped support our business goals and an honest retrospective.

TL;DR: It took longer than expected but was totally worth it.

Who EyeEm?

We’re EyeEm — while this might not mean much to you, we’re quite popular amongst 25 millions photographers around the world. We connect businesses with talented individuals and we have had our apps available on App Store and Google Play for a while now.

With EyeEm you can stay in touch with the contemporary photography — not only you can learn how to shoot, find new inspirations or share your pictures with others but you also get to earn money if your work sells… and it just so happens our mobile apps got a piece of Flutter in it.

Market Keywording — our new Flutter based feature

The Problem

You might be asking yourself why choose Flutter over a native solution you have already? Why change something that just works™?

Well, for us there were few factors. Our mobile team is relatively small compared to the size of our company. We have two iOS and two Android developers, one QA engineer, one designer and one PM — it’s expensive to do the same job twice.

Second of all, due to different development paces and architecture choices, our apps drifted apart and created a significant feature imparity over time. Android was usually pretty fast to deliver while iOS was struggling with legacy code. This in turn created unhealthy environment where Android was pressured to do more features so that we could e.g. A/B test product ideas whereas iOS was pressured by deadlines and missing features.

Finally, our tech stack was getting outdated, both on iOS and Android. With SwiftUI/Compose on the horizon, essentially all the UI we were adding was being rendered obsolete — declarative UI is now the next big thing to go for.

How To Flutter

This began as a one man effort — I started to work on a Flutter prototype of our app to see how hard it would be to replicate our Android app/architecture and get it running on iOS. Needless to say I quickly got hooked into the entire new ecosystem. At the same time I saw there were some pieces missing in the Flutter framework and decided to spare my free time to work on them —I figured that even if the prototype hadn’t been successful I would still have gotten something out of it.

This is how Android Strings To Flutter, Mjolnir-dart and Voyager open-source projects came to life— I wrote them because I knew they could be useful to others.

The Prototype

Three weeks and three weekends down the road it was finally the time to show the prototype to the team and the management. The initial reactions were mixed. We needed a decision on whether we want to go forward with Flutter or not. Since this wasn’t just my call I’ve put a process framework in place so that we could achieve some kind of resolution. This involved giving others a month to collect their feedback on the prototype, then once we knew where we were all standing we could take the final decision together — it did not go as expected.

So are we doing this or what?

Flutter became a divisive topic for our team — new language, new stack, “staying true to the platform” vs “owning every pixel”. Our designer was worried, PM was feeling hopeful, iOS devs were definitively skeptics and QA was all like 🤷. We ended in quite heated discussion which led us to not making any decision. I continued investing my personal time in Flutter just because it was fun but the idea of switching to a new framework at our company had to be postponed.

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 😉

Thanks for reading ❤️ If you want to stay connected, you can follow me on twitter/github or just reach out directly on fluttercommunity.slack. If you’re interested in EyeEm — wait no more and join 25M+ photographers today!

--

--