Skip to main content

zeba_academy_result 0.0.1 copy "zeba_academy_result: ^0.0.1" to clipboard
zeba_academy_result: ^0.0.1 copied to clipboard

A lightweight type-safe Result type for representing success and failure in Dart and Flutter applications.

zeba_academy_result #

pub package likes popularity license

A lightweight, type-safe Result type for Dart and Flutter applications.

zeba_academy_result provides a simple way to represent operations that can either succeed with a value or fail with an error, without relying on exceptions for normal application flow.


Features #

  • ✅ Type-safe success and failure results
  • ✅ Generic success value type
  • ✅ Generic failure error type
  • ✅ Success<T, E>
  • ✅ Failure<T, E>
  • ✅ map() successful values
  • ✅ mapError() failure values
  • ✅ fold() pattern-style handling
  • ✅ when() convenient result handling
  • ✅ Safe valueOrNull access
  • ✅ Safe errorOrNull access
  • ✅ getOrThrow() support
  • ✅ getOrElse() fallback values
  • ✅ getOrElseGet() dynamic fallbacks
  • ✅ Value-based equality
  • ✅ Useful toString() output
  • ✅ No runtime dependencies
  • ✅ Pure Dart compatible
  • ✅ Flutter compatible
  • ✅ Small and easy to integrate

Why use Result? #

Many application operations have two possible outcomes:

Success → Value
Failure → Error

For example:

Result<User, LoginError>

can represent:

Success(User)

or:

Failure(LoginError.network)

Instead of throwing exceptions for expected failures, you can explicitly model the result.

This makes APIs easier to understand and handle.


Installation #

Add zeba_academy_result to your pubspec.yaml:

dependencies:
  zeba_academy_result: ^0.0.1

Then run:

flutter pub get

For Dart-only projects:

dart pub get

Import #

import 'package:zeba_academy_result/zeba_academy_result.dart';

Basic Usage #

Success #

const Result<int, String> result = Success(100);

Failure #

const Result<int, String> result = Failure(
  'Something went wrong',
);

The same Result type can therefore represent both outcomes.


Success #

Create a successful result:

const result = Success<int, String>(42);

Access the value:

print(result.value);

Output:

42

Check its state:

print(result.isSuccess); // true
print(result.isFailure); // false

Failure #

Create a failed result:

const result = Failure<int, String>(
  'Network error',
);

Access the error:

print(result.error);

Output:

Network error

Check its state:

print(result.isSuccess); // false
print(result.isFailure); // true

Safe Value Access #

Use valueOrNull when you don't want an exception:

const Result<int, String> result = Failure(
  'Something went wrong',
);

final value = result.valueOrNull;

print(value); // null

For a successful result:

const Result<int, String> result = Success(42);

print(result.valueOrNull); // 42

Safe Error Access #

Use errorOrNull:

const Result<int, String> result = Failure(
  'Network error',
);

print(result.errorOrNull);

Output:

Network error

For success:

const Result<int, String> result = Success(42);

print(result.errorOrNull); // null

fold() #

fold() lets you handle both possible outcomes in one place:

const Result<int, String> result = Success(42);

final message = result.fold(
  onSuccess: (value) => 'Value: $value',
  onFailure: (error) => 'Error: $error',
);

print(message);

Output:

Value: 42

For a failure:

const Result<int, String> result = Failure(
  'Network error',
);

final message = result.fold(
  onSuccess: (value) => 'Value: $value',
  onFailure: (error) => 'Error: $error',
);

print(message);

Output:

Error: Network error

when() #

when() provides pattern-style result handling:

final result = getUser();

result.when(
  success: (user) {
    print('User: $user');
  },
  failure: (error) {
    print('Error: $error');
  },
);

This is useful when building application and repository layers.


map() #

Use map() to transform a successful value.

const Result<int, String> result = Success(10);

final mapped = result.map(
  (value) => value * 2,
);

print(mapped);

Output:

Success(20)

Failures are automatically preserved:

const Result<int, String> result = Failure(
  'Network error',
);

final mapped = result.map(
  (value) => value * 2,
);

print(mapped);

Output:

Failure(Network error)

mapError() #

Use mapError() to transform an error.

const Result<int, String> result = Failure('404');

final mapped = result.mapError(
  (error) => int.parse(error),
);

print(mapped);

Output:

Failure(404)

Successful values are preserved when using mapError().


getOrElse() #

Return a fallback value when the result is a failure:

const Result<int, String> result = Failure(
  'Something went wrong',
);

final value = result.getOrElse(0);

print(value);

Output:

0

For success:

const Result<int, String> result = Success(42);

final value = result.getOrElse(0);

print(value);

Output:

42

getOrElseGet() #

Use getOrElseGet() when the fallback depends on the error:

const Result<int, String> result = Failure(
  'Network error',
);

final value = result.getOrElseGet(
  (error) => error.length,
);

print(value);

The failure error is passed directly to the fallback function.


getOrThrow() #

If you explicitly need the successful value:

const Result<int, String> result = Success(42);

final value = result.getOrThrow();

print(value);

Output:

42

Calling getOrThrow() on a failure throws a StateError:

const Result<int, String> result = Failure(
  'Network error',
);

result.getOrThrow();

Use this method only when a failure is genuinely unexpected.


Typed Errors #

One of the biggest advantages of Result<T, E> is that errors don't have to be strings.

Define an error type:

enum LoginError {
  invalidCredentials,
  network,
  server,
  unknown,
}

Then return:

Result<User, LoginError>

For example:

Result<User, LoginError> login() {
  return const Failure(
    LoginError.invalidCredentials,
  );
}

Handle it safely:

final result = login();

result.when(
  success: (user) {
    print('Welcome ${user.name}');
  },
  failure: (error) {
    switch (error) {
      case LoginError.invalidCredentials:
        print('Invalid credentials');
        break;

      case LoginError.network:
        print('Network error');
        break;

      case LoginError.server:
        print('Server error');
        break;

      case LoginError.unknown:
        print('Unknown error');
        break;
    }
  },
);

Repository Example #

A repository method can explicitly describe its possible outcome:

Future<Result<User, String>> getUser() async {
  try {
    final user = await fetchUser();

    return Success(user);
  } catch (error) {
    return Failure(error.toString());
  }
}

Then:

final result = await repository.getUser();

result.when(
  success: (user) {
    print('Loaded user: $user');
  },
  failure: (error) {
    print('Failed to load user: $error');
  },
);

Flutter Example #

Result works well with Flutter state management and UI layers.

final result = await repository.getUser();

result.when(
  success: (user) {
    setState(() {
      currentUser = user;
    });
  },
  failure: (error) {
    ScaffoldMessenger.of(context).showSnackBar(
      SnackBar(
        content: Text(error),
      ),
    );
  },
);

Chaining Operations #

map() can be chained:

const Result<int, String> result = Success(10);

final finalResult = result
    .map((value) => value * 2)
    .map((value) => value + 5)
    .map((value) => 'Final value: $value');

print(finalResult);

Output:

Success(Final value: 25)

Failures continue through the chain without executing the success transformation.


Equality #

Success and Failure support value equality.

const first = Success<int, String>(42);
const second = Success<int, String>(42);

print(first == second); // true

Failures also support equality:

const first = Failure<int, String>('Error');
const second = Failure<int, String>('Error');

print(first == second); // true

API Overview #

Result #

API Description
isSuccess Whether the result is successful
isFailure Whether the result is a failure
valueOrNull Safely retrieves the success value
errorOrNull Safely retrieves the error
map() Transforms the success value
mapError() Transforms the failure
fold() Handles success and failure
when() Pattern-style result handling
getOrThrow() Retrieves the value or throws
getOrElse() Retrieves value or fallback
getOrElseGet() Retrieves value or computed fallback

Success #

Success<T, E>(value)

Failure #

Failure<T, E>(error)

Design Goals #

zeba_academy_result is intentionally small.

The package focuses on:

  • Explicit success/failure modeling
  • Type safety
  • Simple APIs
  • No unnecessary dependencies
  • Pure Dart compatibility
  • Flutter compatibility
  • Easy testing
  • Predictable behavior

The package does not attempt to replace complete functional-programming libraries.


Requirements #

  • Dart SDK ^3.12.0
  • Flutter compatible
  • No runtime dependencies

Development #

Clone the repository and install dependencies:

git clone https://github.com/zeba-academy/zeba_academy_result.git
cd zeba_academy_result
flutter pub get

Format the project:

dart format lib test

Run static analysis:

flutter analyze

Run tests:

flutter test

Validate package publishing:

flutter pub publish --dry-run

Project Structure #

zeba_academy_result/
├── lib/
│   ├── src/
│   │   ├── result.dart
│   │   ├── success.dart
│   │   └── failure.dart
│   └── zeba_academy_result.dart
│
├── test/
│   └── zeba_academy_result_test.dart
│
├── CHANGELOG.md
├── LICENSE
├── README.md
└── pubspec.yaml

License #

This project is licensed under the GNU General Public License v3.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:


Zeba Academy #

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

Explore courses, tutorials, real-world projects, and hands-on learning resources.

➡️ Main website: zeba.academy

➡️ Courses and resources: code.zeba.academy

➡️ YouTube: Zeba Academy

➡️ Instagram: @zeba.academy


Contributing #

Contributions are welcome.

If you find a bug, have an improvement, or want to suggest a feature:

  1. Open an issue.
  2. Describe the problem or proposed improvement.
  3. Fork the repository.
  4. Create a feature branch.
  5. Add or update tests.
  6. Run dart format lib test.
  7. Run flutter analyze.
  8. Run flutter test.
  9. Submit a pull request.

Please keep contributions focused, tested, and consistent with the package's lightweight design.


Support #

If this package is useful to you, consider:

⭐ Starring the repository 📦 Using the package on pub.dev 🐛 Reporting issues 💡 Suggesting improvements 🤝 Contributing code or documentation


Made with ❤️ by Zeba Academy

0
likes
150
points
56
downloads

Documentation

API reference

Publisher

verified publisherzeba.academy

Weekly Downloads

A lightweight type-safe Result type for representing success and failure in Dart and Flutter applications.

Homepage

License

GPL-3.0 (license)

Dependencies

flutter

More

Packages that depend on zeba_academy_result