How Flutter Programs Run Under the Hood: Simply Explained

Anmol Singh
4 min readMar 23, 2024

Flutter applications are built using the Dart programming language. Dart is a free, open-source, object-oriented language developed by Google. One of its key strengths is cross-platform development.

This means you can write code once and deploy it on various platforms like mobile (Android, iOS), desktop (Windows, macOS, Linux), and even the web — all with near-native performance. This is achieved by compiling Dart code into machine code specific to the target platform.

Another key feature of Dart is its ability to make changes iteratively and quickly which is done through the use of the hot-reload feature. This feature allows you to see the result instantly in your running app.

The power of making changes on the fly is what we’ll be diving deep into today. Let’s see how dart code is transitioned into an app in the first place. But for that, we first need to understand a few terms.

1. Dart compiler:

The Dart compiler is a specific tool used to translate Dart code into either machine code or bytecode. Bytecode is a low-level code that is the result of the compilation(translation) of a source code which is written in a high-level language (Dart). It is processed by a virtual machine like Dart Virtual Machine (Dart VM) discussed below.

Types of Compilation:

a) Ahead-of-Time (AOT) Compilation: This process translates the entire Dart codebase into machine code specific to the target platform (like ARM for mobile devices). This is typically used for release builds in Flutter to achieve potentially better performance and smaller app size compared to JIT.

b) Just-in-Time Compilation (JIT): This is the primary technique used by Dart VM for compiling dart code on the fly. It translates small chunks of Dart code into bytecode (an intermediate representation of code) just before they are needed for execution by Dart VM. This approach allows for faster development cycles with features like hot reload in Flutter.

c) Dart to JavaScript Compilation: The Dart compiler, specifically dart2js, is used to translate Dart code into a combination of JavaScript and WebAssembly for web development.

Flutter provides three main build modes that determine how the Dart code is compiled and optimized for different purposes:

Debug Mode:

  • JIT Compilation: The Dart code is translated into bytecode on the fly for hot reload functionality.
  • Focus: Fast development cycles with rapid code updates.

Profile Mode:

  • Similar to Debug Mode: Uses JIT compilation for profiling purposes.
  • Focus: Gathering detailed performance data to identify bottlenecks.

Release Mode:

  • Optional AOT Compilation: The entire Dart codebase is translated into machine code specific to the target platform (ARM, x64).
  • Focus: Smaller app size and potentially improved performance for production apps.
  • No Debugging Information: Removed to reduce app size.

2. Dart Virtual Machine:

The Dart VM is the primary execution engine or interpreter for Dart code. It’s not a physical machine but a software layer that provides an environment for Dart programs to run. It acts as an intermediary between your Dart code and the underlying hardware (processor, memory) of your device. The Dart VM mainly uses Just-in-Time Compilation (JIT) for code execution.

Some of the other responsibilities of Dart VM include:

  • allocating and deallocating memory as needed by the running Dart program.
  • automatically reclaiming memory that is no longer being used by the program.
  • enforcing security measures to prevent malicious code from accessing unauthorized resources.

It is possible for hot reload to work because of JIT. It is so because the Dart VM can efficiently update only the modified parts of the code with the newly JIT-compiled bytecode. This allows the app to reflect the changes almost instantly without a full restart.

For AOT compilation, especially in release builds, a common approach is hot restart. This involves restarting the entire app with the newly compiled machine code after code changes are made. While not as instantaneous as hot reload with JIT, hot restart provides a way to test changes in a more optimised environment (AOT) without requiring a full app rebuild from scratch.

Now let’s see how it works practically. When you write Dart code and run it, several processes occur depending on the development stage and compilation mode you’re using. Here’s a breakdown of the major phases:

1. Development Time:

  • Project Setup/Initialization
  • Writing Dart code using an IDE or code editor
  • Code Analysis
  • Testing (writing and running unit, widget, integration tests)

2. Compile Time:

  • Dependency Resolution
  • Build Process (including code generation, asset bundling, invoking Dart compiler — AOT or JIT)
  • The build process can happen through 3 modes: Debug mode, profile mode & Release mode.

3. Runtime:

  • Device/Emulator Deployment
  • Hot Reload (executing already-compiled bytecode with Dart VM)
  • AOT Compilation (executing machine code directly on the processor)

Conclusion

Flutter’s ability to run programs efficiently across multiple platforms is a testament to the powerful combination of the Dart programming language, the Dart compiler, and the Dart Virtual Machine. The seamless integration of Just-in-Time (JIT) compilation for rapid development cycles and hot reload, along with the option for Ahead-of-Time (AOT) compilation for optimized performance and smaller app sizes, caters to the diverse needs of developers throughout the app development lifecycle.

As appeared on anisingh.com

--

--