Migration to Flutter Guide
Discover our battle-tested 21-step framework for a smooth and successful migration to Flutter!

Asynchronous programming in Flutter

What is asynchronous programming in Flutter?

Asynchronous programming in Flutter app development allows the app to perform time-consuming operations (network calls, disk access) without blocking the UI.

It does not mean running code on a separate thread by default.

Flutter is single-threaded, and this distinction is critical to understand.

Why does it matter in Flutter app development?

Flutter renders at 60–120 FPS.

If the main thread is blocked for more than a few milliseconds, users see:

  • frozen UI
  • dropped frames (jank)
  • unresponsive gestures

Correct async usage is essential for performance.

How does it work?

Flutter uses Dart's event loop with:

  • Future
  • async / await
  • Stream

When you use await, the function pauses only while waiting for I/O (like HTTP or database calls).

The UI thread stays free to render frames.

Future<User> loadUser() async {
  final data = await api.fetchUser();
  return User.fromJson(data);
}

The biggest myth: async ≠ background thread

This is the most common misunderstanding.

  • async / await does not create a new thread
  • CPU-heavy work still runs on the main UI thread

That's why this code can freeze the app, even with async:

await heavyJsonParsing(); // CPU work → UI jank

Rule of thumb

  • I/O work (network, database) → async / await
  • Heavy CPU work (parsing, loops, image processing) → Isolates (compute, Isolate.run)

Futures vs Streams: What's the difference?

Future:

  • One-time result
  • Example: HTTP request, file read

Stream:

  • Multiple values over time
  • Example: WebSockets, Firebase, sensors

Streams must be managed carefully, especially their lifecycle.

Best practices

  • Never run heavy CPU work on the UI thread.
  • Use compute or Isolate.run for expensive tasks.
  • Keep async logic out of widgets.
  • Always handle errors with try-catch.
  • Cancel streams when widgets are disposed.
  • Use FutureBuilder / StreamBuilder only for UI.

When not to use asynchronous programming?

Avoid async for:

  • simple calculations
  • synchronous UI logic
  • CPU-heavy work without isolates

Common mistakes to avoid

Using BuildContext after await

onPressed: () async {
  await login();
  Navigator.pop(context); // crash risk
}

If the widget was disposed while waiting, this will crash.

Fix

if (!context.mounted) return;

Always check after await before using context.

Forgetting to cancel Streams

If you call .listen() manually, you must cancel it in dispose():

subscription.cancel();

Otherwise, you create memory leaks.

Sequential vs parallel async work

This is slow:

final user = await getUser();
final posts = await getPosts();

This is faster:

final results = await Future.wait([
  getUser(),
  getPosts(),
]);

Independent tasks should run in parallel.

Learn more

Social Media Activity Feed with Stream

Implementing Social Media Activity Feed with Stream

In a recent project, we embarked on the exciting task of enhancing an existing app with dynamic social media elements. Choosing Stream as our ally, we navigated through challenges and came up with strategies that helped us overcome them, resulting in the successful delivery of a news feed feature. Read our complex article.

Flutter architecture by LeanCode

Feature-Based Flutter App Architecture - LeanCode

Read about the LeanCode approach to Flutter architecture. We highlight some of the design decisions that should be made when developing a feature in mobile apps. This includes our approach to dependency injection, state management, widget lifecycle, and data fetching.

12 Flutter & Dart Code Hacks
by LeanCode

12 Flutter & Dart Code Hacks & Best Practices – How to Write Better Code?

In this article, we’re sharing LeanCode’s 12 practical Flutter and Dart patterns that help you write less boilerplate, make your code cleaner, and catch mistakes earlier. Apply these patterns and you'll find yourself coding faster, communicating more clearly with your teammates, and spending less time debugging issues that the compiler could have caught.