zeba_academy_nullable

pub package likes popularity license

A lightweight, dependency-free collection of generic nullable-value utilities for Dart and Flutter.

zeba_academy_nullable provides convenient helpers for fallback values, nullable transformations, conditional execution, filtering, first-non-null selection, and other null-safe operations while working naturally with Dart's built-in null-safety system.


Features

  • ✅ Generic nullable-value utilities
  • ✅ Fallback values
  • ✅ Lazy fallback evaluation
  • ✅ Nullable transformations
  • ✅ Transformations with fallback values
  • ✅ Conditional execution
  • ✅ Null-only execution
  • ✅ Conditional value selection
  • ✅ Nullable filtering
  • ✅ First non-null value selection
  • ✅ Generic fold operations
  • ✅ Convenient extension methods
  • ✅ Fully null-safe
  • ✅ No runtime dependencies
  • ✅ Lightweight and easy to integrate
  • ✅ Works with Dart and Flutter
  • ✅ Suitable for production applications

Installation

Add zeba_academy_nullable to your pubspec.yaml:

dependencies:
  zeba_academy_nullable: ^0.0.1

Then run:

flutter pub get

For Dart-only projects:

dart pub get

Import

import 'package:zeba_academy_nullable/zeba_academy_nullable.dart';

Quick Start

Fallback values

Use valueOr when you want a fallback value when the original value is null.

String? username;

final displayName = username.valueOr('Guest');

print(displayName);

Output:

Guest

When the value is not null, the original value is returned:

String? username = 'Sarvesh';

final displayName = username.valueOr('Guest');

print(displayName);

Output:

Sarvesh

Lazy fallback values

Use valueOrElse when calculating the fallback may be expensive.

String? token;

final value = token.valueOrElse(
  () => generateDefaultToken(),
);

The fallback callback is only executed when the value is null.


Nullable transformations

Use mapNullable to transform a nullable value without manually checking for null.

String? name = 'Sarvesh';

final length = name.mapNullable(
  (value) => value.length,
);

print(length);

Output:

7

When the value is null:

String? name;

final length = name.mapNullable(
  (value) => value.length,
);

print(length);

Output:

null

Transform with a fallback

Use mapOr when you want a fallback result.

String? ageText;

final age = ageText.mapOr(
  (value) => int.parse(value),
  0,
);

print(age);

Output:

0

For a non-null value:

String? ageText = '25';

final age = ageText.mapOr(
  (value) => int.parse(value),
  0,
);

print(age);

Output:

25

Lazy transformation fallback

Use mapOrElse when the fallback result should be calculated only when needed.

String? value;

final result = value.mapOrElse(
  (value) => value.length,
  () => calculateDefaultLength(),
);

The fallback callback is not executed when value is non-null.


Execute only for non-null values

Use runIfNotNull when you want to perform an action only when a value exists.

String? username = 'Sarvesh';

username.runIfNotNull(
  (value) {
    print('Hello $value');
  },
);

Output:

Hello Sarvesh

For a null value, the callback is not executed.


Execute only when null

Use ifNull when you need to perform an action only when a value is missing.

String? profileImage;

profileImage.ifNull(() {
  print('Show default profile image');
});

First non-null value

firstNotNull returns the first available non-null value.

final value = NullableUtils.firstNotNull<String>([
  null,
  null,
  'Flutter',
  'Dart',
]);

print(value);

Output:

Flutter

If every value is null, the result is null.

final value = NullableUtils.firstNotNull<String>([
  null,
  null,
  null,
]);

print(value);

Output:

null

First non-null value with fallback

Use firstNotNullOr when you always need a non-null result.

final apiUrl = NullableUtils.firstNotNullOr<String>(
  [
    null,
    null,
    'https://example.com',
  ],
  'https://default.example.com',
);

print(apiUrl);

Null checks

You can explicitly check nullable values using:

NullableUtils.isNull(value);

and:

NullableUtils.isNotNull(value);

Example:

String? username;

if (NullableUtils.isNull(username)) {
  print('Username is missing');
}

Or:

String? username = 'Sarvesh';

if (NullableUtils.isNotNull(username)) {
  print('Username is available');
}

Conditional values

Use takeIf when a value should only be retained when a condition is true.

final result = NullableUtils.takeIf(
  'Flutter',
  true,
);

print(result);

Output:

Flutter

When the condition is false:

final result = NullableUtils.takeIf(
  'Flutter',
  false,
);

print(result);

Output:

null

Predicate-based selection

Use takeIfOrNull when the condition depends on the value itself.

final result = NullableUtils.takeIfOrNull(
  100,
  (value) => value > 50,
);

print(result);

Output:

100

If the predicate fails:

final result = NullableUtils.takeIfOrNull(
  100,
  (value) => value > 200,
);

print(result);

Output:

null

Nullable filtering

Use where to keep a nullable value only when it satisfies a predicate.

final result = NullableUtils.where(
  10,
  (value) => value.isEven,
);

print(result);

Output:

10

Example where the predicate fails:

final result = NullableUtils.where(
  11,
  (value) => value.isEven,
);

print(result);

Output:

null

The extension equivalent is:

final result = 10.where(
  (value) => value.isEven,
);

Conditional conversion

Use convertIf when you want to transform a value only when a condition is satisfied.

final result = NullableUtils.convertIf(
  10,
  (value) => value > 5,
  (value) => 'Value: $value',
);

print(result);

Output:

Value: 10

If the condition fails, null is returned.


Fold nullable values

fold lets you explicitly handle both cases.

String? username;

final message = username.fold(
  onValue: (value) => 'Welcome $value',
  onNull: () => 'Welcome Guest',
);

print(message);

Output:

Welcome Guest

With a value:

String? username = 'Sarvesh';

final message = username.fold(
  onValue: (value) => 'Welcome $value',
  onNull: () => 'Welcome Guest',
);

print(message);

Output:

Welcome Sarvesh

API Reference

NullableUtils

valueOr

static T valueOr<T>(
  T? value,
  T fallback,
)

Returns value when non-null, otherwise returns fallback.


valueOrElse

static T valueOrElse<T>(
  T? value,
  T Function() fallback,
)

Returns value when non-null, otherwise lazily evaluates fallback.


firstNotNull

static T? firstNotNull<T>(
  Iterable<T?> values,
)

Returns the first non-null value.

Returns null when all values are null.


firstNotNullOr

static T firstNotNullOr<T>(
  Iterable<T?> values,
  T fallback,
)

Returns the first non-null value or the supplied fallback.


isNull

static bool isNull<T>(T? value)

Returns true when the value is null.


isNotNull

static bool isNotNull<T>(T? value)

Returns true when the value is not null.


map

static R? map<T, R>(
  T? value,
  R Function(T value) transform,
)

Transforms a non-null value and returns null for a null input.


mapOr

static R mapOr<T, R>(
  T? value,
  R Function(T value) transform,
  R fallback,
)

Transforms a value or returns a fallback.


mapOrElse

static R mapOrElse<T, R>(
  T? value,
  R Function(T value) transform,
  R Function() fallback,
)

Transforms a value or lazily computes a fallback.


runIfNotNull

static T? runIfNotNull<T>(
  T? value,
  void Function(T value) action,
)

Runs an action only when the value is non-null.


ifNull

static T? ifNull<T>(
  T? value,
  void Function() action,
)

Runs an action only when the value is null.


takeIf

static T? takeIf<T>(
  T value,
  bool condition,
)

Returns the value when the condition is true.


takeIfOrNull

static T? takeIfOrNull<T>(
  T value,
  bool Function(T value) predicate,
)

Returns the value when the predicate succeeds.


convertIf

static R? convertIf<T, R>(
  T? value,
  bool Function(T value) condition,
  R Function(T value) convert,
)

Conditionally converts a nullable value.


let

static R? let<T, R>(
  T? value,
  R? Function(T value) transform,
)

Applies a transformation only when the value is non-null.


where

static T? where<T>(
  T? value,
  bool Function(T value) predicate,
)

Keeps a nullable value when the predicate succeeds.


nullable

static T? nullable<T>(
  T? value,
)

Explicitly preserves a nullable value.


fold

static R fold<T, R>(
  T? value, {
  required R Function(T value) onValue,
  required R Function() onNull,
})

Handles both nullable and non-null cases.


Extension API

The package also provides:

extension NullableValueExtension<T> on T?

Available extension methods:

valueOr()
valueOrElse()
mapNullable()
mapOr()
mapOrElse()
runIfNotNull()
ifNull()
takeIf()
takeIfOrNull()
where()
fold()

This allows concise syntax such as:

final result = username
    .mapNullable((value) => value.trim())
    .mapOr(
      (value) => value.toUpperCase(),
      'UNKNOWN',
    );

Generic Usage

The utilities work with custom types as well.

class User {
  final String name;

  const User(this.name);
}

You can write:

User? user;

final name = user.mapNullable(
  (value) => value.name,
);

Or:

final displayName = user.mapOr(
  (value) => value.name,
  'Guest',
);

No special model classes or wrappers are required.


Flutter Example

The package can be useful inside Flutter widgets and ViewModels.

class ProfileViewModel {
  String? username;
  String? profileImage;

  String get displayName {
    return username.valueOr('Guest');
  }

  String get imageUrl {
    return profileImage.valueOr(
      'assets/images/default_profile.png',
    );
  }
}

Another example:

Text(
  user?.name.valueOr('Unknown') ?? 'Unknown',
)

For more complex transformations:

final displayName = user?.name.mapOr(
  (value) => value.trim().isEmpty
      ? 'Unknown'
      : value,
  'Unknown',
);

Design Philosophy

zeba_academy_nullable is intentionally small.

The package does not introduce another nullable abstraction such as:

Option<T>
Some<T>
None<T>
Maybe<T>
Nullable<T>

Instead, it works directly with Dart's native:

T?

This keeps the package:

  • Simple
  • Familiar
  • Lightweight
  • Generic
  • Easy to adopt
  • Compatible with existing Dart code

For explicit Some/None style programming, a dedicated optional-value abstraction is more appropriate.


Dependencies

This package has zero runtime dependencies.

It relies only on Dart's standard language and null-safety features.


Compatibility

The package is designed for modern Dart and Flutter projects.

environment:
  sdk: ">=3.12.0 <4.0.0"
  flutter: ">=1.17.0"

Testing

The package includes comprehensive unit tests covering:

  • Fallback values
  • Lazy fallbacks
  • Nullable transformations
  • Transformation fallbacks
  • Conditional execution
  • Null execution
  • First non-null selection
  • Conditional filtering
  • Conditional conversion
  • Fold operations
  • Extension methods
  • Generic behavior

Run the test suite with:

flutter test

Static Analysis

Run:

flutter analyze

The package is intended to maintain a clean analyzer result before every release.


Formatting

Format the package with:

dart format lib test

Publishing

Before publishing to pub.dev, run:

flutter pub publish --dry-run

If everything is correct:

flutter pub publish

Contributing

Contributions are welcome.

Before submitting a pull request:

  1. Keep APIs generic and null-safe.
  2. Avoid unnecessary dependencies.
  3. Add tests for new functionality.
  4. Run dart format.
  5. Run flutter analyze.
  6. Run flutter test.
  7. Update documentation when public APIs change.
  8. Keep the package focused on nullable-value utilities.

License

This project is licensed under the GNU General Public License v3.0 (GPL-3.0).

See the LICENSE file for the complete license text.


About Me

✨ I’m Sufyan bin Uzayr, an open-source developer passionate about building and sharing meaningful projects.

You can learn more about me and my work at sufyanism.com or connect with me on LinkedIn.


Your All-in-One Learning Hub!

🚀 Explore courses and resources in coding, tech, and development at Zeba Academy.

Empower yourself with practical skills through curated tutorials, real-world projects, and hands-on experience. Level up your tech game today! 💻✨

Zeba Academy is a learning platform dedicated to coding, technology, and development.

➡ Visit the main site: zeba.academy

➡ Explore hands-on courses and resources: code.zeba.academy

➡ Check out the YouTube channel for more tutorials: Zeba Academy on YouTube

➡ Follow on Instagram: Zeba Academy on Instagram


Thank You

Thank you for visiting and supporting this project! ❤️

If you find zeba_academy_nullable useful, consider giving the project a star, sharing it with other Dart and Flutter developers, and contributing to the open-source community.

Built with ❤️ for the Dart & Flutter community.