This episode dissects the Flutter architecture: how Widgets, the rendering engine, and the Dart runtime work together, the reactive UI model and widget lifecycle, the relationship between the widget tree, element tree, and render tree, and the JIT and AOT compile model with hot reload.

In episode 1 you learned why Flutter draws its own UI. Now we move to the next layer: how Flutter works under the hood. Understanding the architecture is what separates a developer who merely stacks widgets from one who can debug complex problems.
Episode 2 dissects four pillars of the Flutter architecture: the roles of widgets, the rendering engine, and the Dart runtime; the reactive UI model and widget lifecycle; the relationship between three trees that work together — the widget tree, element tree, and render tree; and the JIT and AOT compile model that powers hot reload.
Flutter is composed of three layers that work side by side:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: Scaffold(
body: Center(
child: Text('Halo Flutter'),
),
),
);
}
}The code above is a complete Flutter application. runApp(MyApp()) attaches the widget to the screen, then the framework builds the widget tree downward. Notice that MaterialApp, Scaffold, Center, and Text are all widgets.
Flutter uses a reactive model: a widget is a declaration of the UI for a given state. When state changes, build is called again and the framework updates the screen efficiently — you don't write imperative commands to mutate the UI.
Stateful widgets have a well-defined lifecycle. The methods you'll use most often:
initState — called once when the widget is inserted into the tree.didUpdateWidget — called when the parent replaces the widget's configuration.dispose — called when the widget is removed; the place to clean up resources.import 'package:flutter/material.dart';
class CounterPage extends StatefulWidget {
const CounterPage({super.key});
@override
State<CounterPage> createState() => _CounterPageState();
}
class _CounterPageState extends State<CounterPage> {
int _count = 0;
@override
void initState() {
super.initState();
print('Widget dipasang');
}
@override
void dispose() {
print('Widget dibuang');
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: Center(child: Text('$_count')),
floatingActionButton: FloatingActionButton(
onPressed: () => setState(() => _count++),
child: const Icon(Icons.add),
),
);
}
}setState in the example above tells the framework that state has changed, so build runs again. This is the cycle you'll master in episode 6 when we discuss state management.
Flutter works with three structures at once:
BuildContext.When your build code runs, Flutter hydrates the widget tree into the element tree, then the render tree computes sizes and paints. Flutter's core optimization: when the tree is rebuilt, only the changed parts are marked as dirty and re-rendered — the rest is skipped.
Every widget has a BuildContext that comes from its element. The context is used to look up ancestor widgets, navigate, read the theme, and much more. Understanding that context is an element, not a widget, explains many strange errors early in your Flutter learning journey.
Flutter uses two Dart compile modes:
flutter run): enables hot reload and hot restart.flutter build): code is compiled to machine code, resulting in fast startup and runtime.With JIT, you change code and press r in the terminal to immediately see the result on the emulator without a full restart:
flutter run --releaseflutter run --release activates AOT mode: the app is built with full optimization, suitable for measuring real performance. Don't use this mode during development, because you lose hot reload.
Verifying code quality before committing also uses the Flutter toolchain:
dart analyzedart analyze detects potential problems in your code without running it — a habit that will save you a lot of debugging time.
Key takeaways:
build is called again whenever state changes.initState, didUpdateWidget, and dispose.dart analyze regularly before committing.In the next episode 3 we get hands-on: installation and project setup — installing the Flutter SDK with environment verification, creating a new project with flutter create, exploring the generated project structure (lib, pubspec.yaml, android, ios, web), and running your first app on an emulator or device. You start actually touching code.