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

11

111

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?

Almund answered 24/2, 2017 at 19:14 Comment(3)
Is there a typo in your question? By routeinfo["no_route"] != false you mean routeinfo["no_route"] == false, right? – Botulinus
Yeah right edited. Bit tired its late ;-) – Almund
You could design your application in a way that a key will only be there if there is a truthy value to it, so you can just check for the presence of the key and trust it. Maybe it's impossible design it that way (depending on the context), but it's worth trying. – Skilling
G
224

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) {
  //
}
Germaun answered 23/10, 2018 at 12:18 Comment(9)
Interesting syntax 😁 Can you explain it a bit more in detail what role of the questionmarks is? The classical elvis is ?: right? – Almund
@Almund - updated the answer with some explanations – Germaun
Another nice way to write this: if ((value ?? '') == '') { ... } – Exception
best answer. $0.02 - this is the way it should be done unless there's a specific reason not to – Garate
1.) The expression myString?.isEmpty ?? true is a "nullable expression", and if your if statement has more than one condition (like the OP's example), each nullable expression must be wrapped in parenthesis, which is annoying and decreases readability. 2.) Syntactic sugar is great, but most of us write code that others also have to read, use, maintain, etc. Unless some drastic performance increase would be recognized with a more complex solution, it is usually best to go with the simple solution that everyone will understand. In this case that is myString != null && myString.isNotEmpty. – Sandell
@Sandell agreed. As I said, if your requirement is simply empty or null then this answer is good. If, like the OP your requirement is more complex, there are better answers here. Including your suggestion. – Germaun
Interesting answer. But as much as it looks short, it adds extra cognitive complexity and is more difficult to read than a simple null check followed by an empty check. In the end, readability should be prioritized over small optimization(often unnecessary) – Encroachment
@SaurabhKumar I'm glad you find the answer interesting, but that was not my intent. I was aiming to give a shorter expression as the OP asked. Good point about readability, though. – Germaun
For anyone wondering, the opposite condition would be written if (routeinfo["no_route"]?.isNotEmpty ?? false) – Trisa
B
74

You could do

if (["", null, false, 0].contains(routeinfo["no_route"])) {
  // do sth
}
Botulinus answered 24/2, 2017 at 19:28 Comment(4)
Getting error: type 'Null' is not a subtype of type 'String' – Adalbertoadalheid
@Adalbertoadalheid - lowercase null? not in quotes? (i.e. null, not Null or 'null') – Decay
Kindly suggest me what is difference null, Null and NULL? Thanks – Adalbertoadalheid
Creating a mutable data structure for an empty string check?! In general, this is very bad practice, but is especially bad practice when you consider that Dart's primary function in this world is as a language for mobile devices (i.e. embedded systems) with limited memory and processing power. Please don't do this! – Sandell
T
45

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://mcmap.net/q/194261/-dart-null-false-empty-checking-how-to-write-this-shorter).
  bool get isNullEmptyOrWhitespace =>
      this == null || this.isEmpty || this.trim().isEmpty;
}

/// - [isNullOrEmpty], [isNullEmptyOrFalse], [isNullEmptyZeroOrFalse] are from [this StackOverflow answer](https://mcmap.net/q/194261/-dart-null-false-empty-checking-how-to-write-this-shorter)
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.
Toxicogenic answered 20/1, 2020 at 15:13 Comment(6)
Fixed checking collections if they were empty or not with private getter _isObjectIterableEmpty – Toxicogenic
Fixed another issue where map is not handled. I presumed it was a Literal, apparently it's not! Fixed is introduced with _isMapObjectEmpty private getter. – Toxicogenic
You will not want to use .isNull or isNotNull in the future. With null safety, you can promote a nullable variable no non-nullable with a == null check, but not with .isNull. That makes isNull and isNotNull a dangerous habit to get into. – Captivity
This is bad. I updated my answer accordingly. Your comment was confusing back in June, when I revisited today I got what you mean and explained it for others.. – Toxicogenic
With null-safety, your extension should be on String? instead of String – Frolick
Thanks @Arash, I will update my answer for the newest flutter and dart versions and inform you here in comments when I'm done. – Toxicogenic
C
30

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).

Captivity answered 25/2, 2017 at 14:2 Comment(2)
Modified the answer to add support for whitespace. Also check my full modified answer below. – Toxicogenic
the lambda function worked great. bool isNullEmptyOrZero(Object o) => o == null || false == o || "" == o || 0==o; I added a check for 0 to exclude – Punctate
W
16

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';
Woodland answered 2/1, 2020 at 11:17 Comment(3)
the import get removed by android studio, probably it cannot detect the usage of it. any idea to work around it? – Handicraftsman
With "null safety" enabled (in Dart 2.12 beta), using one of the extension method proposed (e.g. isNullOrEmpty), on a nullable object leads to the error: An expression whose value can be 'null' must be null-checked before it can be dereferenced.. – Wescott
if(testvariable == null){} condition does not work. Getting VSCode suggestion - The operand can't be null, so the condition is always false. Try removing the condition, an enclosing condition, or the whole conditional statement – Adalbertoadalheid
R
5

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 if the collection is neither null nor empty, you can do:

if (map?.containsKey('foo') ?? false) {
  print('map is not-null, and key "foo" is present.');
}

if (list?.isNotEmpty ?? false) {
  print('list is not-null and not empty');
} 
Rhapsodist answered 6/6, 2021 at 11:12 Comment(1)
It looks terrible. Is it possible to write some extension func that works well with Null safety? – Formation
A
5

For strings i like this approach:

extension NullableStringExtensions<E> on String? {
  /// Returns `true` if this string is `null` or empty.
  bool get isNullOrEmpty {
    return this?.isEmpty ?? true;
  }

  /// Returns `true` if this string is not `null` and not empty.
  bool get isNotNullNorEmpty {
    return this?.isNotEmpty ?? false;
  }
}

Credits go to the author of this package: https://pub.dev/packages/string_ext

Abiosis answered 9/6, 2022 at 15:3 Comment(0)
C
2
bool isNullString(String? value) {
      if (value == null || value.isEmpty ) {
        return true;
      } else {
        return false;
      }
    }

and use this method like

isNullString(yourValue)
Conductive answered 23/5, 2022 at 7:26 Comment(1)
OK other than the value == false check; a condition which is not possible since value was declared as String?. Additionally, you can shorten this by getting rid of the if/else and simply returning return value == null || value.isEmpty – Sandell
E
2

You can do an extension like this so you can use it everywhere:

extension StringExtensions on String? {
  bool isNullOrEmpty() {
    return this == null || this == "";
  }

}
Equimolecular answered 6/6, 2023 at 9:55 Comment(0)
M
1

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.

Misconceive answered 3/8, 2020 at 4:42 Comment(0)
H
0

check for not null and not empty

if (routeinfo["no_route"]?.isNotEmpty == true) {
  // 
}
Hircine answered 15/1 at 19:25 Comment(0)

© 2022 - 2024 β€” McMap. All rights reserved.