Decoding Flutter: What Happens When You Run a Flutter App?
If you've ever started learning Flutter, you've seen the magic. You save your code, and instantly, your app updates on your phone. No recompiling, no restarting. But what's really going on under the hood? How does a single Dart codebase turn into a high-performance native app on six different platforms?
The official Flutter YouTube channel has a fantastic, if a bit unpolished, series called "How Flutter Works" that dives into just this. As part of our community support, we're going to break down the first episode,
What is Flutter? The Big Picture
At its heart, Flutter is a declarative, multiplatform UI framework created by Google and written in the Dart language.
Let's unpack that, because every word matters.
1. Declarative: The New Way to Build UIs
This is the most important concept to grasp.
- The Old Way (Imperative): Imagine giving directions to a taxi driver. "Turn left, drive two blocks, turn right, stop at the red building." You are manually controlling every step. This is how traditional UI development often worked: "find this text box," "get its value," "find this label," "set its text to the value." You manually change the UI.
- The New Way (Declarative): Imagine holding up a picture of your destination. "Take me here." You describe the end result, and the driver (Flutter) figures out the best way to get there.
In Flutter, you write code that describes what your UI should look like for any given state. Your entire UI is a function of your application's state (UI = f(state)).
When that state changes (e.g., a user logs in, a counter is incremented), you don't manually change the UI. You just tell Flutter the state has changed, and Flutter rebuilds your UI from scratch to match that new state. It's incredibly fast, and it eliminates an entire class of bugs where your UI and your state get out of sync.
2. Multiplatform: One Codebase, Many Targets
This is Flutter's famous promise. You write your application once, and Flutter can compile and run it on:
- iOS
- Android
- Web (running in any modern browser)
- Windows
- macOS
- Linux
It even extends to other environments like TVs (LG's webOS) and embedded systems. This is possible because Flutter doesn't rely on the native UI components of each platform. Instead, Flutter brings its own entire rendering engine and paints every single pixel to the screen.
3.Dart: The Secret Sauce
Flutter is written in Dart, a language also invented by Google. Dart is uniquely suited for the job:
Client-Optimized: It's designed for building user interfaces.
Compiles to Native Code: For mobile and desktop, Dart compiles directly to fast, native ARM or x86 machine code. This is why Flutter apps are so performant—there's no JavaScript bridge or interpreter slowing things down.
JIT and AOT Compilation: Dart has two compilation modes.
Just-In-Time (JIT): Used during development. This is what powers the next feature...
Ahead-Of-Time (AOT): Used when you build your app for release. This makes your final app fast to start and highly optimized.
The Magic: Stateful Hot Reload
The JIT compiler is the magic behind Stateful Hot Reload.
When you're developing and you hit "save," Flutter injects your new code into the running app in less than a second. It then just reruns the build method (more on that in a second).
Crucially, your app's state is preserved. If you're 5 screens deep in your app and have data loaded, you stay on that screen, and your data remains. You just see your new UI changes instantly. This isn't just a gimmick; it fundamentally changes the speed at which you can develop and experiment.
The Core of Your App: Everything is a Widget
In Flutter, your entire application, from the app itself to a button, to a piece of text, to padding, is a widget.
A widget is an immutable blueprint. It's a lightweight object that just holds configuration. You compose these simple widgets together to build complex UIs.
Your main job as a Flutter developer is to write classes that extend Widget and implement one all-important method: build.
The build Method: Describing Your UI
The build method is where your declarative UI comes to life. Its job is to return a widget that describes what this part of your UI should look like.
It receives one critical parameter: BuildContext. This object is your widget's "place in the tree" and lets you find other things, like the screen size, the current theme, or an InheritedWidget (a topic for another day).
A Great Example: The Declarative Counter
The classic counter app is the perfect way to understand this. The video explains it, but let's write it out to make it crystal clear.
import 'package:flutter/material.dart';
// We start with a StatefulWidget because its data (the count) needs to change.
class CounterPage extends StatefulWidget {
@override
_CounterPageState createState() => _CounterPageState();
}
// The "State" object holds the changeable data and the build method.
class _CounterPageState extends State<CounterPage> {
// 1. This is our "state".
int _counter = 0;
// 2. This is a function that MODIFIES our state.
void _incrementCounter() {
// setState() is the magic trigger!
// It tells Flutter: "My state has changed, you need to re-run the build method!"
setState(() {
_counter++;
});
}
// 3. This is our declarative build method.
// It DESCRIBES the UI based on the current state.
@override
Widget build(BuildContext context) {
// Every time setState is called, this entire method runs again.
return Scaffold(
appBar: AppBar(
title: const Text('Flutter Declarative UI'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
"You have pushed the button this many times:",
),
Text(
'$_counter', // The UI reads the value directly from our state.
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
// Tapping the button calls our function...
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
Let's walk through the flow:
The app starts. The
buildmethod runs,_counteris0, and the user sees "0" on the screen.The user taps the
FloatingActionButton.The
onPressedcallback fires, calling_incrementCounter().Inside this function,
_counterbecomes1, and thensetState()is called.setState()tells Flutter to schedule a new frame.Flutter re-runs the
buildmethod.This time, when the
Textwidget is built, it reads_counterand gets the new value,1.Flutter's efficient engine sees that the only thing that changed is this one
Textwidget, and it instantly updates the screen.
We never manually said "find the text label and set its text to 1." We just changed the state and described what the UI should look like with that new state. That's the declarative mindset.
The Three Trees: How Flutter is So Fast
The video mentions that Flutter uses three "trees" to manage your app. This is the secret to its performance.
- Widget Tree: This is your code—the blueprint you just wrote with all your
Text,Center, andColumnwidgets. It's lightweight and disposable. - Element Tree: This is the "brains" of the operation. The
Elementtree is a middle-man that holds the state of your widgets and a reference to theWidgetblueprint. This is what allows Flutter to know what changed and what to rebuild. This is what survivesbuildcalls, which is why your counter value isn't lost. - RenderObject Tree: This is the "muscle." These are the actual objects on screen that handle painting, layout ("how big is this box?"), and hit-testing (detecting taps).
When you call setState and the build method runs, Flutter creates a new Widget Tree. It then compares this new tree with the old one, figures out the minimal set of changes, updates the Element tree, and finally tells the RenderObject tree the exact tiny update to make to the screen.
The Flutter "Layer Cake"
Finally, the video shows a diagram of Flutter's architecture, which looks like a layer cake.
- Top: Framework (Dart): This is where you live. It's all the widgets (Material, Cupertino), animations, and gesture detectors. It's all written in Dart.
- Middle: Engine (C++): This is the high-performance core. Written in C++, it handles graphics (using the Skia or new Impeller engine), text layout, file I/O, and networking. It's the "engine" that does all the heavy lifting.
- Bottom: Embedder (Platform-Specific): This is the platform-specific "glue." It's a small piece of native code (for iOS, Android, Windows, etc.) that hosts the Flutter Engine, sets up a "canvas" for Flutter to paint on, and funnels native inputs (touch, keyboard) into the engine.
Conclusion
So, "How does Flutter work?"
It's a declarative framework where you describe your UI as a function of your state. You build this UI by composing lightweight widgets. When your state changes, you call setState(), and Flutter efficiently rebuilds your UI by comparing your new Widget Tree with its persistent Element Tree, and then tells the RenderObject Tree to paint the changes.
This is all powered by the Dart language, which runs in the high-performance C++ Engine that is "embedded" in your native platform app.
This architecture is what gives us a fast, beautiful, multiplatform experience with a revolutionary developer workflow.
In the next chapter, the series dives deeper into those three trees. Stay tuned!
0 Comments