Skip to content

Flutter and Dart errorFlutter and Dart: “Bad state: No element”

The app throws Bad state: No element (a StateError), or the screen goes red right after a list loads. Code called .first, .last, .single or firstWhere on a list that was empty, or that had no matching item. Dart doesn’t return null for these calls; it throws.

Short answer

Find the .first, .last, .single, reduce or firstWhere call on the first stack trace line in your own code. Check isEmpty before it, use firstOrNull (built into Dart 3) or firstWhereOrNull (import package:collection/collection.dart), or give firstWhere an orElse. Then decide what the screen shows when there is no item. The list is usually empty because data has not loaded yet, a filter matched nothing, or an id from a route or a link points to an item that was deleted.

Why the list is empty, and the fix

  1. 01

    .first on a list that has not loaded

    “The first element. Throws a StateError if this is empty.” Dart, Iterable.first

    build reads items.first while items is still the empty list it started as. Check isEmpty, and show a loader or an empty state.

    if (items.isEmpty) return const Text('No items yet');
    final latest = items.first;
  2. 02

    firstWhere that matches nothing

    “If no element satisfies test, the result of invoking the orElse function is returned. If orElse is omitted, it defaults to throwing a StateError.” Dart, Iterable.firstWhere

    firstWhere((p) => p.id == id) throws when no item has that id: a deleted record, a stale id from a deep link or notification, or an int id compared with a String. Use firstWhereOrNull and handle null, or pass orElse.

    import 'package:collection/collection.dart';
    
    final product = products.firstWhereOrNull((p) => p.id == id);
    if (product == null) return const NotFoundScreen();
  3. 03

    .single, .last or reduce on the wrong count

    “Throws a StateError if this is empty or has more than one element.” Dart, Iterable.single

    .single throws on an empty list and on a list with two or more items (that one says Bad state: Too many elements). .last and reduce throw on an empty list too. Use them only where the count is guaranteed, such as right after a check, or use singleOrNull and lastOrNull.

  4. 04

    await stream.first on a stream that closes empty

    “If this stream is empty (a done event occurs before the first data event), the returned future completes with an error.” Dart, Stream.first

    await stream.first fails with the same message when the stream closes before sending a value, for example a stream built from an empty list or a finished download. Use firstWhere with orElse on the stream, or listen and handle the done event.

Checked against the vendor’s docs on : Dart API: Iterable.first; Dart API: Iterable.firstWhere; Dart API: Iterable.single; Dart API: Stream.first; Dart API: StateError class.

Rather hand it over?

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

Related: Flutter “type 'Null' is not a subtype of type 'String'” · Flutter “Null check operator used on a null value” · Flutter LateInitializationError · Flutter “Looking up a deactivated widget’s ancestor is unsafe”

Questions

More on this error

Why does it happen only on a fresh install?

A fresh install has no cached data, so lists start empty and code that assumed at least one item fails. Test with a new account and with the network off.

Why doesn’t orElse: () => null compile?

With null safety, orElse must return the list’s own type, and null is not a Product. Use firstWhereOrNull instead, which returns Product? and lets you check for null.

Is “Bad state: Stream has already been listened to” the same error?

No. It is a different StateError, from listening twice to a stream that allows one listener. Listen once, or use a broadcast stream.

Can you fix it for me?

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.

A different error?

Describe it and get a written quote

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; Simon replies by email, usually within one business day.