Skip to content

Instantly share code, notes, and snippets.

@sunderee
Created October 23, 2023 11:17
Show Gist options
  • Save sunderee/5710cd1e71cf39ae2a8871beae3541e3 to your computer and use it in GitHub Desktop.
Save sunderee/5710cd1e71cf39ae2a8871beae3541e3 to your computer and use it in GitHub Desktop.
Base state that can be used in Flutter apps using BLoC (inspired by Riverpod).
import 'package:equatable/equatable.dart';
import 'package:meta/meta.dart';
sealed class BaseState<T> extends Equatable {
bool get isInitial;
bool get isLoading;
bool get isSuccessful;
bool get hasFailed;
T? get data;
Exception? get exception;
const BaseState._();
const factory BaseState.initial() = InitialState<T>;
const factory BaseState.loading() = LoadingState<T>;
const factory BaseState.successful(T data) = SuccessfulState<T>;
const factory BaseState.failed(Exception exception) = FailedState<T>;
}
typedef OnInitialOrLoading<R> = R Function();
typedef OnData<T, R> = R Function(T data);
typedef OnException<R> = R Function(Exception exception);
extension BaseStateExt<T> on BaseState<T> {
T get _safeData {
if (isSuccessful && data != null) {
return data as T;
}
throw StateError('Cannot have null data.');
}
R when<R>({
required OnInitialOrLoading<R> onInitial,
required OnInitialOrLoading<R> onLoading,
required OnData<T, R> onData,
required OnException<R> onException,
}) {
if (isInitial &&
!isLoading &&
!isSuccessful &&
!hasFailed &&
data == null &&
exception == null) {
return onInitial();
}
if (isLoading) {
return onLoading();
}
if (hasFailed || exception != null) {
return onException(exception!);
}
return onData(_safeData);
}
}
@immutable
final class InitialState<T> extends BaseState<T> {
@override
bool get isInitial => true;
@override
bool get isLoading => false;
@override
bool get isSuccessful => false;
@override
bool get hasFailed => false;
@override
final T? data;
@override
final Exception? exception;
const InitialState()
: data = null,
exception = null,
super._();
@override
List<Object?> get props => [
isLoading,
isSuccessful,
hasFailed,
data,
exception,
];
}
@immutable
final class LoadingState<T> extends BaseState<T> {
@override
bool get isInitial => false;
@override
bool get isLoading => true;
@override
bool get isSuccessful => false;
@override
bool get hasFailed => false;
@override
final T? data;
@override
final Exception? exception;
const LoadingState()
: data = null,
exception = null,
super._();
@override
List<Object?> get props => [
isLoading,
isSuccessful,
hasFailed,
data,
exception,
];
}
@immutable
final class SuccessfulState<T> extends BaseState<T> {
@override
bool get isInitial => false;
@override
bool get isLoading => false;
@override
bool get isSuccessful => true;
@override
bool get hasFailed => false;
@override
final T data;
@override
final Exception? exception;
const SuccessfulState(this.data)
: exception = null,
super._();
@override
List<Object?> get props => [
isLoading,
isSuccessful,
hasFailed,
data,
exception,
];
}
@immutable
final class FailedState<T> extends BaseState<T> {
@override
bool get isInitial => false;
@override
bool get isLoading => false;
@override
bool get isSuccessful => false;
@override
bool get hasFailed => true;
@override
final T? data;
@override
final Exception exception;
const FailedState(this.exception)
: data = null,
super._();
@override
List<Object?> get props => [
isLoading,
isSuccessful,
hasFailed,
data,
exception,
];
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment