DEV Community

Cover image for Simple Flutter Performance Tips to Build Faster Apps
anju sabharwal
anju sabharwal

Posted on

Simple Flutter Performance Tips to Build Faster Apps

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');
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

Avoid:

Widget _buildItem(String title) => Text(title);
Enter fullscreen mode Exit fullscreen mode

📜 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');
}
Enter fullscreen mode Exit fullscreen mode

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'),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

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());
}
Enter fullscreen mode Exit fullscreen mode

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());
}
Enter fullscreen mode Exit fullscreen mode

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]);
  },
);
Enter fullscreen mode Exit fullscreen mode

Avoid:

ListView(
  children: items.map((item) => ItemTile(item)).toList(),
);
Enter fullscreen mode Exit fullscreen mode

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)