Choosing a hybrid native framework for our first mobile application
Here at Policygenius, we set out to build a new product that will help our customers develop strong financial planning habits that fit into their busy lives. While we were researching the best way to deliver this new service, we found that there were certain feature sets that we could deliver only through a mobile application.
If there’s one thing that our team is dogmatic about, it’s that there’s usually a best tool for the job and this situation was no different. We needed to find the right mobile development framework to not only create an ideal experience for our users, but also an efficient and reliable developer experience that was well supported by the software community generally.
Our choice revolved around five main comparisons: Performance, Team Familiarity, Documentation, Plugins, and Tooling. All of these would help us determine whether we’d be able to experiment quickly while building a quality app.
Here’s how we came to decide on the right hybrid mobile framework for our app.
Note: There were many phases in this decision-making process that won’t be written about here: pure native development (Java/Kotlin/Swift/Objective-C), progressive web apps, other hybrid frameworks like Xamarin and Cordova, etc. The tl;dr is that we slowly and deliberately whittled down our choices until we were left deciding between React Native and Flutter.
Where we’re coming from
Policygenius’ engineering team has largely been focused on providing a first-in-class web experience for our users (both internal and external), deploying a multitude of applications and services to support such an experience. We’ve started developing more Golang microservices, but our stack consists mostly of React.js and Ruby-on-Rails. These two are our wheelhouse, and we’ve developed with them in lock-step with the community over the past few years.
This familiarity with React made React Native the early frontrunner. We liked that we would be able to reuse much of our team’s experience developing React for web, along with experience managing dependencies with yarn or npm, debugging in the browser, etc.
On the other hand, Flutter was totally new and foreign to us. Few of us had ever heard of Dart, some of us had been burned by frequent and unsavory maintenance to AngularJS, and none of us really knew about the Flutter SDK other than it seemed to be getting some buzz. We decided to try it out because of its promises of high performance and seamless compatibility between iOS and Android.
Here’s how we decided.
While we’re not going to be building a graphically intense gaming app, we definitely want our users to have a performant experience. This starts with the app load experience and pervades throughout the rest of the app experience: list rendering, animations, etc. We didn’t want to be limited by our framework should we want to improve any of these.
As an AOT-compiled framework that is entirely compiled down into native code, Flutter avoids having to even communicate and manipulate native components by rendering its own UI with elements that are indistinguishable from their native counterparts. This leads to native-speed boot times and buttery-smooth graphics.
You are no longer working with HTML5 components, you’re now working with native-based components that are named generally enough to map to actual native OS components (e.g.
<View /> maps to
android.view on Android or
UIView on iOS). The fact that there are these direct-to-OEM component mappings under the hood is nice in that you don’t have to write native code, but more restrictive because certain decisions are made for you, both in how a React Native component is implemented in native components and how a React Native component is implemented in React.
Flutter might be new to engineers on our team, but I found Dart to be very familiar as a C-like language, almost like a more elegant TypeScript or Flow (strongly typed languages used for React Native development). In the end, it seemed like the time it would take to get comfortable with Dart and Flutter wouldn’t be that much more than the time it would take a React developer to get used to React Native components and the structure of a React Native app.
Diving into a whole new framework is much less daunting when you have plenty of resources to guide your development process. The Flutter team at Google did an incredible job investing in tools and documentation from the very beginning of the project, manifesting in surprisingly comprehensive documentation.
I found React Native’s documentation lacking in a lot of areas. There weren’t great explanations around a lot of its components and their props, and I often found it hard to find what I was looking for. With Flutter, I found myself actually successfully looking at their documentation rather than instinctually jumping down a StackOverflow rabbit hole.
The engineering teams for React Native and Flutter took different approaches to building plugins that complement the core frameworks. For React Native, the philosophy has been to lean much more heavily on the open source community to build out complementary features, especially for device features. As the framework has aged, the team has even begun to stop maintaining/deprecating 1st party components (such as
AsyncStorage) and left that for the community to maintain.
On the other hand, the Flutter team has not only provided Material and Cupertino style widgets as a part of the core framework, but also written more than a dozen 1st party plugins for functionalities that are deemed crucial and sensitive to users (device features, data storage, auth, etc). These are all open source, in addition to allowing for 3rd party plugins on the Dart packages repository. You get the best of both worlds: a growing list of 1st party plugins with all the support of the open source community. It also helps that there’s a culture around providing full usage examples in each package’s repo.
When it came to the tooling around Flutter and React Native (installation, running, building, coding, debugging), I was blown away by the comprehensiveness of the Flutter SDK and less than satisfied with the React Native tools.
With Flutter, installation was easy and verifiable (
flutter doctor command), initial load was fast and warning/error-free (even in Xcode!), hot-reloading worked out-of-the-box, 1st party plugins for multiple IDEs were readily available, automatic dependency management was built-in, a widget tree for debugging was easily navigable, and clear error messages/backtraces let me know where I had made silly mistakes.
With React Native, I had no way to know whether I had installed everything properly and, once I ran the default, out-of-the-box app, I was greeted with multiple warnings/errors both in the app and in Xcode. Hot reloading did not work out-of-the-box and even then did not detect changes to my
package.json the same way Flutter’s hot reloading detects changes to my
pubspec.yaml file. I was able to look at the component hierarchy in the browser, but any error messages were totally indecipherable and their backtraces seemed to end a step or two before the line where the exception was actually thrown.
In the end we decided to go with Flutter, and in the couple of months since this comparison research was originally conducted, we’ve been really happy with our choice. It’s been a great development experience and our designer loves that it’s so easy for us to compose different widgets together to bring our brand and design system to life on whatever devices we need. The community is growing and we haven’t run into any walls or frustrations with the framework, allowing us to focus on creating a great product for our customers.
There’s one main caveat I’d like to add: there are lots of companies that have built totally sufficient apps with React Native. I’m sure by the time this article comes out, lots of the issues I’ve written about will likely have been solved or moved closer to being solved (seems like generating a project with
react native init no longer results in tons of Xcode warnings…nice!). All that aside, given our resources, goals, research, and experience using Flutter and React Native side-by-side, the Flutter community’s growth and the Flutter team’s dedication to building it out (Flutter for Web recently was released), we felt that Flutter was the best choice for our team.
We’ve gathered a pretty great bunch of product, design and engineering folks to tackle some pretty tough problems in the insurance industry. Fun fact: we still need a lot more. If you’re interested in what we have going on here and want to help us on our mission to make insurance suck less, mosey on over to our careers page to see if something fits.