Flutter vs Qt QML for cross-platform app development

If you read my articles you can see me mostly writing about Qt QML or Node.js with which I have been working for many years. However, I do like to look and evaluate other frameworks and technologies, and this time it came to Flutter with which I’ve been working in my free time for the past year.

In this article we are going to take a high level look on both Flutter and Qt QML and see what they can provide for when you consider doing native app development for multiple platforms.

Flutter

Flutter is Google’s UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase.

Flutter is a relatively new framework written from scratch (who doesn’t like a new framework?) using a custom C++ engine based around 2D Skia library and Dart language. Skia is a 2D C++ library also developed by Google and is used by many well known projects like Android, Chrome and Firefox while Dart is (now) a client-optimized typed programming language that can compile to native code and is also made by Google so one can expect that to be a good base for a UI framework.
The funny story for me already is that I came in touch with Dart at early beginnings while looking and languages good for backend development. How times have changed.

In any case I’ve been following Flutter development from the early beginnings to which I stumbled upon from Google Fuchsia demos back in 2015/16. First thing I’ve seen was mentions of targeting stable 120 FPS which sounded…interesting. There is nothing really to prevent you from trying this, but nobody was really targeting that given the state of mobile platforms. It was time when I would say having stable 30fps with nice animations on a mobile app would be a miracle.
This made me more than happy to do a test run in 2020 with a current version 1.17.0 especially since in many ways it shares the vision of Qt QML but it also has additional nice features and things like hot reload.
You update a part of your code and it automagically updates that part of the app on your device while retaining state! What is not to like?

Qt QML

Qt framework (Qt QML is just one part of it)

The Qt QML module provides a framework for developing applications and libraries with the QML language.

I just realized that this might be quite confusing if you are not part of the Qt ecosystem. So lets try to simplify.

There is Qt Framework itself written in C++ which provides APIs and modules for many things . It has existed for more than 20 years and is currently at version 5.x while next year 6.0 should come out. Obviously many hours of work went into it and it was adapted and improved through the years.

One of modules it provides is Qt QML which gives QML language and engine needed to make UI applications together with JavaScript.
You will also see a lot of mention of QtQuick which is QML standard library but you will find it interchangeably used as name for apps written with QML. Worth noting is that QML as a language was introduced more than 10 years ago.

There is also a Qt GUI module which is a different and older but still supported way of creating UI apps but that is developed in C++ and not relevant for this article.

So what we are looking here is a QML declarative language that you easily combine with JavaScript to make UI applications for many platforms.
It can be extended by C++ and rest of Qt modules and depending on your plans and requirement that might be needed or wanted.
In most of previous examples I showed here we used only QML and JavaScript with no C++ extension.

For further info as always my recommendation is to read Qt docs as they are super detailed on most of the topics and you can also look at Youtube videos like the one made about QML by KDAB.

Programming language

Flutter

Flutter system overview

So Dart.

Dart is a client-optimized language for fast apps on any platform

First interesting thing is that Dart 2.0 was refined more for Flutter usage because declarative UI interfaces is not what Dart was made for. One tiny annoying example is having mandatory semicolons. This is not something I would expect in a modern language and especially not in a declarative one that is used for UI development.
And no, I don’t think that one and a half example that you know about having issues with optional semicolons are reason to have them.

The most important part here is a that using Flutter you will be dealing with only one language. Just Dart. Only if you want to contribute or fix bugs on the engine and platform integration is when you will do it in other languages. But for usual framework user you will be in your Dart box.

That means while targeting Android and iOS platform if some functionality is missing in existing Flutter framework you will need to write that yourself using native language of that platforms, i.e. Java / Kotlin or Objective C++ / Swift — which should be obvious and expected.

There is also obvious benefit to having one language and I’m happy to see somebody doing exactly this compared to all the HTML frameworks that have to deal with legacy HTML limitations since HTML was also not made for UI apps development. Or even worse when some UI frameworks written from scratch start emulating HTML or CSS syntax to help people to work with framework. Usually it just ends up being custom solution with custom quirks not helping anybody.

I do personally wish Flutter was done using some other existing language instead but it is quite clear why Dart was chosen.

Qt QML

When it comes to Qt QML things to get a bit more complicated.

QML was made from scratch to be used for UI development and that really shows as we will soon see from the syntax. It also implements JavaScript support (soon hopefully TypeScript) which integrates nicely with QML.
However, that does mean that while with Qt C++ APIs you get support for many things and also access to whole C++ ecosystem, with QML you will get less. So if something is not supported in QML plugin you will need to extend it. Good thing is that is easy to extend but I realized for many people that is just too much. QML, JavaScript and C++? It would for sure help if there is an official package manager but we will touch on that soon.

I think it’s worth it but I understand the intimidation, especially if you have been working only with C++ or JavaScript right now. When I was looking into Dart few years back it seemed too much just because it’s a new language for me so it is a clear drawback if you would need to learn 3 potentially new languages.

Platform support

I would say that we are kinda lucky that today there are only 2 main mobile platforms that need to be targeted. If you still had active Windows Phone OS and Blackberry 10 OS (which I both miss), having 4 teams just to develop one app for each platform wouldn’t sound so feasible. On top of that you still probably need to include a Web version optimized for mobile usage.
In any case even with two platforms it’s twice the effort that needs to be done. And is not necessarily just development. It’s the whole platform setup, deployment and testing that comes after it. So having any part in your stack that can cover multiple platforms just sounds attractive.
Facebook decided to abandon HTML WebView approach and go with platform native apps in 2012 for their Facebook app and then later started React Native framework as proposed solution to targeting multiple platforms from one code-base. They also wanted to solve issue of mobile apps having a native look. That mostly came from issue of Web apps not looking as well as a native apps due to not using native widgets. They would emulate native look but that would then never look really the same.
Nobody was sure if that was an issue but everyone was saying it. I would argue that the issue was because those apps were not performant enough so even if it would look the same and animate the same it wouldn’t really perform the same.
It’s worth mentioning that both Flutter and QML also don’t use native components. They emulate the look by drawing custom widgets that can be used on any platform.
Reasoning is that today expectation is that people want a custom branded UI so native widgets are not that interesting beyond few components.

To sum up, idea of having one framework that covers multiple platforms is always interesting. Write once, deploy anywhere. That tiny lie just sounds so great.

Flutter

Yes but not really

Flutter officially at this moment, in stable release, supports only Android and iOS platforms. Web support is there in beta release and I can say it did work for me out of the box after switching to beta channel . It’s also quite nice way to develop and test apps if you don’t want to trouble with a real device or emulator but want to see how your app will look. However many plugins available have no web support yet and it wants to gets stuck on hot reload in my experience.

Of course if there was desktop support you could build an app just for a machine you are working on and use also that for a quick preview. And that is actually being worked on. There is alpha release for MacOS with Windows and Linux support being in early stages.

So at this moment it is limited in stable support but it’s good to know that there is plan to expand it even further. This does however mean, by my experience, that there will be missing features and bugs in early releases for those platforms.

Qt QML

Many platforms and supported functionality

Qt is just there for so much longer that it supports almost all known and interesting platforms you can develop an app for as described in official docs.

Windows, Linux, MacOS, iOS, Android which also covers tvOS/watchOS and Xbox One. Web platform is indeed also officially supported by Qt for WebAssembly which I showed in dedicated article.

Comparing these two Qt QML is a clear winner at this moment. Flutter is quite fresh so it will take time to implement a stable support for all platforms.

Qt also integrates more deeply and providers more feature on platforms. For example, I was in a need of audio player which is readily available in QML and is developed by Qt while with Flutter you depend on quite young 3rd party packages. Yikes.

Show me the code

Think declaratively

So in both you will be developing using declarative programming paradigm.
I would say (thankfully) this is nothing new. You can see Android introducing that with Jetpack compose (not that there are no other libraries for it). On the Web React is well known and on iOS you have SwiftUI.
For backend to manage all bits of your system in a similar way you can find it in AWS CloudFormation or Terraform.

In short example with imperative approach it a framework might look like this, it defines the step you take to get to the state you want:

// some very clean imperative UI frameworkint counter = 0;Window window;
ContainerColumn container;
Text text("you should click a button");
Button button;
button.setText(counter);
button.setOnClicked(() => {
button.setText(counter++)
});
container.add(text);
container.add(button);
window.add(container);
window.show();

It is simple enough to figure it out although some things are not really defined so it depends on the framework. For real examples you can check Android SDK, iOS UIKit or even Qt GUI widgets.
This is what people are generally used to (if you started UI development like more than 5+ years ago), just because that is how most of underlying code is developed.
Unfortunately this is not how nice your code will look like. Once you need to manage a bunch of local states and use it in different place and have to split the code it is going to be on you to make that happen in a nice way. What to update when and what are the steps to take to get from one state to the other.

Moving to declarative framework it can look like this, where we define the state we want and framework figures out the steps to take to make it happen:

Flutter

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
void main() => runApp(Counter());class Counter extends StatefulWidget {
@override
MyApp createState() => MyApp();
}
class MyApp extends State<Counter> {
int _counter = 0;
void _increment() {
setState(() {
++_counter;
});
}
@override
Widget build(BuildContext context) {
return WidgetsApp(
color: Colors.black,
builder: (context, int) {
return Column(
children: <Widget>[
Text('Clicked: $_counter'),
RaisedButton(child: Text('Click me!'), onPressed: _increment),
]);
},
);
}
}
Our tiny Flutter app in web browser

I’ll admit the code might look a bit intimidating if you didn’t work with declarative frameworks before but it shines when you start creating complex apps. Indeed, few lines of imaginary framework code as we shown above would get you the same thing but unfortunately it’s hard to make a living from counter apps anyway.
For app state management guide that will explain you the full power behind them in declarative frameworks please read official docs on the topic. It’s a great read explaining real uses cases and many state management options to choose from. Another benefit that in declarative framework it’s usually much easier to see the layout and child / parent relationship from the code structure. Although Flutter does make it quite explicit with child / children properties.

This is also simplest I could make it in Flutter while alternative would be to use MaterialApp but that ends up in even bigger example. I also don’t think MaterialApp should be presented as default. I didn’t use material in my app since I was doing completely custom interface that is more quiz / game like.
And material being considered default for mobile and web platforms by Flutter team is not really selling point for me.
I believe that will be the case even less once you add full desktop platform support so I wouldn’t make most of my tutorials around it.

QML

import QtQuick 2.14
import QtQuick.Controls 2.14
ApplicationWindow {
property int counter: 0
Column {
Text { text: `clicked: ${counter}` }
Button { text: "Click me!"; onClicked: counter++ }
}
}
Our tiny QML desktop app

Yeah, it is the same example and it’s a working QML app. Hierarchy is obvious and is made from the structure you written. So Column is child of Window and Text and Button are children of Column. Hard to miss. It already shows how well QML fits UI development.
Due to it’s property bindings local states are a breeze to handle. Something that you might be familiar with if you worked with MobX (which you can also use with Flutter).
It also has a separate State element and a full Declarative State Machine Framework for more complex state handling.
For real apps you will want to look at best practices and Felgo provides quite good tutorial on how you might do an whole app architecture.

Worth mentioning is that QML also supports Material design, but you know, it’s just a style and not how you name most of your components.

Choice of Dart on UI was never appealing for me given that Dart was not originally intended to be used as a declarative fronted UI language.
Of course, these examples are way to small to get a full picture and with complexity I think the difference really starts to show. You should definitely check out Flutter examples and QML examples for real difference.
I also found readability in Flutter is an interesting topic for discussion (1, 2, 3) which you won’t find for QML.

In any case Flutter and Dart are getting better and better with every release and I expect it will improve even more regarding the syntax as it was done already with new Dart releases.
Editors are also really helping in this regard so soon you will probably get fully used to it.

Editors

VS Code with Flutter and hot reload

Flutter has great editor support. Android Studio and Visual Studio Code are considered fully supported. They support hot-reload feature that makes for a nice way of seeing your changes in real-time while also keeping the state of the app. Android studio is a bit too sluggish for my personal taste but they both do work as expected and they both have good vim emulation support.
I do use Visual Studio Code otherwise so I find it very nice that Dart and Flutter are very well supported.

QtCreator with Help mode

Qt QML is officially supported with Qt Creator. It’s a nice editor that is fully integrated with the whole Qt ecosystem so it’s best to recommend. It has a good vim emulation support and everything you would expect like debugging / profiling and managing of multiple platforms.

It doesn’t have hot reload though. Not in regular free release. There are some 3rd party solutions but your mileage my vary.

You can use others editors but you will probably miss some part of integration and will need to make it work yourself.
An example of using Visual Studio Code you can find at KDAB pages.

Qt Creator also comes with a visual design mode which you can use for developing your Qt QML apps and it looks quite powerful. However, I haven’t personally used it yet.

Qt Creator design mode

Functionality and packages

At some point you will need to extend functionality provided by your language standard library or framework.
Some languages like Go do have quite a big standard library including many things like database interface library, http server and router or even zip archiver. They also (finally?) have a stable package manager so adding and using a new package is very simple.
Node.js has less things in standard modules but does have npm and a central repository. Rust has Cargo. PHP has composer. And Dart has pub.

Flutter

pub.dev

Flutter packages are published as Dart packages under their own category on pub.dev and are very easy to install. Obviously you can and will use Dart packages for any functionality you may need outside of Flutter provided functionality.
But coming specifically to Flutter and UI packages most of them are provided on pub.dev. Even Camera plugin which is developed by Flutter team is provided as a plugin that you download. Actually 47 external packages at this time are maintained by Flutter team.
This provides benefit of providing updates regardless of Flutter release schedule.

Installation and usage are very simple. Once you add the package with a name and version to your app .yaml Android Studio or Visual code can automatically download and install it and after few seconds you are ready to use it. Nice. And yeah flutter pub get is not more complex.

If you do work more with Flutter you will find most of features as part of 3rd party packages which is kinda expected but you will see that many packages are missing functionality and they are not even at 1.x release. Given that Flutter is quite young at only at 1.x release that is also expected.
Like I mentioned before, audio player is also not included and your luck with 3rd party plugins will vary.
However, that will improve with time and the fact that you are one line away from using them is unbelievable time saver.

Qt QML

Qt marketplace

Qt QML comes with a lot of builtin modules. They all contain a bunch of functionality you might or might not expect and I rarely missed something there. And given that Qt and Qt QML exist for quite longer time they are much more stable and feature rich than what you will find in Flutter or other frameworks.
Of course, when needed I would extend it manually by exposing Qt C++ modules to QML myself.

However, once you cannot find something as part of standard C++, Qt and QML, then you are in a hell of a ride. Especially if you are developing for multiple platforms and you want to include your dependency as a library file for all of them.

First thing to mention is that C++ has a bunch of dependency managers but nothing official and description of the current state needs and article on it’s own. Hopefully this improves with modules introduction. For now your best bet are header only libraries.

Qt obviously has a module system and and a documentation on how to make your own plugins but no package manager that would help you with installation of those from some remote location. So if you find some Qt or QML plugin online you are mostly at the mercy of people writing instructions for that plugin to get it to work.
There is (was?) nice idea of qpm (yeah, qt pakage manager) but unfortunately that never really took off (being package manager for Qt written in Golang didn’t help). Idea is to have it simply install a package with qpm install package .

This is by far one of the biggest drawbacks of working with Qt QML because of the constant reinvention of the wheel.
Yes you can Google for some Qt QML packages and then hopefully finding something that you need, but comparing that to Flutter ecosystem and simplicity of managing packages it’s not even a contest.

Qt marketplace was recently introduced but doesn’t solve most important of those problems yet. Hopefully that changes soon in the future.

License

Favorite topic of any Qt developer. This wouldn’t even need a section if there wasn’t a special case of Qt licensing and controversy made during the years and especially lately. This would need a separate article so here we are just going to touch the most important bit.

Flutter permissive license is described here. Packages have their own license but most follow the same BSD version. You can use the library for commercial purposes without making your code open source.

Qt licensing is described here. In general you can use Qt QML under LGPLv3. Some modules are available only under GPLv3 which means you cannot make closed source commercial projects with it. One example is Qt WebAssembly. Other is Qt Virtual Keyboard.

There is more to it and that is not the fun part. One of the videos released on the topic you can find here (Complying with the Requirements of the GPL/LGPL v3 License).

Obviously developing a great library like Qt takes resources. So this is the choice made by The Qt Company as a way of supporting it. Unfortunately there is also no attractive commercial indie license that one could use.

Google on the other hand has resources to provide library like Flutter under more permissive license and is obviously going with a different business model and that does attract a lot of users rapidly.

Conclusion

I prefer declarative UI frameworks. And personally I actually dream about a good modern C++ only declarative framework (something I’ve seen recently in Elements).

Both Flutter and Qt QML frameworks provide great documentation so for technical bits that is the place to go.
Actually, it’s worth mentioning that before I used Flutter I considered the Qt framework having the best documentation one might find around.
Flutter documentation is however equal or better in some aspects with working clickable examples and even videos explaining some concepts.

One additional thing I like in both and that I realized many people are not aware of are Implicit Animations for Flutter or Behavior for QML. In my experience they will cover 90% of the usual animation needs in a very simple way so be sure to check them out.

For me, Flutter is a clear winner when it comes to having just one language, having great package ecosystem and a more developer friendly license. QML wins for sure on development speed and simplicity but also on project stability.
Both are great choices for modern cross-platform app development so as always choice will be yours.

Happy coding.

Developer by day, architect at night — never satisfied

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store