Qt for iOS – Tips, Insights and Biggest Painpoints
Whether your product is in its early stages or ready to launch, the iPhone platform is a compelling option to […]
Choosing the best framework to develop an application is a tough task. Nonetheless, that is a critical decision to make before starting the development of your next project. Currently, quite a popular method of software development is cross platform software development. What is cross platform development? This is an approach that allows you to reuse code between different operating systems like iOS and Android or macOS and Windows. Following this way of apps development is first of all cost and time effective. In multiple cases, it is a better approach than native app development. Therefore, if you want to follow this approach, reduce costs and develop apps more quickly, you need to use a cross platform framework such as Flutter, React Native or Qt. But which one to choose? In this article, we compare Flutter vs React Native vs Qt to help you answer that question.
What will we be looking out for? What issues are important to us?
Scythe Studio’s main area of expertise is cross platform software development using Qt framework. As Qt QML consulting company, we make no secret of the fact that we are fans of this technology. We did our best to make this comparison fair and all the opinions on Qt and other frameworks are based on experience on our developers and external specialists.
In the meantime, you can check out our Qt development services.
Let’s start this Flutter vs React Native vs Qt comparison by showing you the history, characteristics, showcases and a code snippet for every of those frameworks. The goal is to give you a composite description of the capabilities and weaknesses of each technology. Mobile apps are the intersection of the platforms supported by each framework. However, while comparing we had a wider picture in mind than just mobile app development.
Let’s start our deliberation on the best framework with the description of Flutter. The first stable release of this framework took place in 2018. Therefore, it’s the least mature technology on our list. The solution is developed mainly by Google and it’s really the apple of their eye.
Flutter apps are coded in the Dart programming language. Dart is an object-oriented language that may look very intelligible to you if you already know C++, Java, or other C-style languages. It should be highlighted that Flutter has great programmer experience, especially for beginners. You can develop Flutter apps with Android Studio, IntelliJ IDEA, Visual Studio Code, and Emacs that have official Flutter plugins.
The main and first platforms supported by Flutter are mobile ones – Android and iOS. Although the technology is quite immature, it must be said that it’s under constant development. At this very moment when I write this article (July 2022) there are 210 open pull requests on Flutter GitHub repository. This fast development of the framework brought support for web Flutter development and recently for desktops. However, the desktop support is actually a bit unstable and unequal between platforms. For instance, at the time I write this, you can’t use Firebase SDK – the main Google’s solution, on Windows. Mobile app development is a way more popular than desktop app development.
Unlike React Native, Flutter doesn’t render native UI elements, but paints them on its own. Because of that UI controls don’t look natively by default. They rather suggest users follow Material Theme design, which is Google’s standard for graphical user interfaces. It has advantages like the fact that the app looks in the same way, even on old devices.
Material Theme is natural for Android mobile apps, but it’s not for iOS. It simply doesn’t look well on Apple’s platform. Therefore, Flutter developers took care of iOS apps developers and they created a Cupertino package that is a set of iOS-style widgets. They look great, but if you would like to release an app to both platforms and achieve a native app look you should have two UI codebases.
It should be said that Flutter has top-notch developer onboarding experience, documentation, and other reference materials. Also, the community is really engaged in the project.
Flutter framework itself is quite small. There are not as many modules like in the Qt, but Google’s child has a strong and constantly growing community that contributes to the open packages repository pub.dev. You can find plenty of open-source third party libraries there, which is great for Flutter developers. Keep in mind though that it is somehow risky to rely on external developers in case of maintenance and updates of those packages. Those community libraries have really unequal support between platforms.
To let you see the differences between each framework, we implemented a simple TODO app that gets tasks from the mocked webserver, displays them in a list and has features to add new ones and complete existing ones. The functionalities and look of the application stay the same regardless of used technology. That’s why we don’t follow Material Theme design for Flutter app, which is a kind of default for this framework.
import 'dart:convert'; import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'input_section.dart'; import 'task_widget.dart'; import 'task.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatefulWidget { const MyApp({Key? key}) : super(key: key); @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { late Future<List<Task>> futureTasks; @override void initState() { super.initState(); futureTasks = getTasks(); } @override Widget build(BuildContext context) { return MaterialApp( title: "TODO", home: Scaffold( backgroundColor: const Color(0xFFEBEBEB), body: Padding( padding: const EdgeInsets.only(left: 20, right: 20, top: 80), child: Stack( children: <Widget>[ Column( children: [ const Align( alignment: Alignment.topLeft, child: Text( 'Your tasks', style: TextStyle( fontWeight: FontWeight.bold, fontSize: 24), ), ), const SizedBox( height: 30, ), Expanded( child: FutureBuilder<List<Task>>( future: futureTasks, builder: (context, snapshot) { if (snapshot.hasData) { List<Task> data = snapshot.requireData; return ListView.builder( itemCount: data.length, itemBuilder: (BuildContext context, int index) { return TaskWidget( text: data[index].title, id: data[index].id, onPressed: () { deleteTask(data[index].id); }, ); }); } return const CircularProgressIndicator(); }, ), ), ], ), InputSection( onAddClicked: ((taskText) => addTask(taskText)), ), ], ), ), )); } Future<List<Task>> getTasks() async { final response = await http.get( Uri.parse('http://62766064bc9e46be1a160ec4.mockapi.io/api/v1/tasks')); if (response.statusCode != 200) { throw Exception('Couldn\'t get tasks'); } var tasksJson = jsonDecode(response.body) as List; return tasksJson.map((taskJson) => Task.fromJson(taskJson)).toList(); } void deleteTask(String id) async { final response = await http.delete(Uri.parse( 'http://62766064bc9e46be1a160ec4.mockapi.io/api/v1/tasks/$id')); if (response.statusCode != 200) { return; } setState(() { futureTasks.then((value) => value.removeWhere((item) => item.id == id)); }); } void addTask(String taskText) async { final response = await http.post( Uri.parse('http://62766064bc9e46be1a160ec4.mockapi.io/api/v1/tasks'), headers: <String, String>{ 'Content-Type': 'application/json; charset=UTF-8', }, body: jsonEncode(<String, String>{ 'task': taskText, }), ); if (response.statusCode != 201) { return; } setState(() { futureTasks = getTasks(); }); } }
Above you can see the main.dart file where most of this app’s code is located. As you can see, Dart is similar to other C-style programming languages. Building interface is based on UI elements called widgets that have predefined parameters to set if you want to adjust the look and feel of them. Flutter is often criticized for the multiple nestings levels which is considered as bad for code readability.
You can notice that there are no emojis in the screenshot of Flutter app while there are in screenshots of Qt and React Native apps. That’s because Flutter doesn’t have a great support for Unicode and you need to implement a bit tricky solution to handle emojis.
You can find the code of each demo app used in this article on Scythe Studio GitHub profile.
Let’s now take a look at another framework – React Native. It is a cross platform development framework that is a product of Facebook’s (nowadays Meta Platforms Inc.) developers’ efforts to reduce the need to implement the same thing twice.
React Native is an outcome of an internal Hackathon organized by the company in 2013. After months of development, they finally released the framework in 2015. It’s now also open-source, however, it receives necessary input from Meta’s software engineers.
Originally, React Native was targeted only to mobile platforms such as iOS and Android. It’s actually half-true that you can use the same code for web projects. The React Native project is not the same as ReactJS. The feel and look of the code stay similar but you need to have different code for the web and mobile frontend. However, you can share parts of the logic code.
There are also some, yet immature attempts to bring React Native for Windows and macOS.
The idea behind React Native is to offer developers the possibility to create natively-looking applications using JavaScript. Therefore, the controls used tend to look just like native components depending on the platform on which you run the app. This may be useful when the development of an app with a user interface matching the operating system is your goal. However, from our experience, the majority of mobile app owners prefer to have more personalized UI, adjusted to their brand. Below is a comparison of the same application running on iOS and Android.
The fact is that React Native has some major pitfalls. The pitfalls include complex navigation, extensive transitions, complex animations, and gesture handling. That’s why React Native is commonly used in applications that do not require that much. Such as consumer-facing apps (social network apps, news feed apps, e-commerce apps, etc.).
Those mainly performance issues are rather caused by React Native architecture. The current architecture bases upon the Bridge concept that is needed to link native modules with your app’s code. That ends up with underlying issues with unnecessarily copied (instead of shared) data and with employing native features.
If we would want to describe the architecture of every framework from this article, we would need to write a separate post about each of them. Therefore we will not go that much into the details. Nevertheless, the rendering engine responsible for drawing user interfaces is the heart of every framework. Therefore, it must be highlighted that a new C++-based renderer called Fabric is currently in the active rollout. Many of the mentioned aspects will be improved in the next React Native releases.
The goal of this article is not to teach you how to code in a particular technology, but still, it’s nice to see a piece of code. Just to see what suits you better and how the technologies differ from each other. Below you will find the main App.js file from React Native TODO list.
import React, {useEffect, useState} from 'react'; import { StyleSheet, Text, View, Keyboard, TouchableOpacity } from 'react-native'; import InputSection from './components/InputSection'; import Task from './components/Task'; export default function App() { const [task, setTask] = useState(); const [tasks, setTasks] = useState([]); const getTasks = async () => { fetch('http://62766064bc9e46be1a160ec4.mockapi.io/api/v1/tasks', { method: 'GET', }) .then(response => { return response.status == 200 ? response.json() : []}) .then(responseData => { return responseData }) .then(responseData => { if (responseData) { setTasks(responseData) } else { setTasks([]) } }) .catch(err => { console.log("fetch error" + err); }); } const handleAddingTask = () => { Keyboard.dismiss() if (task) { fetch('http://62766064bc9e46be1a160ec4.mockapi.io/api/v1/tasks', { method: 'POST', headers: { Accept: 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ task: task, }) }) .then(() => { getTasks()}) setTask(null) } } const completeTask = (id) => { fetch('http://62766064bc9e46be1a160ec4.mockapi.io/api/v1/tasks/' + id, { method: 'DELETE' }) .then(() => { getTasks()}) } useEffect(() => { getTasks(); }, []); return ( <View style={styles.container}> {/* Tasks section */} <View style={styles.tasksSection}> <Text style={styles.tasksSectionTitle}>Your tasks</Text> <View style={styles.tasks}> { tasks.map((taskItem) => { return ( <TouchableOpacity key={taskItem.id} onPress={() => {completeTask(taskItem.id)}}> <Task text={taskItem.task}/> </TouchableOpacity> ) }) } </View> </View> {/* Input section */} <InputSection style={styles.inputSection} onChangeText={text => setTask(text)} onAddClicked={handleAddingTask()} text={task}/> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#EBEBEB', }, tasksSection: { paddingTop: 80, paddingHorizontal: 20, }, tasksSectionTitle: { fontSize: 24, fontWeight: 'bold', }, tasks: { marginTop: 30, }, });
As you can see the syntax is indistinguishable from that one used in ReactJS. React Native is based on a JavaScript syntax extension called JSX. It should be natural or easy to learn for most of the web developers out there, which is actually a benefit of React Native as you can find software developers from the large JavaScript community. Below is the screenshot of the React Native application that is the result of running this code.
You can find this piece of code and other used in this article on Scythe Studio GitHub.
Let’s continue the comparison of the best cross platform development frameworks with the Qt framework. The first stable release of this framework of C++ language took place back in 1996. Initially, this technology was used for programming desktop apps and it is one of the 4 best frameworks for cross platform desktop development.
In 2013, Qt released a new version with an official support of Android and iOS platform. From that moment, it’s being recognized as an excellent really cross platform solution with great support of not only desktop platforms, but also mobile apps and embedded. Currently, thanks to Qt for WebAssembly, you can also distribute your application to be part of your website.
In the case of Flutter and React Native, desktops are not really well-supported. The support for desktops in those frameworks is a bit fresh and experimental, so not really stable. On the other hand, Qt targets desktop platforms for over 20 years.
Also, Qt has the best support for various platforms compared to other frameworks. Qt offers excellent support for a wide range of embedded devices and it has officially supported iOS and Android for a long time already. That’s why we call it a really cross platform framework.
Qt is not the best choice for regular website frontend development, but it’s now possible with the already mentioned Qt for WebAssembly. As Qt is multipurpose technology, you can also use it to develop a backend REST API service if you already know C++.
As aforementioned, Qt is a framework of C++ programming language bringing a lot of new features to it. C++ is often blamed for being an outdated programming language. Actually, programming in C++ is convenient as C++ evolves and there are new standards of this language. There are solutions and techniques to simplify memory management in C++, but still you need to remember to use them.
Modern Qt applications consist of two elements, user interface implemented using QML and application’s logic written in C++. This approach allows you to make a clear distinction between these two aspects of every application. Moreover, having heavy calculations and app’s logic on the C++ side means you will benefit from the performance of this programming language. From one of our previous posts you can learn how to integrate QML and C++.
import QtQuick import QtQuick.Controls Window { id: root width: 400 height: 200 visible: true title: qsTr("Say Hello to Qt World") color: "darkgreen" Button { id: button anchors.centerIn: parent text: qsTr("Change background color") onClicked: { root.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1) } } }
As you can see on the code snippet above, QML syntax looks a bit like a mix of JSON and CSS. Nevertheless, QML items have other sets of properties comparing to CSS attributes. QML is an example of declarative programming language, which means that you just define what you would like to achieve without a need to describe the flow.
QML employs JavaScript for scripting, so if you or your team already know this programming language, you can utilize some of your skills in order to boost your Qt development.
Qt has great documentation, but it should be said that it’s focused a bit too much on API and not on implementing actual solutions.
The framework is based on modules providing sets of specific functionalities. The most important modules are those from the Framework Essentials category. They are the backbone and the spine of the Qt framework. Among those most important modules you find components like Qt Core that provides non-graphical classes and also modules allowing rich UI implementation using both C++ and QML languages.
Qt offers a huge number of additional functionalities included in the framework itself. The graphic above presents only part of dozens of Qt Framework Add-Ons. All of those Add-Ons are shipped within a Qt framework allowing you to rely on it in order to implement unconventional features like 3D drawing, accessing Bluetooth or NFC hardware or devices sensors. The full list of Qt framework features can be found on their official website. Thanks to the wide number of modules there is no need to often use native code.
The are also many C++ third party libraries that you may want to use in the project.
Below you will find main.qml file that is the heart of the Qt TODO application created for the purpose of this article.
import QtQuick Window { id: root visible: true color: "#EBEBEB" Item { id: tasksSection anchors { fill: parent margins: 20 topMargin: 80 bottomMargin: 0 } Text { id: tasksSectionTitle font { bold: true pointSize: 24 } text: qsTr("Your tasks") } ListView { id: tasksListView anchors { top: tasksSectionTitle.bottom topMargin: 30 bottom: parent.bottom } width: parent.width model: tasksModel spacing: 20 delegate: Task { width: tasksSection.width height: 54 text: title onClicked: { restApiClient.sendDeleteTaskRequest(id) } } } } InputSection { id: inputSection anchors { bottom: parent.bottom left: parent.left right: parent.right margins: 20 bottomMargin: 30 } onAddClicked: function (task) { restApiClient.sendPostTaskRequest(task) } } Component.onCompleted: { restApiClient.sendGetTasksRequest() } }
You can notice that we refer to restApiClient and tasksModel that are not declared here in QML. That’s because we wanted to give you a brief overview of QML and C++ integration. The goal was to separate logic (communication with the web server and operations on data, respectively) as it is the most important Qt QML good practice to follow.
void RestApiClient::sendGetTasksRequest() { QNetworkRequest request(k_endpointUrl); QNetworkReply *reply = m_networkManager->get(request); connect(reply, &QNetworkReply::finished, this, [=]() { if (reply->error() == QNetworkReply::NoError) { parseGetTasksResponse(reply->readAll()); } reply->deleteLater(); }); }
Above you can see what one of the C++ functions executed from QML, looks like. C++ is a great place to use native components, so if you need to write or access native code you will need to do it from C++.
RestApiClient restApiClient; TasksModel tasksModel; Object::connect(&restApiClient, &RestApiClient::tasksReady, &tasksModel, &TasksModel::synchronize); QObject::connect(&restApiClient, &RestApiClient::taskRemoved, &tasksModel, &TasksModel::onTaskRemoved); engine.rootContext()->setContextProperty("restApiClient", &restApiClient); engine.rootContext()->setContextProperty("tasksModel", &tasksModel);
This demo presents a lot of other Qt features like Signals & Slots and Model/View programming, so feel free to visit Scythe Studio Github profile to get familiar with these concepts.
We seriously considered whether to post a Qt, Flutter and React Native performance comparison in this article. It’s obvious that C++ will have the best results for extensive calculations that require significant CPU usage. However, we wanted to test something that is an actual case as executing demanding calculations is not common for mobile apps. We wanted to test something that really affects the end user. Therefore, we decided to test rendering as UI is something the user deals with all the time. Therefore, we implemented three, identical simple applications that render 200 images in a grid and rotate them in a loop. It’s not the perfect test, but it mimics screens with multiple animations
The Flutter, React Native and Qt performance comparison was done with the following setup:
After running the app there were actually no clearly visible rendering issues. Despite demanding task, every app behaved nicely. Below you will find the GameBench profiling results. They are similar to each other. The differences are caused by various approaches used by frameworks created. The low CPU usage of Qt deserves a special mention.
You can find apps used for the benchmarks on Scythe Studio GitHub profile.
What’s the easiest cross platform development framework to learn? There is no simple answer to that question as it depends on the background of the particular developer. Nowadays, most beginners who just want to learn how to code, start with JavaScript programming language. Therefore, it is a big thing for React Native as its precursor – React.js, is a web JavaScript framework. JavaScript knowledge is also a benefit if you consider learning Qt as it’s used as a scripting language in QML.
You already learned from this article that Qt is a C++ framework. C++ exists on the market for quite a long time and it’s constantly developed with new, modern features. However, it’s a fact that C++ is not the easiest language to learn compared to JavaScript. With Qt, it is actually very convenient to use, but if you prefer other programming languages you can still use Qt with them. How? Qt has bindings to a lot of other languages, including Rust, Go, Java and Python. In particular, Qt for Python is quite popular.
From what you could see in the code snippets above, QML has readable and easy-to-learn syntax. It’s pretty easy to learn as well. Flutter on the other hand has the most specific approach for UI defining, so at the beginning, it may be though to learn this. However, like other frameworks, Flutter has great documentation and learning materials, so you should manage to learn it. The fact that you should think about is that Dart (Flutter’s programming language) is actually not used often outside the framework. For those reasons, your Dart programming skills may not be useful when not combined with Flutter.
With all you learned so far, you should have a good overview on what all of those three frameworks look like. We covered the performance, support for particular platforms, maturity and functionalities. Besides that we should mention another topic, which is releases policy. Flutter since the first stable release in 2018, already had two new major versions. That’s a problem for long-term projects as the life span of such projects is counted in years. Every major update brings significant changes to the framework and developers need to deal with it what increases maintenance costs. Not to mention the hell with dependencies that also rely on particular framework version.
The Qt’s framework releases policy is a golden standard in this matter. They provide high-grade backward compatibility and every three minor versions (usually there are around two minor releases during a year) there is a Long Term Support version. Those special versions are getting improvements and fixes for three years, which is quite important for companies with long project life-cycles.
The other topic is under which license particular framework is available. Flutter is licensed with a New BSD license, which makes this framework free to use with some minimal restrictions. React Native uses a MIT license that is very liberal and you can use it for free. Qt is actually the only solution with multiple licenses including commercial one. Some less usual modules are not available in free, open source version. That’s because Qt has other business model. They do not provide services like Google, so they have to earn on licenses sales.
The tooling is another topic that we should cover. Both React Native and Flutter have great support in multiple IDEs. The best Qt development experience you have when using the Qt Creator IDE, but there are also plugins to develop using Visual Studio or Visual Studio Code. Some people set up CLion for Qt development.
Qt framework comes with a tool that closes that gap between designers and developers. Qt Design Studio is a WYSIWYG dedicated designer that can be operated by UX/UI designer. It produces QML code that is then used in the actual application. Qt also have a plugin to export Figma design with just a few clicks.
Let’s then conclude deliberations on the best cross platform app development framework with a comparison table.
In multiple cases going cross platform is a more effective solution than native development. The development process for native applications takes more time and costs more. That is why there is a need for cross-platform development. Let’s answer what to choose – Flutter vs React Native vs Qt for cross platform app development.
Qt has the widest spectrum of supported devices. It’s also the most comprehensive solution from today’s list as it has plenty of built-in modules. With Qt you can also benefit from thousands of open-source plain C and C++ libraries, which is a huge pros as well.
React Native is backed by a giant – Meta Inc. and it’s under serious re-architecture that shall improve the performance of this framework and fix other pain points. It’s also a JavaScript framework, so you may benefit from the huge popularity of JS while looking for people to help you develop the project.
Flutter in which Google invests a lot and about which community is very enthusiastic. There is definitely a great future ahead of all of these frameworks. So, before choosing the best framework for your project, you should ask yourself those three questions.
In fact, there is no significant common ground when it comes to the functionality of these frameworks. Nevertheless, all of the technologies from this article should work well for the popular use case which is displaying and operating on the data fetched from the web server. However, if you need to have unusual features like 3D, sensors, Bluetooth or NFC, you should take a look at Qt.
The support for iOS and Android is equally good on Flutter, React Native and Qt. If you develop a mobile application only, React Native and Flutter would be great to use as there are used more often in this area.
However, if you need a production ready desktop or embedded framework, Qt is the way to go.
In the software development industry, time is an actual currency. For that reason, (unless you’re not in a hurry or you want to learn something new) you should consider a technology that is the closest to your tech-stack as a developer or to the skills of software engineers in your company.
After answering honestly to yourself, you should have a clue what you need for your next project. Remember about keeping an eye on our blog in order to be updated on the news from the cross platform software development world.
Let's face it? It is a challenge to get top Qt QML developers on board. Help yourself and start the collaboration with Scythe Studio - real experts in Qt C++ framework.
Discover our capabilitiesWhether your product is in its early stages or ready to launch, the iPhone platform is a compelling option to […]
Welcome to another blog post in the series discussing a specific technical topic. Today, we will take on one form […]
Scythe Studio has been on the market since 2020, delivering numerous projects for satisfied clients and navigating a significant learning […]