I built the same app with Flutter, React Native, and Ionic

Francisco Magalhaes
11 min readApr 27, 2024

--

Embarking on a quest to find the optimal cross-platform mobile development framework, Flutter, React Native, and Ionic were the ones that stood out to me. The three were appealing as they promised good performance while utilizing a single codebase for multiple platforms.

To start venturing into this new world, I created three versions of a mobile app that consumed an API and displayed basic data on the screen. The idea was simple: build an app that shows the details of a random country. I have previously worked on a similar project in Python.

Mini LCD connected to Raspberry Pi
Mini LCD displaying details of a random country every day

For a fair comparison, all three versions of the app had to meet the following requirements:

  • A country appears once the screen loads
  • A loading spinner shows before the first country appears
  • A circular button to get another random country
  • The entire list of countries is fetched only once (from RESTCountries)

Let’s first delve into my experience building a mobile app using Flutter.

Flutter

As described on Flutter’s website, “Flutter is an open source framework by Google for building beautiful, natively compiled, multi-platform applications from a single codebase.” It was created in 2017 and is based on Dart, an object-oriented programming language.

Before diving into development, one hour was spent reading Flutter’s documentation and watching tutorials. The team behind Flutter was enthusiastic and engaging, which fueled my motivation to dive deeper.

The setup of the development environment and Android Emulator went smoothly. It was expected, given that I was using a Google IDE (Android Studio) to use a Google framework (Flutter) to create apps for a Google Operating System (Android).

Time to create an app. Timer on. In 4 hours I produced this:

Flutter app

There weren’t any particularly complex issues encountered during the process. The Android Studio’s suggestions helped a lot while developing. And Hot Reload? What a time-saver! With a simple Ctrl+S, you’ll see your changes reflected immediately in the device, without a page refresh!

Despite the great experience with the setup and tooling, as I delved into development, I encountered a design pattern that stood out: the pervasive use of widgets. Flutter’s concept of “everything is a widget” became apparent, leading to a lot of nesting and mixing of styles with elements. Even paddings are widgets! This approach felt less intuitive than the component-based structures I was accustomed to.

Look at this example to see what I mean:

Having a padding, which is a small UI detail, being created right above the actual content of the screen leaves me with a slight sense of discomfort. I can only imagine how it would look in a scenario with numerous UI requirements. Maintaining focus will be challenging when these separate concerns compete for attention within the same field of view.

React Native

Created by Meta in 2015, React Native is an open-source JavaScript framework, that lets you build native Android and iOS applications. React Native has gained widespread adoption in the industry, serving as the foundation for well-known applications such as Facebook and Instagram.

I’ve worked with the React library before. VS Code was already installed. I just needed to get React Native running.

Oh, and what a pain it was. When I started using React Native, it was a whole different ball game compared to Flutter’s smooth setup. Right away, I faced two options for managing my workflow: Expo Go and React Native CLI. Unaware that these two options were mutually exclusive, I tried to follow online solutions without understanding the distinction. This led to a mess of dependencies that was hard to manage. In case you want to set up your environment for React Native, make sure to follow only the official documentation. By doing so, you’ll likely avoid experiencing the same frustration.

The app was completed within 2.5 hours, despite having unresolved issues displaying the images. To ensure the flags appeared, I forced width and height, which sometimes translated into a deformed flag. I was unable to display the flag at all on an Android device or simulator. It only worked in the browser.

React Native app

React Native offered a concise and readable approach in terms of code. This conciseness comes from React’s component-based architecture, which promotes modularity and code reusability. Each component is a JavaScript function. Through the use of JSX (JavaScript XML), React allows us to write HTML-like code to describe our components’ UI. In my example, it looks like this:

Ionic

Ionic, founded in 2012 is “an open source mobile UI toolkit for building modern, high quality cross-platform mobile apps from a single code base in React, Vue or Angular.” Ionic puts a lot of its investment in a rich set of UI components and attractive user interfaces that adapt to the target platform.

Ionic Angular was my choice for the app. Angular is a full-fledged framework built by Google in 2016 and used in many of their web apps. I spent 30 minutes watching Ionic videos and reading guides. The familiar Angular development ecosystem gave me the confidence to start the app after that short learning period.

I followed the instructions on the website to start the app from the blank template. Once the setup was complete, the result was a project with several generated JSON and TypeScript configuration files that made me uncomfortable. “Why do I need these many files?” I thought. But at least there was an app with text in the center, that could quickly be previewed in the browser.

The development process was mostly easy and it took only 2 hours to achieve the same functionality as in the Flutter and React Native versions. But I would be lying if I said I didn’t copy some code from the React Native version, namely the constructor of the Country class.

Ionic app

Ionic provides a set of UI components that make UI implementation much easier. I have used the Spinner and the Floating Action Button. The documentation provides simple examples so that integration is seamless.

On the contrary to React, Angular does not use JSX. Instead, it uses a template syntax. A Template is a section of HTML but with more functionality added to it, such as interpolation, event binding, and directives.

By having the UI separated into specific .html files, my TypeScript files became focused only on logic, possibly mitigating collaboration conflicts between developers working on the same screen.

Comparison between Flutter, React Native, and Ionic

Code style and language

In my perspective, the JavaScript-based frameworks win in terms of code readability and simplicity. I achieved the same functionality with fewer lines of code when compared to Flutter. I didn’t quite enjoy Flutter’s nested code style. It looks confusing and hard to read, with margins, padding, sizes, elements, and logic all mixed up, contrary to React Native and Ionic. Whether you prefer JSX or Angular Templates for implementing views ultimately comes down to personal preference. I lean towards Angular because its file structure closely resembles what will be rendered in the DOM.

Although I knew a bit of React and Angular (but neither React Native nor Ionic) before, which likely influenced the development time difference, I do feel they are easier to learn than Flutter.

Ecosystem

An interesting way of assessing a framework’s adoption is by checking the number of installed apps built with them. As of April 2024, in the United States, a notable 12.57% of the top 500 installed apps (from Play Store) were built using React Native, in contrast to Flutter, which holds a 5.24% share, and Ionic, holding only 0.52%.

The three frameworks offer extensive documentation. They all also have some degree of investment in interactive examples, but I feel that React Native’s is sometimes more confusing when it comes to setup and tooling. I faced that firsthand with my disorientation with Expo Cli and React Native Cli. This is not entirely React Native’s fault: there are many unofficial guides with many different approaches to starting an app. React Native also offers a lot of freedom when it comes to building a project, which despite bringing a lot of solutions to the table, comes up with missing documentation and unclear best practices in certain areas.

Flutter is growing in adoption, outpassing React Native’s and Ionic’s number of public GitHub repositories. Despite that, when we compare languages instead of frameworks, JavaScript, 16 years older than Dart, is still an obvious winner in adoption. JavaScript was born for frontend development but is now widely used for server-side as well. The entire JavaScript ecosystem is full of all types of resources, including third-party libraries for numerous use cases. If you learn Flutter, you’re only learning Flutter. If you build with React Native or Ionic, you learn how to build web components using standard technologies.

Tooling

Hot Reload
For testing in the browser, all three frameworks will reload the application when a change is made. During my manual tests, the three of them performed the reload quite fast after a code change, displaying another random country after 1-2 seconds, which includes 1 call to the countries API.

When it comes to testing in an Android device or simulator, Flutter takes the win with Hot Reload. Changing the file was enough for the view to update, without refreshing the entire page. It simply rebuilds the widget tree. In React Native and Ionic, however, any change in the files would trigger a refresh in the whole page (including the extra API call), which made it slightly more difficult to compare the before and after UI changes. Although in some cases, React Native should be able to preserve the state through Fast Refresh, in my tests, it didn’t, showing the same results as Ionic’s Live Reload.

Remote debugging
The frameworks use different debugging approaches; in all, we can remotely debug an Android app through the browser. In my tests, Flutter was the fastest to open the app (already installed) for wireless debugging.

Flutter: 10 seconds
React: 17 seconds
Ionic: 21 seconds

Flutter DevTools has a simple and intuitive interface with many features that can be found in the well-known Chrome Developer Tools. I found it interesting that there’s an option to slow animation to help fine-tune them. It also includes in-app UI debugging capabilities, which will help better understand the implemented design specs, such as margins and sizes.

Flutter DevTools
Flutter in-app debugging

Speaking of Chrome Developer Tools, debugging Ionic apps is similar to debugging a standard website, which is a big pro. The familiarity of this interface eases the debugging process. From performance to network and the possibility of quickly experimenting with CSS changes, Chrome Developer Tools has us covered. Ionic is the only framework of the three that does not offer any in-app debugging features.

Even though React Native has in-app debugging capabilities, such as a performance overlay, network information, and element inspector, it stays behind Flutter and Ionic when it comes to remote debugging. React Native offers React DevTools, which focuses on the component hierarchy of the app. It’s also possible to use the Hermes engine, which will allow (only) a few features of the Chrome Developer Tools to be used for debugging.

React DevTools

App size
While I haven’t explored any methods to reduce release .apk size, Ionic yielded a file size of 3.2MB, while Flutter resulted in 18.1MB and React Native 25.2MB. After installation, they occupied 9.61MB, 34.66MB, and 55.47MB respectively.

User Experience

Building a cross-platform mobile app with a consistent and modern look and feel is essential for delivering a seamless user experience. Both Flutter’s pre-built UI components and Ionic’s Adaptive Styling feature ensure adherence to Material Design on Android and Cupertino on iOS. In other words, Flutter and Ionic adapt the look and feel of UI components to the target platform.

With React Native, one must rely on third-party libraries to develop a Material Design-compliant UI. Maintaining extra dependencies can easily be a no-no for many developers. I felt the difference when I had to implement more styling in the Floating Action Button in the React Native version, to end up with a similar UI to Flutter and Ionic.

Performance

In theory, as it compiles to native code, Flutter should run faster than React Native, since the latter relies on JavaScript bridging. This is a technique used to allow JavaScript code to access the native features of a mobile device. Such a process can potentially introduce an overhead to the performance of an application. Ionic would be the slower of the three, given that it runs on a web view (a browser that is invisible to the user). However, Ionic states that the myth of it being slower than React Native is just that… a myth.

This comparison isn’t something I could test with my app, given its barely-existing performance requirements.

Conclusion

I’m interested in delving deeper into Flutter’s code style, even though I haven’t fully embraced it yet. There is a lot to learn about best practices, and the Hot Reload feature seems like a game-changer for debugging. Nevertheless, it is also incredibly appealing to take existing TypeScript, HTML, and CSS knowledge and transfer it to the development of a mobile app. The familiarity and established confidence in the JavaScript ecosystem make me inclined to put Flutter aside for now.

From development to debugging, including integration with existing web libraries, building an Ionic app feels like building a traditional web app. This makes me believe that growing my skills in Ionic development will not only enhance my proficiency as a mobile developer but also as a web developer. When compared with React Native, Ionic also takes a win in terms of implementing interfaces that follow Android and iOS design principles.

It’s important to acknowledge that while valuable insights were gained, many aspects, such as code organization, separation of concerns, and component reusability, may only become apparent as a project evolves.

As the mobile development industry has shown, great applications can be built with either framework, with good performance and access to native capabilities. Thus, don’t underestimate the importance of choosing a framework that you enjoy programming with. Rather than spending hours looking for answers online, why not explore with a sample app?

I hope you had a nice time with my story and my perspective as a cross-platform mobile development apprentice.

Further reading

See what the frameworks have to say about each other:

My apps

--

--

Francisco Magalhaes

Software Engineer, specially excited about automation, with a passion for traveling and music. Exploring mobile app development.