Flutter is fun, but it can be confusing, especially when talking about widget rebuilds. Every widget in Flutter can be rebuilt through the build()
method, but does every widget always rebuild together? The answer is: not always. Understanding this allows you to create apps that are more efficient, faster, and cleaner.
This article will explore an advanced Flutter topic: interactive visual examples where some widgets rebuild differently when state changes. We’ll use real examples and a casual explanation so the concept is easy to understand, even for beginners who have played with build()
before.
1. Why Widgets Can Rebuild Differently
In Flutter, widgets are immutable. This means once a widget is created, its content cannot be changed. If we want to update the UI, Flutter rebuilds the widget via build()
.
However, only widgets affected by the state change will rebuild. This is important because:
-
Improves performance → not all widgets need to be rendered again every time state changes.
-
Easier modularization → you can split UI into smaller, independent widgets.
-
Better control over UI → you know exactly which part will update during interactions.
2. Creating a Simple Interactive Example
Let’s create an example with 3 different widgets:
-
Counter A → only rebuilds when button A is pressed.
-
Counter B → only rebuilds when button B is pressed.
-
Static Text → never rebuilds.
Here’s the code:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Interactive Rebuild Demo',
home: Scaffold(
appBar: AppBar(title: const Text('Widget Rebuild Demo')),
body: const RebuildDemo(),
),
);
}
}
class RebuildDemo extends StatefulWidget {
const RebuildDemo({super.key});
@override
State<RebuildDemo> createState() => _RebuildDemoState();
}
class _RebuildDemoState extends State<RebuildDemo> {
int counterA = 0;
int counterB = 0;
@override
Widget build(BuildContext context) {
print('RebuildDemo build called'); // See when rebuild occurs
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
CounterWidget(
label: 'Counter A',
count: counterA,
onPressed: () {
setState(() {
counterA++;
});
},
),
CounterWidget(
label: 'Counter B',
count: counterB,
onPressed: () {
setState(() {
counterB++;
});
},
),
const StaticText(),
],
);
}
}
class CounterWidget extends StatelessWidget {
final String label;
final int count;
final VoidCallback onPressed;
const CounterWidget({
required this.label,
required this.count,
required this.onPressed,
super.key,
});
@override
Widget build(BuildContext context) {
print('$label build called'); // See when this widget rebuilds
return Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('$label: $count'),
const SizedBox(width: 20),
ElevatedButton(
onPressed: onPressed,
child: const Text('Increment'),
),
],
),
);
}
}
class StaticText extends StatelessWidget {
const StaticText({super.key});
@override
Widget build(BuildContext context) {
print('StaticText build called'); // Does not change
return const Padding(
padding: EdgeInsets.all(8.0),
child: Text(
'This text never rebuilds!',
style: TextStyle(fontSize: 18, color: Colors.blue),
),
);
}
}
3. Example Analysis
-
RebuildDemo
This is the mainStatefulWidget
. CallingsetState()
triggers rebuild of the entire_RebuildDemoState
. -
CounterWidget
This is a StatelessWidget, but it receivescount
andonPressed
from the parent.-
When counterA changes → Counter A rebuilds, Counter B also rebuilds because the parent calls
setState()
. -
To prevent Counter B from rebuilding, we can separate it into an independent stateful widget.
-
-
StaticText
Because it does not depend on state, its content remains the same. Usingconst
ensures Flutter won’t rebuild it.
4. Optimization: Avoid Unnecessary Rebuilds
If we want only Counter A to rebuild without affecting Counter B, we can move the state inside each widget:
class CounterWidgetStateful extends StatefulWidget {
final String label;
const CounterWidgetStateful({required this.label, super.key});
@override
State<CounterWidgetStateful> createState() => _CounterWidgetStatefulState();
}
class _CounterWidgetStatefulState extends State<CounterWidgetStateful> {
int count = 0;
@override
Widget build(BuildContext context) {
print('${widget.label} build called');
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('${widget.label}: $count'),
const SizedBox(width: 20),
ElevatedButton(
onPressed: () {
setState(() {
count++;
});
},
child: const Text('Increment'),
),
],
);
}
}
With this approach:
-
Counter A and B are independent → incrementing one does not affect the other.
-
The UI is more efficient and performs better for large applications.
5. Interactive Visualization
Visualization tips to understand rebuilds:
-
Add
print()
in everybuild()
→ check the console to see when rebuild occurs. -
Use different colors in each widget → easier to see which part changes.
Container(
color: count % 2 == 0 ? Colors.green : Colors.red,
child: Text('$label: $count'),
)
Color changes each time setState()
is called, so you can visually see rebuilds.
6. When to Use This Strategy?
-
Apps with many widgets → limiting rebuilds saves memory and CPU.
-
Widgets with deep trees → such as dashboards with charts, long lists, or animations.
-
Frequent interactions → like increment buttons, switches, sliders, so UI remains smooth.
With this interactive example, we learned important concepts:
-
Not all widgets need to rebuild whenever
setState()
is called. -
Moving state to small widgets makes the app more efficient.
-
const
and Stateless widgets that don’t depend on state will not rebuild, so use them for static elements. -
Debug with
print()
or color changes helps understand when rebuilds occur.
Flutter is powerful, but mastering efficient widget rebuilds is key to creating apps that are responsive, fast, and professional.
Tips:
-
Use
const
as much as possible for static widgets. -
Consider Provider, Riverpod, or Bloc for complex state management.
-
Always split widgets into smaller parts for modularity and maintainability.
With this example, you now have the foundation to create advanced Flutter apps with interactive visuals and controlled widget rebuilds. You can explore further with long lists, animated charts, or interactive UI using these principles.
0 Comments:
Post a Comment