Can cross-platform tools surpass native development?
In recent years, I have attended a couple of mobile development conferences where cross-platform development was always present. With every new conference, the topic got even more exciting, so we at Parkside decided to re-evaluate the latest available options. Many clients are asking for a cross-platform approach to cut costs and still serve their product to a vast amount of users.
Cross-platform development is meant to be the rising star of development — especially mobile development. The community behind cross-platform mobile development has grown and many new options have emerged which outperform the simple outdated HTML approach. In this article, I compare the relatively new Flutter framework with the big contender React Native. Why am I just covering those two? Talking to developers and judging from what’s being published in articles lately, those received more acceptance by the community than others and I had the feeling that they outperform others in several areas.
Each framework has its unique architecture to build apps, so they each have their pros and cons. While native apps offer superior user experience and performance, cross-platform frameworks are designed to save costs and speed up the development. Let’s take a closer look at them.
Flutter is Google’s open-source development kit to create 2D cross-platform apps. It’s based on the programming language Dart, also developed by Google. The application is compiled ahead-of-time into native ARM code which results in a better performance. Flutter is not supposed to be a wrapper on top of iOS/Android but draws the UI from scratch using a C++ Library called Skia (https://skia.org). The Flutter team promotes it as an efficient tool for prototyping because of its hot-reload functionality. If you are already familiar with object-oriented concepts, you can quickly adapt to Dart/Flutter without much mobile experience.
From an outside perspective, cross-platform (mobile) development sounds like a great solution to lower development costs by saving time. In the case of iOS & Android, you could theoretically be twice as fast and cut the budget in half. Let’s see how these frameworks and native development compare when considering some of the core aspects of mobile development.
The effort involved in the project setup is often underrated when estimating how quickly developers can get to the actual coding.
Native: The development IDEs include everything you need to start right away. Adding libraries is straightforward, either using Android Gradle or iOS CocoaPods. I can’t count how often I did this and how every single time it worked like a charm.
Flutter: When I first encountered Flutter on a conference, the setup seemed to be straightforward. I just followed the Flutter intro by downloading the latest Flutter SDK and choosing Visual Studio Code as my Flutter-dedicated IDE. I preferred VSC over Android Studio since I wanted to take advantage of the extensions for Flutter & Dart. I started right away with a generated project sample and could easily include dependencies in the pubspec. Again, as with native development, everything is automated for you.
React Native: When I started the setup of React Native, I had some problems configuring it to run correctly. It’s also supposed to work out-of-the-box by installing node & watchman and then using npm to install React Native Client. Adding dependencies is also possible via npm. During the whole process, I encountered a lot of versioning issues and it took some effort to find matching dependencies. As I was trying to resolve those issues, I had a feeling that a lot of other developers must have encountered similar problems.
. . . .
In my opinion, the setup of native development is intuitive and fast, followed by the Flutter setup with few downsides. Due to the many versioning issues that arose with React Native, I consider it a few notches below its competitors.
The UI of an app is crucial to its success. Users prefer fancy-looking apps over others. There are different approaches to this topic — either following the guidelines of each platform and therefore keeping the user in a known environment or going for a unique custom design to represent the brand and letting your product stand out.
Native: The native IDEs offer diverse possibilities to create the UI, either by means of a visual editor, markup language or code. I like to use the visual editor for drafting and reusable code for the production stage. Having complete access to the native UI components as well as pixel-perfect and performant rendering is also enjoyable.
Flutter: Everything is based on widgets and those are created in code. Combining & stacking widgets will create the visual UI. They use the Flexbox approach to lay out elements in a similar way to CSS. When starting with Flutter, it was confusing to me that, among other things, Opacity and Alignment (Center) are also widgets and that I had to wrap the desired element in one of them to get, for instance, an opacity effect on-screen. Without encapsulation, the UI code can quickly get messy and hard to read. Also, be prepared to have many indents.
Since Flutter is not wrapping native components but instead trying to mimic them, it seems that not every rebuild of native components is 100% pixel-perfect despite their claims to the contrary. Flutter is using Skia to render its widgets, no bridge or mapping included; therefore you can expect high performance in release builds. Debug builds on the other hand use a virtual machine to run the Dart code, hence the debugging process may not be as performant.
. . . .
There is no real winner in that category. As for native development, you have to create the UI multiple times for different platforms. Flutter is a viable option if your design is custom and you are not extensively using native components (you always have to wait for a Flutter update if the platform changes its style). React Native tries to be pixel-perfect as Native code while creating one layout for multiple platforms.
React Native would win in the rendering of fundamental components over Flutter, but there is also a great potential for bugs inside the mapping and it’s generally slow. I do like the Flutter approach, but nesting felt a bit overwhelming at first. The structure improves if you encapsulate a lot, but it’s still far from perfect. Native development has the downside of having to do the same thing multiple times.
Not only is the look a critical element but also the interaction with an app. How are the different development approaches affecting the intuitive usage of an app?
Native: Creating an enjoyable experience is not hard for a native developer. Reuse built-in animations and follow the guidelines for intuitive user experience and the user will be immediately familiar with how to use the app.
Flutter: Using the suggested Material Design, the essential interaction with the app is already built in and you can create an intuitive app interaction pretty fast. I am using iOS and therefore the Material Design differs from what I would expect from an iOS app. Still, I think you can get used to it quite fast, though there’s no guarantee that users will accept it. Even though creating a custom design is fairly easy in Flutter, I’d suggest not to overdo it. There is a high chance of confusing the user.
Creating animations in Flutter works like a charm. They are not too time-consuming and you get smooth results, but beware of the temptation of employing them excessively just because they’re simple.
React Native: Since real native components are rendered, the feeling of using the app is pretty close to a native app. On the other hand, creating a pleasant mobile experience with custom components is not that easy (at least for me).
React Native also provides a built-in animation SDK, but from my point of view, it cannot compete with the others.
. . . .
I would see Flutter & native development as winners because I felt more comfortable creating animations — even with little effort the results look excellent.
There’s every likelihood that an app which only looks good won’t have many users. There has to be a good mixture of useful functionality, appealing design and high performance. A user wants to access as much information or perform as many tasks as possible in a short time, due to the brief median usage duration of a single application.
Flutter: The code is compiled ahead-of-time into native ARM code; therefore, there should be no performance issues in general. There is (at least in the release build) no bridge involved, which could affect the performance. The 2D Skia framework being used to draw the UI is also a compelling and fast framework with no drawbacks in comparison with the native one.
. . . .
Let’s look at an example.
I created a simple app with each of the technologies in question: native (Android), Flutter & React Native. All apps were tested on a Google Pixel XL device — let’s compare memory & CPU performance as key measurements here.
Flutter and native Android code are just using about 10–15% of CPU, while React Native is using around 25–30%. When comparing the memory usage one can see the overhead of cross-platform applications. While the native app allocates 80 MB, both cross-platform apps are >100 MB. Since it is a simple app, the energy consumption is negligible in all cases.
In simple apps with few demands on CPU/GPU, the user probably won’t notice a great difference between any of those options.
Increasing the productivity of programmers is a crucial point from a financial point of view. In my opinion, that should be the actual selling point of cross-platform development. Let’s see if that’s really the case.
Native: Creating a layout and writing business logic is quite fast when following the suggested architecture. Of course, if your app has to support multiple platforms, you have to write everything more or less twice.
Flutter/React Native: Write once — support Android and iOS. Sounds awesome! But are you twice as fast?
Based on my experience, the answer is ‘no’ — you will still have to write some platform-specific code or treat the platforms differently. Also, I have the feeling that creating some parts (e.g., UI elements) is not as fast compared to using the natively provided toolset.
Generally speaking, you can save time, but certainly not cut it in half. It also highly depends on your knowledge (or available developer resources) of the programming language and architecture in question. Having an experienced native mobile team still trumps investing into acquiring React or Dart skills. Perhaps as a React web developer React Native is the way to go.
Every app sinks or swims based on its stability. Ease of maintenance is most often disregarded when estimating the costs of a new product. The ecosystem of mobile apps is in constant change; nearly every year new OS versions are being released and apps have to adapt to them.
Native: Access to the latest SDK is granted ahead of the OS release to ensure the functionality of your application — pretty convenient. Still, if something in your environment changes, you’ll have to do it twice.
Flutter: Since Flutter is using its own render stack, components will render correctly, but there’s a chance that some bridging functionality is not working as expected. When that’s the case, you will have to wait for an update to the Flutter SDK.
React Native: Because of the native mapping, a lot of error sources can emerge. As with Flutter, there is a waiting period before you can start working on your app to support new OS versions.
. . . .
During my research, I experienced many versioning issues while using React Native. Currently, Flutter outperforms React Native, but native development is by far the winner thanks to early adoption & simple use of new feature-sets, though you have to update twice if something in your app environment changes.
Using carefully chosen third-party libraries can make a developer’s life more comfortable and also help to release the app faster. There’s a certain point where reinventing the wheel just doesn’t make sense.
Native: The native mobile developer community is highly active and has been steadily growing since the release of Swift & Kotlin – languages it is also helping to improve / extend.
Flutter: Flutter has a steep adoption curve. During the last couple of months, the community nearly exploded, maybe because of the structured roadmap (which Google is pushing further) or the ability to create web apps with a single codebase (currently in beta: Hummingbird). Although it is relatively new, a lot of functionality is already built into the Flutter SDK and primary platform-specific bridges are available as packages (e.g. Bluetooth, camera, authentication).
React Native: React Native has been heavily promoted by Airbnb, Facebook & Instagram in the past few years. Airbnb is said to have recently stopped using React Native in their apps in favor of complete native versions. The community is still quite considerable since the framework allows for a quick transition from React web to mobile app development.
Though looking at conference talks and posts in developer forums, the community doesn’t seem to be as active as it used to.
A rich and extensive documentation not only helps you when you get stuck at some point — as I have found, it can also be indicative of the reliability of a framework in terms of structure.
Native: Both Android and iOS, Kotlin & Swift respectively, have a thought-out structure and a documentation that is easy to use.
Flutter: The first stable Flutter SDK was released at the end of 2018. Even though it’s relatively new, the documentation is a good starting point and delivers precise up-to-date information.
React Native: While the documentation is also up-to-date, I felt that the instructions are often vague and not too much of a help without digging deeper.
. . . .
At the time of writing this article, both native platforms and Flutter have useful docs. React Native on the other hand depends a lot on the community and on add-on frameworks. From my point of view, the React Native documentation just couldn’t provide me with useful answers in most cases.
When I encountered cross-platform development for the first time, I thought that it would be a short hype. Now, after diving deeper into the topic, I can fully understand why some frameworks became that popular. I worked on a couple of native apps which had to look and feel the same on different platforms. In such cases, those cross-platform tools can be very helpful for independent developers who seek to support multiple platforms.
To sum up, I do not think that the current cross-platform approaches really surpass native development. All the same, they can be used for simple apps which are not relying on the newest OS features or CPU/GPU-intensive calculations. For those use cases, if you want to establish your own design on both platforms, cross-platform may very well be the better choice.
It also helps web developers to more easily set foot in the mobile world. Even mobile developers should be open to those new technologies/options since not all parts of them are bad. I also believe that the current options are going to improve and different new approaches will appear. For example, Kotlin Native looks really promising: sharing the business logic, but creating the UI for each platform separately.
Mobile development is still in its infancy and there are many more changes yet to come. Let’s be open-minded about new technologies and see what the future will bring.