Widget Tree in Flutter :
In Flutter, the widget tree represents the hierarchical structure of UI elements in your app.
Every screen in Flutter is composed of nested widgets forming a tree-like structure.
Understanding the Widget Tree
Parent-Child Relationship: Every widget has a parent, and it can have multiple
children.
Types of Widgets:
o Parent Widget: Contains child widgets.
o Child Widgets: Nested inside a parent widget.
Composition: Flutter’s UI is built by composing multiple widgets.
Example: Widget Tree Structure
Let's consider this UI:
A Scaffold (Root widget)
o An AppBar (Title bar)
o A Column (Vertically aligned widgets)
A Text widget
A Row (Horizontally aligned widgets)
Two ElevatedButton widgets
Code Example: Simple Widget Tree
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: Scaffold(
appBar: AppBar(title: const Text("Flutter Widget Tree")),
body: const MyWidgetTree(),
),
);
}
}
class MyWidgetTree extends StatelessWidget {
const MyWidgetTree({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const Text(
"Welcome to Flutter!",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 20), // Adds spacing
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
ElevatedButton(
onPressed: () {
print("Button 1 Clicked");
},
child: const Text("Button 1"),
),
const SizedBox(width: 20), // Space between buttons
ElevatedButton(
onPressed: () {
print("Button 2 Clicked");
},
child: const Text("Button 2"),
),
],
),
],
),
);
}
}
Widget Tree Breakdown
MyApp
└── MaterialApp
└── Scaffold
├── AppBar
│ └── Text("Flutter Widget Tree")
└── MyWidgetTree (Custom Widget)
└── Center
└── Column
├── Text("Welcome to Flutter!")
├── SizedBox (Spacing)
└── Row
├── ElevatedButton ("Button 1")
├── SizedBox (Spacing)
└── ElevatedButton ("Button 2")
Widget Build Visualization in Flutter 🎨
Flutter's widget build process follows a tree structure, where each widget rebuilds when needed,
based on state changes or UI updates. Understanding how the build() method works helps
optimize UI performance.
How Flutter Builds Widgets
1. Build Phase:
o Flutter calls the build() method of the widget.
o It constructs the widget tree from top to bottom.
2. Rendering Phase:
o Flutter determines changes in the UI and repaints only affected parts.
3. Rebuild Triggers:
o State changes (setState())
o Parent widget updates
o Screen resizing (hot reload, device rotation)
Example: Visualizing Widget Build
Below is an example showing how Flutter builds and rebuilds widgets using a stateful widget.
Code Example: Counter App with Build Logs
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
print("MyApp Widget Built");
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
int counter = 0;
@override
Widget build(BuildContext context) {
print("HomeScreen Widget Built");
return Scaffold(
appBar: AppBar(title: const Text("Build Visualization Example")),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"Counter: $counter",
style: const TextStyle(fontSize: 24, fontWeight:
FontWeight.bold),
),
const SizedBox(height: 20),
ElevatedButton(
onPressed: () {
setState(() {
counter++;
});
},
child: const Text("Increment Counter"),
),
],
),
),
);
}
}
Build Process Log
When you run the app, the following logs will be printed in the console:
First Run (App Start)
MyApp Widget Built
HomeScreen Widget Built
After Clicking the Button (Rebuild)
HomeScreen Widget Built
👉 Notice that MyApp does NOT rebuild, only HomeScreen does.
Widget Build Visualization
The widget tree structure looks like this:
MyApp
└── MaterialApp
└── HomeScreen (Stateful)
└── Scaffold
├── AppBar
└── Center
└── Column
├── Text ("Counter: X")
├── SizedBox (Spacing)
└── ElevatedButton ("Increment Counter")
Key Takeaways
✅ The build() method runs every time setState() is called.
✅ Only affected widgets are rebuilt, not the entire app.
✅ Logging print() statements inside build() helps debug UI rendering.
Advanced Widget Build Visualization with ListView.builder 🚀
In large Flutter apps, efficient widget rebuilding is crucial. Instead of building a large number
of widgets manually, we use ListView.builder() to dynamically generate widgets only
when needed, improving performance.
Example: Dynamic List with Build Logging
This example displays a scrollable list of numbers, and logs when each list item is built.
Code Example
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
print("MyApp Widget Built");
return MaterialApp(
debugShowCheckedModeBanner: false,
home: const HomeScreen(),
);
}
}
class HomeScreen extends StatefulWidget {
const HomeScreen({Key? key}) : super(key: key);
@override
State<HomeScreen> createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
final List<int> numbers = List.generate(100, (index) => index + 1);
@override
Widget build(BuildContext context) {
print("HomeScreen Widget Built");
return Scaffold(
appBar: AppBar(title: const Text("ListView.builder Example")),
body: ListView.builder(
itemCount: numbers.length,
itemBuilder: (context, index) {
print("Building List Item: ${numbers[index]}"); // Logging each item
build
return ListTile(
leading: const Icon(Icons.numbers),
title: Text("Item ${numbers[index]}"),
);
},
),
);
}
}
Widget Build Process
First Run (App Start)
MyApp Widget Built
HomeScreen Widget Built
Building List Item: 1
Building List Item: 2
Building List Item: 3
...
👉 Only the visible list items are built, not all 100 at once.
When Scrolling Down
Building List Item: 11
Building List Item: 12
Building List Item: 13
...
👉 New items build only when they appear on the screen, improving performance.
Widget Tree Visualization
MyApp
└── MaterialApp
└── HomeScreen (Stateful)
└── Scaffold
├── AppBar
└── ListView.builder
├── ListTile (Item 1)
├── ListTile (Item 2)
├── ListTile (Item 3)
├── ...
Key Takeaways
✅ ListView.builder() builds items on demand, reducing memory usage.
✅ Only visible items are built, improving scrolling performance.
✅ Efficient for large lists, unlike ListView(children: [...]), which builds everything at
once.