Dart null / false / empty checking: How to write this shorter?

If StatementDartConditional

If Statement Problem Overview


This is my code for true on everything but empty string, null and false:

if (routeinfo["no_route"] == "" || routeinfo["no_route"] == null || routeinfo["no_route"] == false) {
    // do sth ...
}

This is my code for true on everything but empty string, null, false or zero:

if (routeinfo["no_route"] == "" || routeinfo["no_route"] == null || routeinfo["no_route"] == false || routeinfo["no_route"] == 0) {
    // do sth...
}

How can I write this shorter in Dart? Or is it not possible?

If Statement Solutions


Solution 1 - If Statement

If your requirement was simply empty or null (like mine when I saw this title in a search result), you can use Dart's safe navigation operator to make it a bit more terse:

if (routeinfo["no_route"]?.isEmpty ?? true) {
  // 
}

Where

  • isEmpty checks for an empty String, but if routeinfo is null you can't call isEmpty on null, so we check for null with
  • ?. safe navigation operator which will only call isEmpty when the object is not null and produce null otherwise. So we just need to check for null with
  • ?? null coalescing operator

If your map is a nullable type then you have to safely navigate that:

if (routeinfo?["no_route"]?.isEmpty ?? true) {
  //
}

Solution 2 - If Statement

You could do

if (["", null, false, 0].contains(routeinfo["no_route"])) {
  // do sth
}

Solution 3 - If Statement

Late 2020 Update

Summary
  • This answer holds true, except for isNull and isNotNull. They no longer provide type promotion when Null Safety is introduced in dart/flutter in the future.
  • Other helpers like isNullOrEmpty do not provide type promotion, as they are in a different (sub-)scope compared to callsite.
  • My personal opinion, is that you can drop isNull and isNotNull but keep other helpers as you shouldn't expect them to do type promotion for you.
Explanation
Demonstration

Here's a demonstration why encapsulation/helper-getter of isNull (== null) and isNotNull (!= null) is a very big problem:

// Promotion works
int definitelyInt(int? aNullableInt) {
  if (aNullableInt == null) { // Promote variable `aNullableInt` of Nullable type `int?` to Non-Nullable type `int`
    return 0;
  }
  return aNullableInt; // Can't be null! This variable is promoted to non-nullable type `int`
}

When "Null Safety" is shipped in the dart release you are using, the type promotion in above code works! HOWEVER:

// Promotion does NOT work!!!
int definitelyInt(int? aNullableInt) {
  if (aNullableInt.isNull) { // does NOT promote variable `aNullableInt` of Nullable type `int?`
    return 0;
  }
  return aNullableInt; // This variable is still of type `int?`!!!
}

The above doesn't work, because the null check == null and != null are encapsulated in a sub-scope (different stack frame) and not inferred to have this "promotion" effect within definitelyInt scope.



Early 2020 Update

Here's a modified version using getters instead of instance/class methods and covering whitespaces, isNull, and isNotNull.

Second Update

It also accounts for empty lists and maps now!

Third Update

Added private getters for safety and modularity/maintainability.

Fourth Update

Updated the answer to fix a particular problem with Map, it's not correctly being handled when empty! Because Map is not an Iterable. Solved this issue by introducing _isMapObjectEmpty U

Solution

extension TextUtilsStringExtension on String {
  /// Returns true if string is:
  /// - null
  /// - empty
  /// - whitespace string.
  ///
  /// Characters considered "whitespace" are listed [here](https://stackoverflow.com/a/59826129/10830091).
  bool get isNullEmptyOrWhitespace =>
      this == null || this.isEmpty || this.trim().isEmpty;
}

/// - [isNullOrEmpty], [isNullEmptyOrFalse], [isNullEmptyZeroOrFalse] are from [this StackOverflow answer](https://stackoverflow.com/a/59826129/10830091)
extension GeneralUtilsObjectExtension on Object {
  /// Returns true if object is:
  /// - null `Object`
  bool get isNull => this == null;

  /// Returns true if object is NOT:
  /// - null `Object`
  bool get isNotNull => this != null;

  /// Returns true if object is:
  /// - null `Object`
  /// - empty `String`s
  /// - empty `Iterable` (list, set, ...)
  /// - empty `Map`
  bool get isNullOrEmpty =>
      isNull ||
      _isStringObjectEmpty ||
      _isIterableObjectEmpty ||
      _isMapObjectEmpty;

  /// Returns true if object is:
  /// - null `Object`
  /// - empty `String`
  /// - empty `Iterable` (list, map, set, ...)
  /// - false `bool`
  bool get isNullEmptyOrFalse =>
      isNull ||
      _isStringObjectEmpty ||
      _isIterableObjectEmpty ||
      _isMapObjectEmpty ||
      _isBoolObjectFalse;

  /// Returns true if object is:
  /// - null `Object`
  /// - empty `String`
  /// - empty `Iterable` (list, map, set, ...)
  /// - false `bool`
  /// - zero `num`
  bool get isNullEmptyFalseOrZero =>
      isNull ||
      _isStringObjectEmpty ||
      _isIterableObjectEmpty ||
      _isMapObjectEmpty ||
      _isBoolObjectFalse ||
      _isNumObjectZero;

  // ------- PRIVATE EXTENSION HELPERS -------
  /// **Private helper**
  ///
  /// If `String` object, return String's method `isEmpty`
  ///
  /// Otherwise return `false` to not affect logical-OR expression. As `false` denotes undefined or N/A since object is not `String`
  bool get _isStringObjectEmpty =>
      (this is String) ? (this as String).isEmpty : false;

  /// **Private helper**
  ///
  /// If `Iterable` object, return Iterable's method `isEmpty`
  ///
  /// Otherwise return `false` to not affect logical-OR expression. As `false` denotes undefined or N/A since object is not `Iterable`
  bool get _isIterableObjectEmpty =>
      (this is Iterable) ? (this as Iterable).isEmpty : false;

  /// **Private helper**
  ///
  /// If `Map` object, return Map's method `isEmpty`
  ///
  /// Otherwise return `false` to not affect logical-OR expression. As `false` denotes undefined or N/A since object is not `Map`
  bool get _isMapObjectEmpty => (this is Map) ? (this as Map).isEmpty : false;

  /// **Private helper**
  ///
  /// If `bool` object, return `isFalse` expression
  ///
  /// Otherwise return `false` to not affect logical-OR expression. As `false` denotes undefined or N/A since object is not `bool`
  bool get _isBoolObjectFalse =>
      (this is bool) ? (this as bool) == false : false;

  /// **Private helper**
  ///
  /// If `num` object, return `isZero` expression
  ///
  /// Otherwise return `false` to not affect logical-OR expression. As `false` denotes undefined or N/A since object is not `num`
  bool get _isNumObjectZero => (this is num) ? (this as num) == 0 : false;
}

Assumptions

This presumes Dart 2.7 or above to support Extension Methods.

Usage

The above are two Extension classes. One for Object and one for String. Put them in a file separately/together and import the files/file at callsite file.

Description

Coming from Swift, I tend to use extensions like user @Benjamin Menrad's answer but with getter instead of method/function when applicable -- mirroring Dart's computed properties like String.isEmpty.

Coming from C#, I like their String's helper method IsNullOrWhiteSpace.

My version merges the above two concepts.

Naming

Rename the Extension classes whatever you like. I tend to name them like this:

> XYExtension

Where:

  • X is File/Author/App/Unique name.
  • Y is name of Type being extended.

Name examples:

  • MyAppIntExtension
  • DartDoubleExtension
  • TextUtilsStringExtension
  • OHProviderExtension

Criticism

> Why private getters instead of simple > this == null || this == '' || this == [] || this == 0 || !this

  • I think this is safer as it first casts the object to the right type we want to compare its value to.
  • More modular as changes are central within the private getters and any modification is reflected everywhere.
  • If type check fails within the private getter, we return false which doesn't affect the outcome of the root logical-OR expression.

Solution 4 - If Statement

I would write a helper function instead of doing everything inline.

bool isNullEmptyOrFalse(Object o) =>
  o == null || false == o || "" == o;

bool isNullEmptyFalseOrZero(Object o) =>
  o == null || false == o || 0 == o || "" == o;

That avoids the repeated lookup (like the contains operation), but it is much more readable. It also doesn't create a new List literal for each check (making the list const could fix that).

if (isNullEmptyOrFalse(routeinfo["no_route"])) { ... }

When struggling with making something short and readable, making a well-named helper function is usually the best solution.

(Addition: Now that Dart has extension methods, it's possible to add the functionality as methods or getters that are seemingly on the object, so you can write value.isNullOrEmpty directly).

Solution 5 - If Statement

As coming from Android and Kotlin, I prefer extension methods over a static helper method. And with Dart 2.7 you can now use that as well:

extension Extension on Object {
  bool isNullOrEmpty() => this == null || this == '';

  bool isNullEmptyOrFalse() => this == null || this == '' || !this;

  bool isNullEmptyZeroOrFalse() =>
      this == null || this == '' || !this || this == 0;
}

Now you can just call these methods from anywhere in your code:

if (anyVariable.isNullOrEmpty()) {
  // do something here
}

You might need to manually import the dart class, where you put your extension methods, for example:

import 'package:sampleproject/utils/extensions.dart';

Solution 6 - If Statement

With Null safety:

Say, you have a nullable Map and a List which have nullable values in it.

Map<String, List?>? map;
List<String?>? list;

To check for null, empty and false, you could do:

if (map?['key']?.isEmpty ?? false) {...}

if (list?[0]?.isEmpty ?? false) {...}

Solution 7 - If Statement

package:quiver has an isEmpty function that returns true if the argument is null or the empty string.

It's also trivial to implement such a function yourself.

Solution 8 - If Statement

bool isNullString(String? value) {
      if (value == null || value.isEmpty || value == false) {
        return true;
      } else {
        return false;
      }
    }

and use this method like

isNullString(yourValue)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBlackbamView Question on Stackoverflow
Solution 1 - If StatementJannie TheunissenView Answer on Stackoverflow
Solution 2 - If StatementHarry TerkelsenView Answer on Stackoverflow
Solution 3 - If Statementom-haView Answer on Stackoverflow
Solution 4 - If StatementlrnView Answer on Stackoverflow
Solution 5 - If StatementBenjamin MenradView Answer on Stackoverflow
Solution 6 - If StatementCopsOnRoadView Answer on Stackoverflow
Solution 7 - If StatementjamesdlinView Answer on Stackoverflow
Solution 8 - If StatementadarshView Answer on Stackoverflow