This is my code for true on everything but empty string, null and false:
if (routeinfo[\"no_route\"] == \"\" || routeinfo[\"no_route\"] == null || routeinfo[
isNull
and isNotNull
. They no longer provide type promotion when Null Safety
is introduced in dart/flutter in the future.isNullOrEmpty
do not provide type promotion, as they are in a different (sub-)scope compared to callsite.isNull
and isNotNull
but keep other helpers as you shouldn't expect them to do type promotion for you.Null Safety
was finally introduced in Dart/Flutter. But as of October 2020, this feature is still not available for stable releases on dart/flutter. Check out the quick guide Null Safety or the thorough Understanding Null Safety.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.
Here's a modified version using getters instead of instance/class methods and covering whitespaces, isNull, and isNotNull.
It also accounts for empty lists and maps now!
Added private getters for safety and modularity/maintainability.
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
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;
}
This presumes Dart 2.7 or above to support Extension Methods.
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.
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.
Rename the Extension classes whatever you like. I tend to name them like this:
XYExtension
Where:
Name examples:
MyAppIntExtension
DartDoubleExtension
TextUtilsStringExtension
OHProviderExtension
Why private getters instead of simple
this == null || this == '' || this == [] || this == 0 || !this
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.
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).
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';
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 operatorYou could do
if (["", null, false, 0].contains(routeinfo["no_route"])) {
// do sth
}