mobile · Apr 10, 2018

Quire: Building a Sophisticated Mobile App with Google’s Flutter

Quire Android mobile app built with Google Flutter framework for cross-platform development

Last updated: October 30, 2026

TL;DR

Quire built its Android app with Google's Flutter after writing the iOS app in Swift, and this is the engineering write-up. How Flutter draws on canvas, how its bridge works, and how Dart and Flutter compare with Swift and the iOS SDK at the language, framework, and performance level.

Every team with a phone app ends up in the same meeting. The iOS app has a feature the Android app doesn't. The Android app has a bug the iOS app fixed in March. Two codebases, two release trains, and one small team that was supposed to be building the product.

We wrote our iOS app in Swift a couple of years ago. Last year we chose Flutter for the Android app. This is what that was like, the good and the bad, from the front-end developer who did most of it.

Who is writing this?

I'm a front-end developer at Quire, a project management tool for capturing ideas, managing tasks, and working with a team.

We've used Dart on our server and our web client for years, so when Google released Flutter, a mobile framework that runs on Dart, we were already most of the way to a decision. Flutter shipped its first alpha in May 2017 and was still in beta while we built on it. We shipped anyway.

Two months after the official launch in the Play Store, I think we've learned enough to be useful to anyone in the Flutter community or thinking of joining it.

Read more on how Quire built its large application with Dart.

Why did we choose Flutter?

One language, one platform.

We ship an update almost every week, sometimes every day, and maintaining two copies of the same logic had become the slowest part of the week.

We tried the hybrid route first. A Cordova build gave us one codebase, and a UI that didn't reach the standard we wanted. Scrolling stuttered. Transitions felt a step removed from native.

Flutter gave us three things at once. UI rendering fast enough that the stutter went away, smooth transitions, and a core language we already shared across server, web, and now mobile. One codebase, one model, a fast development cycle. That was the whole requirement.

What is Flutter?

Flutter is Google's UI framework for Android and iOS. You write the UI once in Dart and run it on both platforms. When you need something only the platform can do, a bridge carries your Dart call to native code and back, so almost anything Swift, Kotlin, or Java can do is still reachable.

Two ideas carry the whole framework.

Drawing and bridging.

How does Flutter draw on canvas?

Flutter draws the UI itself, directly onto a canvas inside iOS's UIViewController or Android's Activity. Your Dart UI code compiles to machine code and paints. There's no translation into platform widgets, which is why Flutter's UI and transitions feel smooth. Compared with CSS, it uses a simpler layout model, and simpler is faster.

How does Flutter's bridge work?

When the app needs a platform resource, the bridge carries the Dart command and its data to the native layer and brings the answer back. Because the drawing already happens inside Flutter, the bridge isn't involved in rendering at all. That cuts the context-switch cost well below what hybrid frameworks like React Native pay on every frame.

How does Flutter compare with the iOS SDK?

Flutter is open source. When an API call does something surprising, you read the framework and find out why. You can trace the whole system's architecture from the top.

With the iOS SDK, the equivalent is a shelf of books, a lot of trial and error, and the slow discovery of rules nobody wrote down. I've done both.

I prefer reading the source.

How does Dart compare with Swift at the language level?

Swift people talk about local reasoning, the ability to understand a piece of code by reading only that piece. The language supports it. The SDK often doesn't.

Set a delegate on a UIView component, or a callback on a function call, and the related logic ends up in different sections of the file because that's how the delegate pattern works. Callbacks have their own problem, which is callback hell.

What's the best async pattern in Swift? Callbacks, GCD, PromiseKit, Reactive Swift? Everyone has a different answer, which means nobody has one. In Dart, the same job reads like this.

Future scrollToComment(Comment comment) async {
  await model.scrollTo(comment.position);
  await model.highlight(comment);
}

That's local reasoning you can see.

Swift loses a second point on build time. Three years ago we started porting our model and building the iOS UI in Swift. The codebase grew, and the compile time grew with it. Two or three minutes for a release build is tolerable, since it's automated and rare. A one-line style change that takes over a minute to check is not, and in our worst cases it did.

There are articles on cutting Swift compile time by changing how you write Swift. That's backwards. We picked a new language for clearer syntax, not for syntax that compiles faster.

Dart has an ahead-of-time compiler for production speed and a JIT compiler for development. A one-line change now shows up in about half a second. We're pleased with that number.

How does Flutter compare with iOS at the framework level?

Storyboards in Interface Builder look like the dream. Drag a widget, drop a widget, done. In practice the first advice you get is to keep the storyboard small, and the second is to stop using it.

We stopped when we hit its limits and its performance cost. Building iOS UI in code is its own kind of pain. Components, Auto Layout, legacy layout, and transitions all take more code than you expect, the code blocks sprawl, and every rule lives in a document you haven't read yet.

Flutter treats code as the UI template. In Dart 2.0 you compose a screen the way you'd write JSX or HTML, and the result stays readable months later.

Container(
 child: Center(
   child: Row(
     children: [
       Icon('smile'),
       Text('Hi', style: TextStyle(color: Colors.red))
     ]
   )
 )
)

iOS's official layout solution has never made sense to me, and the third-party ones keep winning for a reason. Flutter uses a Flexbox-style layout model, which I'd pick over any Auto Layout look-alike. There's also a guide that walks web developers through each step. After many years as a web front-end developer, that was good news.

What's the last line of defense?

The bridge. If there's no Flutter way to do something, we can at least do it the way a native iOS or Android developer would. That promise let us commit to a beta framework (alpha, when we started the first Play Store build) and still know we could ship any feature.

We used it.

Compressing a photo in Dart took about 50 seconds at 70 percent quality. In Java, half a second.

What should you look out for with Flutter?

Flutter is in beta and moving fast, so anything below may have changed by the time you read it. These are the gaps we hit and worked around.

No scrollTo index API yet

A ListView scrolls only by offset. That's fine when every row is the same height and awkward when rows vary. Our workaround in the app has three steps.

  1. Give each row an index.
  2. Scroll a constant distance, then check which widget states exist to find the current range.
  3. Repeat step 2 until the target row appears.

For very long lists we added a recommended row height. Scrolling by that estimate keeps the jumps accurate and the motion smooth.

Model flow and binding with initState and dispose

Design the model so it can be listened to many times and knows how many listeners it has. Bind in initState, unbind in dispose, and start or stop the model's work when a binding exists with no listener.

Don't emit events while binding. Firing an event inside initState reaches other listeners, which update their state while the context scope is still locked.

Don't cache widgets. Build is the point.

Building is cheaper than it looks. If you cache a widget, you also own its cleanup. Leave it to Flutter, which borrows React's approach, diffs the tree, and updates only what changed.

Our tree view runs past 2,000 rows in a ListView (we didn't use the builder variant, for reasons of our own), and it's faster than we expected. A stateful widget only creates row state for rows inside the viewport. For the build cache, Flutter published an article worth reading.

Most iOS-look widgets aren't ready

Flutter's focus is Material components. They work on iOS, since Material was designed to cross platforms, but some don't feel right there. The wheel-style date picker is the obvious example. iOS users notice when the platform's behavior is missing.

Input in Flutter

Input has rough edges. In issue #13765, you can't see the newline until you've typed more words. In issue #5986, there's no way to know which key the user pressed.

Was it worth it?

Yes.

We enjoyed building the Android app in Flutter, and we expect to enjoy it more once it leaves beta. Through alpha and beta it has been stable, and the Flutter team answers questions and issues quickly.

Being open source, it lets you see what's been fixed and what's changing. You can fix an issue yourself and send it back.

Flutter is a fast cross-platform framework with native behavior, and for a small team shipping on two platforms, that combination is hard to beat. We're planning to replace the Swift-based iOS app with Flutter in the coming months, and I'll write up that experience when it's done.

We did keep building with it. See how Flutter also powers Quire's mobile Timeline.

Related: Quire Mobile 3.0: Kanban Board comes to Mobile App, a later milestone in the same Flutter-built mobile app.

Native, cross-platform, or Flutter: how do they compare?

Every mobile team faces the same fork. Here's how the options stacked up when we chose ours.

Approach The trade-off Why it mattered for us
Fully native (two codebases) Best performance, but you build and maintain everything twice Too slow for a small team shipping on two platforms at once
Traditional cross-platform One codebase, but the UI often feels a step removed from native We didn't want an app that felt second-class on either phone
Flutter One codebase that draws its own pixels, close to native feel Let us move fast and still ship an app that feels right on iOS and Android

Team collaboration tool to stop juggling tabs and start shipping work

Frequently Asked Questions

Why did Quire build its Android app with Flutter?

Quire wanted one language and platform instead of maintaining two codebases. Flutter delivers high-performance UI, smooth transitions, and a single Dart codebase Quire already shared across server and web.

What is Flutter?

Flutter is Google's UI framework for building Android and iOS apps from one codebase. It bridges to native code, so it can do almost anything Swift, Kotlin, or Java can.

What language does Quire's Flutter app use?

It's written in Dart, the same language Quire already used on its server and web. Dart has an AOT compiler for fast releases and a JIT compiler for fast development.

Was Quire's iOS app also built with Flutter?

At the time, iOS was built in Swift while Android used Flutter. Quire planned to replace the Swift-based iOS app with Flutter soon after.

Jerry Chen
Software engineer. Passionate about all things new.