React Native or Flutter: Which should I choose? (Part two)

This is part two of a three-part series. In part one, we defined where React Native and Flutter fit in with all cross-platform solutions and why we’d want to focus on these two heavyweights. In part two (this part), we’ll go head-to-head in looking at the history, design, and performance. And in part three, we’ll look at the same component built in both React Native and in Flutter.


History

React Native

React Native started out as a hackathon project at Facebook. It is a spinoff of React, the incredibly popular web development framework. The names “React” and “React Native” may be confusingly similar but they are accurate; React Native shares a ton of commonality with React and yet compiles to native for both iOS and Android. As a result, developers who have experience with React will find React Native simple to pick up.

Flutter

Flutter came from Google’s desire to create an even easier way to create Android apps than with Java/Kotlin. Notice that the primary motivation was not cross-platform. The fact that you also get an iOS app for free is awesome but beside the point. The ability to make iOS apps in addition to Android was Google’s secondary concern yet it appears to be the primary motivation for everyone who uses Flutter.

Google has borrowed numerous ideas from React Native, more than just cross-platform development. Many of the best features and design principles of React Native are also found in Flutter. This is nothing new; React Native borrowed heavily from React which borrowed heavily from AngularJS (ironically from Google) which borrowed heavily from Ember and so on and so on.

Just one of the many examples is the virtual DOM, a brilliant idea. Oversimplified, the virtual DOM is a tiny copy of what is on the screen. React Native (and now Flutter) compares saved copies of the virtual DOM to determine what needs to change on the real screen on a background thread, batches those changes and applies them en masse instead of one by one. It makes the application extremely fast. React Native pioneered this idea and Flutter adopted it. Why mess with success? We can expect React Native to return the favor in the near future.

Another example of Flutter’s wise imitation is components.


Components

React Native

React Native’s major strength is its capability to create very easy to understand and therefore easy to maintain components. Components are self-contained hunks of JavaScript and pre-baked React Native elements that are supplied by Facebook out of the box. They provide the basic building blocks you’ll fit together to form your custom-built components. Basically it works like this, you join these basic pre-built components to form more capable hunks of code called custom components. These are in turn put together to form even more capable ones. You eventually put together enough of them to create a fully-formed application that can be distributed to phones via the AppStore/PlayStore. Think software Legos.

It is an elegant design that has proven to be effective over several years and thousands of apps.

Flutter

Flutter has components also but we call them “widgets”. Any developer who has coded web applications using React, Angular, Vue, Polymer or Web Components will find the concept very, very familiar because these have all embraced component-based design even though they’ve all implemented components in different ways. Flutter provides about 156 standard widgets as compared to React Native’s 40.


Stateless and stateful components

Both frameworks support the idea of components that have the ability to maintain their own data (aka. state) and also components that have data that doesn’t change. In both frameworks, the latter components are simpler to write and maintain.

Different terms for the same things

Both frameworks suffer from a lack of clarity in maintaining that state. State management seems to be a major weakness in both. Fortunately a form of Redux is available to both frameworks to shore up that weakness. See flutter_redux by the great Brian Egan for the Flutter version.


The toolchain

Both frameworks involve us writing code and compiling it to two executables, one for iOS and another for Android. It requires the project to be set up just right. When you add in automated testing, tree-shaking, linting, debugging, deploying to a virtual device and/or a tethered device you have a highly complex and brittle process. The set of tools to automate this is called the toolchain.

React Native

The toolchain is an admitted area of weakness for React Native. We have two choices to set up a project; the “react-native” command and the “expo” command. The react-native command is the original and is very complicated and fragile. You can’t develop for iOS on Windows. You can only test on a tethered physical device, not in a virtual device. And there are other problems. Because of these limitations and because we now have an alternative, react-native is no longer recommended for use on projects if you can survive without it.

The newer option, expo is also a command-line tool. But it allows all of those things above and has many other advantages, not the least of which is that the setup time is decreased from hours to minutes. This tool was created by a company called Expo, a for-profit startup. They provide the expo tool for free but encourage you to buy their full-featured tool. This shareware tool is now the tool officially recommended by the React Native team. Unfortunately projects made with expo cannot support native modules written in Java/Kotlin or Swift/Objective-C.

The summary of all of this is that React Native projects must be created with one of two fragile tools, both having major weaknesses and both being incompatible with the other. The React Native toolchain is a hot mess.

Flutter

Flutter also has a highly complex toolchain. But the Flutter team has learned from React Native’s problems and has provided one solid command line tool to setup and manage your development process instead of two broken ones. The command is called “flutter”. And yes, because of the nature of cross-platform development, complexity is inescapable. But Flutter gives us a tool imaginatively called “flutter doctor” which does an automated scan of your toolchain and reports on any problem areas it finds along with an understandable prescription for resolving each issue. It even offers to fix them when able. Flutter’s spin on the toolchain is also troublesome, but flutter doctor makes it much, much easier than React Native’s.


Runtime execution

React Native

React Native creates a true-blue native app on iOS and Android … structurally. But the behavior is kind of faked. React Native has the application use the device’s JavaScript interpreter to execute the code that runs on startup, in response to button clicks, and so forth. This is why developers can write using modern JavaScript. The code that they write is sent unadulterated to the device where it is interpreted. This is both good news and bad news.

The good news is that developers can write using a language that is wildly popular and one that your organization likely already knows. If not, there are tons of developers who know JavaScript and are interested in working with you. Also, it opens us up to the possibility of using popular and proven open-source JavaScript libraries like Redux, Axios, Flow, Lodash, Moment, essentially any JavaScript Library that doesn’t address HTML or CSS. That is a huge advantage.

The bad news though is that React Native pays a performance penalty for using this JavaScript bridge.

Flutter

Flutter doesn’t suffer from the performance penalty of using the JavaScript bridge because it doesn’t use JavaScript. Flutter uses Dart.

The Dart language was invented by Google in 2011 as a potential replacement for JavaScript on the web. That never caught on. JavaScript improved and got more and more popular. But the thing that makes JavaScript so powerful — its dynamic nature — is the very thing that traditional object-oriented developers hate. OO devs who love Java, C#, Objective-C, C++ and the like hate the fact that JavaScript is loosely typed and comparatively unstructured.

Dart “fixes” those problems. We put “fixes” in quotes because for every OO developer who hates JavaScript’s dynamic nature, there is a functional programming developer who loves it and thus turns up his/her nose at Dart.

Dart has historically been a solution in search of a problem. And Google finally has their opportunity to inject Dart into the conversation as a serious contender. Whether or not you like Dart, Flutter is amazing. In order to use Flutter, we have no choice but to use Dart as the language. Google is forcing developers to look seriously at Dart.

To reiterate, some people will love Dart right out of the chute. But even those who don’t love it will quickly see its advantages. Once you get past the angst of having to learn yet another language, you will see that Dart is elegant in its own way with many of the advantages of a strongly-typed language like Java/C# and many of the flexibility features of a loosely-typed language like JavaScript. It may be unpopular now, but experienced OO devs have found it extremely easy to pick up.

Regardless of your love or disdain for the Dart language, it allows us to compile ahead-of-time (AOT) to native instructions tuned for the arm64 chip and therefore running right in the CPU rather than an expensive just-in-time (JIT) compile like React Native is forced to do. Flutter is truly native. You will see this pay off a little at runtime but hugely at app startup.

Speaking of truly native vs. clever facades, let’s discuss how these frameworks handle rendering the UI.


Rendering

React Native

With React Native developers describe their user interfaces using a domain-specific language called JSX. It looks like XML inside of JavaScript because that is essentially what it is. The components that React Native provides us are translated at compile time into their OS-specific equivalents:

in React Native, affordances are rendered using the device’s native look and feel.

Notice the colors, backgrounds, fonts, and even casing are different because the one on the left is compiled to an iOS UIButton and the one on the right is an android.widget.Button. They’re not approximations, they’re truly native objects.

This is a huge advantage for organizations that want their apps to mimic the look-and-feel of the operating system since it happens with zero effort on the part of developers. But when an underlying element changes on iOS or Android, there is a lag until React Native devs support it. Changes at Apple or Google could conceivably cause your code to suddenly break on the affected devices.

A final concern is when the component behaves differently between the OSs. For example, a progress bar works completely differently between iOS and Android. It is evidently impossible for React Native to provide us with one component that renders properly on both so React Native gives us two and forces us to use them both sort of like this:

Platform.OS === “android” ?
<ProgressBarAndroid styleAttr=”Horizontal” indeterminate={false}
progress={0.5} />
:
<ProgressViewIOS style={styles.progressView}
progressTintColor=”yellow” progress ={0.5} />

To try to render the ProgressViewIOS on Android or vice-versa literally throws an exception at runtime, the worst possible thing to occur.

Flutter

This is not the case with Flutter. Flutter uses the open-source Skia 2-d graphics library to render all elements as pixel-perfect mockups of the originals. This means that you and I can’t tell the difference between Flutter’s re-creations and the OS’s real elements. But these are copies that are deployed to users’ devices along with the app. This means four things:

  1. Skia is written to take advantage of the GPU, so it is even faster than native at times.
  2. Your app’s size will be larger since the widgets have to be wrapped in the app. The average Flutter app is currently about four times larger than a native Android app. Note that this number continues to shrink although it will never get as small as pure native.
  3. When Apple or Google changes a UI element, your screen views are completely unaffected since the widgets are independent of the OS.
  4. Your app will look identical on iOS and on Android. If you want them to appear different, you must render a different widget depending on the platform. This is called OS detection and platform-specific rendering.

Platform-specific rendering is extra effort and should be avoided if possible. Speaking of which …


Platform-specific customizations

React Native

Unfortunately, React Native includes a few design decisions that require certain platform-specific customizations. Developers are forced to write at least a little iOS code or Android code for anything other than the simplest apps. Larger organizations may feel like they can’t compromise their product’s quality to avoid the enormous costs of hiring iOS and Android experts. Remember, this is the very problem we were trying to avoid when we decided to pick up React Native in the first place.

AirBnB and Udacity were “all in” on React Native, even creating libararies (Like enzyme, react-dates, react-sketchapp, and rheostat) for React Native that are being used worldwide. In 2018, both famously dropped React Native as a solution, both writing excellent post-mortems about the experience and the reasons behind their decisions. The conclusion for both was that React Native forced them to either sacrifice the quality of their app or maintain not just two but three codebases; iOS, Android, and React Native. The lesson to learn from them was that if you can’t settle for the things that React Native does well, you will end up working harder and slower to produce less capable applications than without it. Caveat emptor!

Flutter

Flutter is not immune to the need for platform-specific customizations, but with Flutter those customizations are usually optional rather than required. As an example, the DatePicker widget can be used on both iOS and Android. But it will render an Android-style date picker regardless of the platform that the user is running. If it would bother your users to have an Android date picker appear on their iPhones, you would opt to do device detection. The key to remember is that you are not forced to do that with Flutter. It is a choice. On React Native, you don’t have an option. You must do device detection and write platform-specific code.

Bottom line: Which should I choose?

React Native and Flutter have a whole lot more in common than differences, but our mission is to help you decide which is best for your organization. If we focus on the differences, one or the other may make itself evident as the better choice for your unique situation.

Overall

Both are popular and open-source.

Performance

Flutter runs slightly faster but React Native’s executable will be smaller.

Developer experience

It is more fun to create the UI in React Native because of the declarative syntax of JSX.
If you like JavaScript, you should go with React Native. If you hate it, Flutter is the way to go.
Cross-platform development is more satisfying in Flutter.
The toolchain is poor all the way around.

Assuming you’ve narrowed it down to these two choices, your biggest influence will be native look and feel vs. native code support. If it is important to you to have your app look like an iOS app on iOS and an Android app on Android but you can tolerate having three code bases, React Native is the better choice for you. On the other hand, if avoiding Objective-C/Swift/Java/Kotlin coding is important but you can tolerate deviation from OS look and feel, then Flutter is the way to go. Many large organizations will opt for the former since they have tighter branding requirements and deeper pockets. Smaller groups will want the latter.

I understand that speed is a concern and there are speed differences. But the speed differences between them are honestly very small, most below the threshold of human perception. You’ll probably not make your decision based on speed. They are both great with Flutter getting the slight nod.

If you already have a team of developers who love React your learning curve is nearly horizontal; choosing React Native becomes a no-brainer. If they don’t know React but still love JavaScript, then React Native will work better for you. On the other hand, if your developers love an OO language (Java, C#, C++, Objective-C, etc.) but really don’t enjoy JavaScript, then Flutter is the better choice for you. Both languages are equally difficult to learn for people with little to no development experience. If you need to hire developers, you’ll have a much bigger pool to choose from going with React Native.

Come back for part three of this series where we’ll look at the languages with code samples of the same app in both platforms.