Asynchronous programming in Flutter, futures, async, await

Build and publish a Flutter-based app for the App Store and Google Play — updated here for 2026 to reflect the current state of async Dart and the device_info_plus plugin.

As we dig deeper into sign-in flows, the app needs to query the device it's running on and surface relevant choices in the UI. That means working with asynchronous code — and in 2026, Dart's async story is more capable and better-tooled than ever.

TL:DR – This instalment focuses on asynchronous programming with futures, async, and await, then applies those concepts to retrieving device information inside a Flutter app.

Asynchronous programming: futures, async, await

If you're new to async Dart, the official codelab is still the best starting point: Asynchronous programming in Dart with futures, async, await. It covers everything you need before touching a plugin that returns a Future.

The core idea hasn't changed: Dart is single-threaded, so operations that take time — network calls, file reads, device queries — return a Future rather than blocking. The async and await keywords let you write that non-blocking code in a style that reads almost like synchronous code.

Future<void> printDeviceModel() async {
  // await suspends this function until the Future completes
  final info = await deviceInfo.deviceInfo;
  print(info.data['model']);
}

Without await, your code would try to use the result before Dart has had a chance to fetch it — a common source of null-related bugs in early Flutter projects.

2026 update: what's changed with async Dart

Dart 3.x brought a wave of productivity improvements that make async code safer and easier to audit. Two lints worth enabling in your analysis_options.yaml are now part of the recommended set:

  • discarded_futures — warns when a Future is called in a context where its result will be silently dropped, which is almost always a bug.
  • unawaited_futures — flags Future-returning calls that aren't awaited and haven't been explicitly wrapped in unawaited(), making intentional fire-and-forget patterns explicit rather than accidental.

Enabling both lints in a project is straightforward:

analyzer:
  enable-experiment:
    - macros
linter:
  rules:
    - discarded_futures
    - unawaited_futures

These additions mean the toolchain now catches entire categories of async mistakes at analysis time rather than at runtime — a meaningful improvement for any app that does real I/O, including device queries.

Supercharge your productivity with Dart 3.12 — new lints, improved analysis, and tighter integration with Flutter's tooling make async code easier to write correctly the first time.

device_info_plus

The actively maintained replacement for the original device info plugin is device_info_plus, part of the Plus Plugins family — a set of community-maintained packages that took over several of Flutter's original first-party plugins. It supports Android, iOS, macOS, Windows, Linux, and web from a single API, which matters as Flutter's multi-platform story has matured.

Add it to your pubspec.yaml:

dependencies:
  device_info_plus: ^10.0.0

Then run flutter pub get as usual.

Usage in 2026

The API is intentionally similar to the old package, so migration is straightforward. The key difference is that you call deviceInfo.deviceInfo to get a platform-agnostic BaseDeviceInfo, or you cast to the platform-specific subclass when you need detailed fields:

import 'package:device_info_plus/device_info_plus.dart';

final deviceInfo = DeviceInfoPlugin();

// Android
if (Platform.isAndroid) {
  final androidInfo = await deviceInfo.androidInfo;
  print('Running on ${androidInfo.model}'); // e.g. "Pixel 9 Pro"
}

// iOS
if (Platform.isIOS) {
  final iosInfo = await deviceInfo.iosInfo;
  print('Running on ${iosInfo.utsname.machine}'); // e.g. "iPhone17,2"
}

Notice the await on each call — this is exactly why understanding futures matters. Each platform query is asynchronous: Dart fires the request, suspends the surrounding function, and resumes once the device responds. Without async/await, you'd be working with unresolved Future objects rather than the actual data.

Why the original package was retired

The original device_info and device_info_null_safety packages are no longer published or maintained by the Flutter team. The Flutter ecosystem consolidated a number of these utility plugins under the Plus Plugins umbrella, which offers faster release cycles, broader platform coverage, and null-safety as a baseline. If you have an older project still referencing device_info, migrating to device_info_plus is a small change with a meaningful payoff in long-term support.

Putting it together

The pattern you'll use repeatedly in Flutter — not just for device info, but for authentication, network requests, local storage, and more — looks like this:

  • Mark the function async.
  • Use await before any call that returns a Future.
  • Handle errors with try/catch, just as you would with synchronous code.
  • Enable the discarded_futures and unawaited_futures lints so the analyser catches mistakes early.

Once async patterns feel natural, the rest of the sign-in flow — checking platform, querying capabilities, conditionally rendering UI — becomes much more manageable. That's exactly where we're headed in the next instalment.