Flutter is an open source cross-platform app building framework created by Google, that uses the Dart programming language. It allows for the development of apps for iOS, Android, the web and desktop, all from the same codebase (as opposed to using separate programming languages and tools for each), greatly reducing development times and complexity.
This results in developers being able to deliver fast apps that feel natural and embrace the unique interactivity options of each platform (for example, gyroscopic control on mobile and hover interactions on desktops) while still sharing the majority of their code between app versions for these different platform.
This article is meant to be a starting point for people who want to know more about Flutter and Dart, and while it does try to simplify concepts as much as possible, I must warn you that it gets fairly technical at places, as I touch on how some of the core components of Flutter really work, and that can be complex, especially if you have little experience with software or programming. In conclusion, I encourage heavy Googling.
And with that being said, let’s start
How Flutter Works
Flutter supports multiple platforms. Yet, each platform Flutter supports has its own tooling, its own programming language(s) and frameworks, and even its own IDEs! So, you might naturally wonder, how does Flutter do it?
In simple terms, Flutter works by having “shells” for each platform, written with native code, which host the Flutter engine, while providing developers with the Flutter framework, which gives them a rich set of tools in the form of libraries in Dart, to more easily interact with the Flutter engine, and render pixels on the screen.
Going a bit more in-depth, these shells are native, platform-specific embedders, written in the native languages of each platform (Java for Android, Objective-C for iOS, etc.), which provide an entrypoint for the Flutter engine to connect with each platform.
The Flutter engine is itself written in C++, and provides a low-level implementation of the core APIs used by Flutter, by bringing together core technologies such as Skia, a 2D graphics rendering engine, and the Dart language runtime, as well as support for text layouts, network functionalities, I/O and plugin support, compilation tools, and more.
The Flutter framework, which is typically how developers interact with Flutter, is written in Dart, and builds on top of this foundation, providing a UI toolkit composed of a series of layers extending from basic foundational classes which handle animation and gestures, to comprehensive Material and Cupertino UI components that adhere to their respective design languages and implement all expected functionalities.
Note that during the compilation process, the Flutter engine itself is also compiled (using LLVM on iOS, Android’s NDK on Android, etc.) and included in the generated executable, with the app loading the Flutter library on app launch, and delegating graphics rendering, gestures, events etc. etc. to the compiled Flutter and app code.
Widgets, Widgets Everywhere
The Flutter framework, uses widgets as a unit of composition. A line you’ll find repeated everywhere when it comes to Flutter is that “Everything is a widget”. And its true!
Widgets are the building blocks of a Flutter app’s user interface, each an immutable declaration of a part of the user interface. Widgets are typically composed of many other small, single-purpose widgets that combine to produce powerful effects.
Furthermore, the “layered” architecture the Flutter framework uses also provides the ability to “tap into” and start building from any of these layers, each subsequent one allowing for more and more control over the rendering process, at the trade-off of increased code complexity, to allow for almost any UI or design to be replicated in Flutter.
Flutter App Architecture
Now, going to individual Flutter projects, each Flutter app comprises of, among others, 3 key areas:
libfolder, which is generally (but not exclusively) the area of the project where your Dart code resides, and where you’ll be spending most of your time
androidfolder, which houses the android portion of your app (the “shell” mentioned earlier), containing a relatively small amount of code when starting out, although this can increase a small bit with the addition of plugins that need to interact with native platform APIs.
iosfolder, which houses the iOS portion of your app (the “shell” mentioned earlier), containing a relatively small amount of code when starting out, although this can increase a small bit with the addition of plugins that need to interact with native platform APIs.
Of course, we can’t talk about developing Flutter projects without mentioning one of the most famous features of Flutter: hot-reload. It allows for code changes to be reflected on running dev versions of the app near-instantly, while preserving state and skipping the need of a full recompile, a benefit of Dart being JIT compilable.
Ecosystem & Dependency Management
Flutter is open source and completely free, with permissive licensing. Traits that have led to it having a thriving ecosystem of third-party projects for everything from accessing on-device camera APIs to pre-made Flutter UI components ready for use. Relatively small compared to those of its much older competition, but growing fast nonetheless.
These projects are housed in self-contained “packages”, and hosted, for free by Google, on its package registry, pub.dev. For Flutter projects, these packages are mentioned in a
pubspec.yaml file, with each new project being generated with one by default.
A Note On Performance & Support
Flutter’s pretty great, yes, but it does have drawbacks, some that will be fixed as time goes on and the framework matures more and more, and some that are more just a side-effect of how Flutter works, and don’t really have any workarounds.
And most, if not all of these remaining problems, stem from the simple fact that Flutter is not native to the platforms it runs on, and so, problems associated with that fact will always plague it, even if strides are made to mitigate them. Flutter, while fast enough for most apps, is not fast enough for apps building with a high number of UI refreshes per second. As such non-trivial games, or just software that by definition requires more processing power, such as design software or professional grade tools, which will always benefit substantially from being built natively (or at-least having the most computationally expensive parts be written natively).
Secondly, while Flutter has a budding plugin ecosystem that handles many native functionalities, including things such as access to camera APIs, location information, gyroscopic information, etc. also as a consequence of not being built natively, platform specific integration features, such as widgets on iOS and Android, Apple Pencil 2 support on iPads, or even features such as the new Live Activities on iOS 16 devices, need to mostly be developed individually for each platform, exactly the way they would if the app was not a Flutter app, making developers have to deal with the exact problem Flutter is designed to solve: not having to learn platform specific languages and tools.
Thirdly, due to issues with how Flutter projects target the Web platform and build for it, web projects are plagued by terrible loading times, jittery scrolling issues, and not-insignificant issues surrounding text rendering and layout, all of which lead to a subpar experience compared to that which Flutter delivers on mobile devices.
While these issues are somewhat small compared to just how many problem Flutter solves, they are not no problem at all, and require solutions that will only come with time, as the framework evolves and grows. So, if you need features such as those, or are building apps that will need a lot of on-device processing power, you might consider going native, as Flutter is not ready for those uses (yet).
We’ve covered a lot of ground here, from explanations on how Flutter itself works, to widgets and the structure of Flutter apps, and more. But perhaps even more importantly, there’s so many things we didn’t cover, such as how Flutter itself renders things onto the screen, how Flutter updates parts of the UI when the underlying data changes, how animations and tickers work, the more conceptual parts of widget composition, and so much more. There’s simply no way one article, or even a couple, could fully encompass the enormous array of systems that come together to make Flutter work.
But, we did properly scratch the surface, and in doing so, we’ve seen the many benefits of using Flutter, as well as some of the not too many areas where it is lacking.
And so, as with everything else, it can reasonably be said that Flutter is not a golden bullet for every single problem. It excels in building mobile apps that are not supposed to be too demanding on a device’s hardware, falling a little short when it comes to the other platforms it supports, as well as when using it in building performance intensive apps (for now).
But Flutter has a growing ecosystem, a passionate community and Google itself backing it, and a framework that is rapidly adding new features and improving older ones. So, while there may be problems here and there, its just going to get better and better.