Flutter makes UI building fast, but small mistakes — like unnecessary rebuilds or heavy logic in build() — can slow your app down.
Here are some practical, beginner-friendly Flutter performance tips every developer should know 👇
🧱 1. Favor Stateless Widgets Whenever Possible
Stateless widgets rebuild less often because they don’t hold changeable data.
Whenever your UI doesn’t depend on variables or inputs that change, use a StatelessWidget.
✅ This keeps your rebuilds predictable and your app smoother.
⚙️ 2. Use const Widgets
If a widget never changes, mark it as const.
This tells Flutter it can reuse that widget instead of recreating it every frame.
const Text('Hello Flutter');
Even better — enable flutter_lints in your project to automatically remind you where const can be applied.
It’s a small habit that makes a noticeable performance difference.
🧩 3. Extract Widgets Instead of Returning Them From Methods
Avoid creating widgets directly inside helper methods.
Instead, define them as separate widget classes — even private ones within the same file.
✅ Good:
class ItemTile extends StatelessWidget {
final String title;
const ItemTile(this.title);
@override
Widget build(BuildContext context) => Text(title);
}
❌ Avoid:
Widget _buildItem(String title) => Text(title);
📜 4. Break Down Large build() Functions
If your screen layout feels long or complex, split it into smaller widgets.
This makes your app easier to read and faster to rebuild — since Flutter can update only the parts that change instead of the entire screen.
✅ Good Practice:
class ProfileScreen extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: const [
ProfileHeader(),
ProfileDetails(),
ProfileActions(),
],
);
}
}
class ProfileHeader extends StatelessWidget {
const ProfileHeader({super.key});
@override
Widget build(BuildContext context) => Text('Header');
}
Breaking your screen into smaller widgets helps Flutter reuse parts of the widget tree efficiently and improves rebuild performance.
🔁 5. Keep setState() Localized
Use setState() only where necessary.
If only a small part of your UI updates, put it inside its own StatefulWidget so that you don’t rebuild the entire page unnecessarily.
✅ Good Practice:
class CounterButton extends StatefulWidget {
const CounterButton({super.key});
@override
State<CounterButton> createState() => _CounterButtonState();
}
class _CounterButtonState extends State<CounterButton> {
int count = 0;
@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: () => setState(() => count++),
child: Text('Count: $count'),
);
}
}
This ensures that only the button rebuilds when tapped — not the entire screen.
For medium or large apps, consider using state management tools like Provider, Riverpod, or Bloc for cleaner and more efficient control over UI updates.
🧮 6. Avoid Heavy Work Inside build()
The build() method should only describe the UI — not perform calculations or heavy logic.
Running expensive work inside build() makes your app slower because it can be called frequently during UI updates.
❌ Avoid:
@override
Widget build(BuildContext context) {
final result = heavyCalculation(); // Runs every rebuild 😬
return Text(result.toString());
}
✅ Do This Instead:
late final int result;
@override
void initState() {
super.initState();
result = heavyCalculation(); // Runs once
}
@override
Widget build(BuildContext context) {
return Text(result.toString());
}
By computing values once — for example, in initState() — your app avoids unnecessary work during rebuilds, keeping the UI smooth and responsive.
📋 7. Use ListView.builder() for Large or Dynamic Lists
When displaying a long or dynamic list of items, always use ListView.builder().
It builds only the visible items on screen, saving memory and improving scroll performance.
✅ Good Practice:
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return ItemTile(items[index]);
},
);
❌ Avoid:
ListView(
children: items.map((item) => ItemTile(item)).toList(),
);
ListView.builder() ensures that only what’s visible is rendered, keeping your app smooth and efficient — especially for large datasets.
🎨 8. Handle Images Wisely
Images are one of the most common reasons for lag or jank in Flutter apps.
Optimizing them properly can make a big difference in performance.
✅ Tips for Optimization:
- Use appropriate image formats for your use case
- Provide multiple resolutions (
1x,2x,3x) — Flutter automatically picks the right one - Compress large images before adding them to your project
Keeping your image assets lightweight and resolution-aware ensures faster loading, smoother scrolling, and better memory efficiency across devices.
💡 Final Tip
Performance optimization isn’t about doing everything at once — it’s about writing predictable, efficient widget trees.
Start with small steps like using const, isolating state, and avoiding rebuild-heavy patterns — and your app will already feel smoother and more responsive.
Top comments (0)