Skip to content

Comments

Sealed Classes & Status Handling Refactor#3396

Closed
Aniketkhote wants to merge 3 commits intojonataslaw:masterfrom
Aniketkhote:master
Closed

Sealed Classes & Status Handling Refactor#3396
Aniketkhote wants to merge 3 commits intojonataslaw:masterfrom
Aniketkhote:master

Conversation

@Aniketkhote
Copy link
Contributor

✨ New Features

  • Sealed Classes for Status Handling

    • Refactored GetStatus to use Dart’s sealed classes for improved type safety and pattern matching
    • Added exhaustive pattern matching with the match method
    • Added when method for side-effect-based status handling
    • Added mapSuccess for transforming success values
    • Added dataOrNull and errorOrNull extensions for safer data access

🔧 Improvements

  • Type Safety: Stronger typing across state management APIs
  • Documentation: Comprehensive docs for all public APIs
  • Testing: Extended test coverage for new sealed class implementation
  • Performance: Optimized state updates with efficient equality checks

⚠️ Breaking Changes

  • GetStatus is now a sealed class with a private constructor
  • ErrorStatus now accepts only one type parameter
  • Custom status implementations must now extend predefined status classes
  • isCustom getter only returns true for CustomStatus instances

🛠 Migration Guide

1. Pattern Matching

Replace if-else chains with match:

// Before
if (status.isLoading) {
  return LoadingWidget();
} else if (status.isError) {
  return ErrorWidget(status.errorMessage);
} else {
  return SuccessWidget(status.data);
}

// After
return status.match(
  loading: () => LoadingWidget(),
  error: (error) => ErrorWidget(error?.toString() ?? 'Unknown error'),
  success: (data) => SuccessWidget(data),
  empty: () => EmptyWidget(),
  custom: () => CustomWidget(),
);

2. Error Status

Update usages of ErrorStatus with two type parameters → now only one:

// Before
ErrorStatus<MyData, Exception> status;

// After
ErrorStatus<MyData> status;

3. Custom Status

Custom implementations should now extend predefined status classes.


This PR makes state handling safer, more expressive, and future-proof.

Comment on lines +15 to +20
// Factory constructors for each variant
const factory GetStatus.loading() = LoadingStatus<T>._;
const factory GetStatus.error([Object? error]) = ErrorStatus<T>._;
const factory GetStatus.empty() = EmptyStatus<T>._;
const factory GetStatus.success(T data) = SuccessStatus<T>._;
const factory GetStatus.custom() = CustomStatus<T>._;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API does not match the previous options.

  const factory RxStatus.loading() = RxLoading;
  const factory RxStatus.loadingMore() = RxLoadingMore;
  const factory RxStatus.success() = RxSuccess;
  const factory RxStatus.error([String? message]) = RxError;
  const factory RxStatus.empty() = RxEmpty;

loadmore is missing. and custom is new.
error accept string not object.

Comment on lines +39 to +49
/// Returns the data if this is a [SuccessStatus], otherwise returns null
T? get dataOrNull => switch (this) {
SuccessStatus(data: var d) => d,
_ => null,
};

/// Returns the error if this is an [ErrorStatus], otherwise returns null
Object? get errorOrNull => switch (this) {
ErrorStatus(error: var e) => e,
_ => null,
};

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I this is not needed i guess

Comment on lines +110 to +138
extension GetStatusExt<T> on GetStatus<T> {
/// Returns the data if this is a [SuccessStatus], otherwise returns [defaultValue]
T dataOr(T defaultValue) => switch (this) {
SuccessStatus(data: var d) => d,
_ => defaultValue,
};

/// Transforms the data if this is a [SuccessStatus], otherwise returns the same status
GetStatus<R> mapSuccess<R>(R Function(T) transform) => switch (this) {
SuccessStatus(data: var d) => GetStatus.success(transform(d)),
LoadingStatus() => GetStatus.loading(),
ErrorStatus(error: var e) => GetStatus.error(e),
EmptyStatus() => GetStatus.empty(),
CustomStatus() => GetStatus.custom(),
};

/// Executes the appropriate callback based on the status and returns the result
R when<R>({
R Function()? loading,
R Function(Object? error)? error,
R Function()? empty,
R Function(T data)? success,
R Function()? custom,
}) {
return switch (this) {
LoadingStatus() =>
loading?.call() ?? (throw StateError('No handler for loading state')),
ErrorStatus(error: var e) =>
error?.call(e) ?? (throw StateError('No handler for error state')),

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i like the way you think. but i feel this is too much apis.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, are you suggesting we should just keep the existing APIs as they are and mainly add sealed, without making bigger changes?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes.

@Aniketkhote Aniketkhote closed this by deleting the head repository Oct 2, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants