Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions example/.flutter-plugins
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# This is a generated file; do not edit or check into version control.
file_picker=/Users/andrespacheco/.pub-cache/hosted/pub.dev/file_picker-6.1.1/
flutter_plugin_android_lifecycle=/Users/andrespacheco/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.17/
permission_handler=/Users/andrespacheco/.pub-cache/hosted/pub.dev/permission_handler-11.0.1/
permission_handler_android=/Users/andrespacheco/.pub-cache/hosted/pub.dev/permission_handler_android-11.1.0/
permission_handler_apple=/Users/andrespacheco/.pub-cache/hosted/pub.dev/permission_handler_apple-9.1.4/
permission_handler_windows=/Users/andrespacheco/.pub-cache/hosted/pub.dev/permission_handler_windows-0.1.3/
file_picker=/Users/carlosdoffiny/.pub-cache/hosted/pub.dev/file_picker-6.2.1/
flutter_plugin_android_lifecycle=/Users/carlosdoffiny/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.24/
permission_handler=/Users/carlosdoffiny/.pub-cache/hosted/pub.dev/permission_handler-11.3.1/
permission_handler_android=/Users/carlosdoffiny/.pub-cache/hosted/pub.dev/permission_handler_android-12.0.13/
permission_handler_apple=/Users/carlosdoffiny/.pub-cache/hosted/pub.dev/permission_handler_apple-9.4.5/
permission_handler_html=/Users/carlosdoffiny/.pub-cache/hosted/pub.dev/permission_handler_html-0.1.3+5/
permission_handler_windows=/Users/carlosdoffiny/.pub-cache/hosted/pub.dev/permission_handler_windows-0.2.1/
2 changes: 1 addition & 1 deletion example/.flutter-plugins-dependencies
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"file_picker","path":"/Users/andrespacheco/.pub-cache/hosted/pub.dev/file_picker-6.1.1/","native_build":true,"dependencies":[]},{"name":"permission_handler_apple","path":"/Users/andrespacheco/.pub-cache/hosted/pub.dev/permission_handler_apple-9.1.4/","native_build":true,"dependencies":[]}],"android":[{"name":"file_picker","path":"/Users/andrespacheco/.pub-cache/hosted/pub.dev/file_picker-6.1.1/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"]},{"name":"flutter_plugin_android_lifecycle","path":"/Users/andrespacheco/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.17/","native_build":true,"dependencies":[]},{"name":"permission_handler_android","path":"/Users/andrespacheco/.pub-cache/hosted/pub.dev/permission_handler_android-11.1.0/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[{"name":"permission_handler_windows","path":"/Users/andrespacheco/.pub-cache/hosted/pub.dev/permission_handler_windows-0.1.3/","native_build":true,"dependencies":[]}],"web":[{"name":"file_picker","path":"/Users/andrespacheco/.pub-cache/hosted/pub.dev/file_picker-6.1.1/","dependencies":[]}]},"dependencyGraph":[{"name":"file_picker","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"permission_handler","dependencies":["permission_handler_android","permission_handler_apple","permission_handler_windows"]},{"name":"permission_handler_android","dependencies":[]},{"name":"permission_handler_apple","dependencies":[]},{"name":"permission_handler_windows","dependencies":[]}],"date_created":"2025-01-15 16:39:31.651420","version":"3.24.1","swift_package_manager_enabled":false}
{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"file_picker","path":"/Users/carlosdoffiny/.pub-cache/hosted/pub.dev/file_picker-6.2.1/","native_build":true,"dependencies":[]},{"name":"permission_handler_apple","path":"/Users/carlosdoffiny/.pub-cache/hosted/pub.dev/permission_handler_apple-9.4.5/","native_build":true,"dependencies":[]}],"android":[{"name":"file_picker","path":"/Users/carlosdoffiny/.pub-cache/hosted/pub.dev/file_picker-6.2.1/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"]},{"name":"flutter_plugin_android_lifecycle","path":"/Users/carlosdoffiny/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.24/","native_build":true,"dependencies":[]},{"name":"permission_handler_android","path":"/Users/carlosdoffiny/.pub-cache/hosted/pub.dev/permission_handler_android-12.0.13/","native_build":true,"dependencies":[]}],"macos":[],"linux":[],"windows":[{"name":"permission_handler_windows","path":"/Users/carlosdoffiny/.pub-cache/hosted/pub.dev/permission_handler_windows-0.2.1/","native_build":true,"dependencies":[]}],"web":[{"name":"file_picker","path":"/Users/carlosdoffiny/.pub-cache/hosted/pub.dev/file_picker-6.2.1/","dependencies":[]},{"name":"permission_handler_html","path":"/Users/carlosdoffiny/.pub-cache/hosted/pub.dev/permission_handler_html-0.1.3+5/","dependencies":[]}]},"dependencyGraph":[{"name":"file_picker","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"permission_handler","dependencies":["permission_handler_android","permission_handler_apple","permission_handler_html","permission_handler_windows"]},{"name":"permission_handler_android","dependencies":[]},{"name":"permission_handler_apple","dependencies":[]},{"name":"permission_handler_html","dependencies":[]},{"name":"permission_handler_windows","dependencies":[]}],"date_created":"2025-02-03 12:59:21.332048","version":"3.24.3","swift_package_manager_enabled":false}
4 changes: 2 additions & 2 deletions example/android/local.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
sdk.dir=/Users/andrespacheco/Library/Android/sdk
flutter.sdk=/Users/andrespacheco/Library/flutter
sdk.dir=/Users/carlosdoffiny/Library/Android/sdk
flutter.sdk=/Users/carlosdoffiny/development/flutter
4 changes: 4 additions & 0 deletions example/lib/pages/remote_data/bloc/pokemon_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,8 @@ class PokemonBloc extends RemoteDataBloc<Pokemon> {
final pokemon = Pokemon.fromJson(response.body);
return pokemon;
}

@override
// TODO: implement data
Pokemon get data => throw UnimplementedError();
}
24 changes: 23 additions & 1 deletion packages/avilatek_bloc/lib/src/remote_data/remote_data_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ export 'package:avilatek_bloc/src/remote_data/remote_data_state.dart';
/// Blocs.
///
/// Any Object/Primitive Data can be accessed via the
/// [RemoteDataInitialized.data] property.
/// [RemoteDataInitialized.data] property or via the
/// [getDataFromState] method.
abstract class RemoteDataBloc<T>
extends Bloc<RemoteDataEvent, RemoteDataState<T>> {
/// Constructor for the RemoteDataBloc.
Expand All @@ -27,9 +28,25 @@ abstract class RemoteDataBloc<T>
) {
_handler = RemoteDataEventHandler<T>();
on<FetchRemoteData>(_mapFetchRemoteDataToState);
on<RemoteDataRestarted>((event, emit) {
emit(RemoteDataUninitialized<T>());
add(const FetchRemoteData());
});
}
late RemoteDataEventHandler<T> _handler;

/// Function that retrieves the data from the current state.
/// It returns a [T] if the state is
/// [RemoteDataInitialized], otherwise it returns null.
/// This method is useful for accessing the data from the current state
/// without having to check the state type.
T? getDataFromState(RemoteDataState<T> state) {
if (state is RemoteDataInitialized<T>) {
return state.data;
}
return null;
}

/// Propagates the [FetchRemoteData] event down to the corresponding event
/// handler.
Future<void> _mapFetchRemoteDataToState(
Expand Down Expand Up @@ -88,4 +105,9 @@ abstract class RemoteDataBloc<T>
RemoteDataState<T> oldState,
FetchRemoteData event,
);

/// Retrieves the data. You should override this method to provide the data
/// to the bloc, using the [getDataFromState] method with an empty [T] object,
/// if this method returns null.
T get data;
}
13 changes: 13 additions & 0 deletions packages/avilatek_bloc/lib/src/remote_data/remote_data_event.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,16 @@ class FetchRemoteData extends RemoteDataEvent {
@override
List<Object?> get props => [simulateError];
}

/// {@template restart_remote_data}
/// Event that triggers a restart, erasing currently loaded data.
/// {@endtemplate}
class RemoteDataRestarted extends RemoteDataEvent {
/// {@macro restart_remote_data}
const RemoteDataRestarted({this.simulateError = false});

/// If true, the [PagedRemoteDataBloc<T>] will simulate an error.
final bool? simulateError;
@override
List<Object?> get props => [simulateError];
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ class RemoteDataEventHandler<T> {
emit(RemoteDataFetched(data));
} catch (e) {
emit(RemoteDataInitialFetchingFailure(e));
emit(RemoteDataUninitialized());
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import 'package:equatable/equatable.dart';
///
extension RemoteDataStateX on RemoteDataState<dynamic> {
/// Returns `true` if the state is not initialized and has no data to show.
bool get isUnititialized => this is! RemoteDataInitialized;
bool get isUnititialized =>
this is! RemoteDataInitialized &&
this is! RemoteDataInitialFetchingFailure;

/// Returns `true` if the state is initialized and has data.
bool get isInitialized => this is RemoteDataInitialized;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,37 @@ export 'package:avilatek_bloc/src/remote_data_paginated/paged_remote_data_state.
///
/// Any Object/Primitive Data can be accessed via the
/// [PagedRemoteDataInitialized.data] property.
/// Or via the [getDataFromState] method.
abstract class PagedRemoteDataBloc<T>
extends Bloc<PagedRemoteDataEvent, PagedRemoteDataState<T>> {
///
PagedRemoteDataBloc() : super(PagedRemoteDataUninitialized()) {
_handler = PagedRemoteDataEventHandler<T>();
on<PagedRemoteDataFetchNextPage>(_mapFetchNextPageRemoteDataToState);

on<PagedRemoteDataRestart>((event, emit) {
on<PagedRemoteDataRestarted>((event, emit) {
emit(PagedRemoteDataUninitialized<T>());
add(const PagedRemoteDataFetchNextPage());
});

on<PagedRemoteDataRetryFetchNextPage>((event, emit) {
add(const PagedRemoteDataFetchNextPage());
});
}
late PagedRemoteDataEventHandler<T> _handler;

/// This method is used to access the data from the current state.
/// It returns a [List<T>] if the state is [PagedRemoteDataInitialized],
/// otherwise it returns null.
/// This method is useful for accessing the data from the current state
/// without having to check the state type.
List<T>? getDataFromState(PagedRemoteDataState<T> state) {
if (state is PagedRemoteDataInitialized<T>) {
return state.data;
}
return null;
}

/// Propagates the [PagedRemoteDataFetchNextPage] event down to the corresponding event
/// handler.
Future<void> _mapFetchNextPageRemoteDataToState(
Expand All @@ -48,7 +65,7 @@ abstract class PagedRemoteDataBloc<T>
),
onNextPageFetched: () => _handler.mapFetchNextPageRemoteDataToState(
event,
state as PagedRemoteDataNextPageFetched<T>,
state as PagedRemoteDataInitialized<T>,
emit,
fetchAndParseNextPage,
),
Expand All @@ -67,7 +84,8 @@ abstract class PagedRemoteDataBloc<T>
} else if (state is PagedRemoteDataUninitialized &&
onPagedRemoteDataUninitialized != null) {
return onPagedRemoteDataUninitialized();
} else if (state is PagedRemoteDataNextPageFetched &&
} else if ((state is PagedRemoteDataNextPageFetched ||
state is PagedRemoteDataNextPageFetchingFailure) &&
onNextPageFetched != null) {
return onNextPageFetched();
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,26 @@ class PagedRemoteDataFetchNextPage extends PagedRemoteDataEvent {
/// {@template restart_remote_data}
/// Event that triggers a restart, erasing currently loaded data.
/// {@endtemplate}
class PagedRemoteDataRestart extends PagedRemoteDataEvent {
class PagedRemoteDataRestarted extends PagedRemoteDataEvent {
/// {@macro restart_remote_data}
const PagedRemoteDataRestart({this.simulateError = false});
const PagedRemoteDataRestarted({this.simulateError = false});

/// If true, the [PagedRemoteDataBloc<T>] will simulate an error.
final bool? simulateError;
@override
List<Object?> get props => [simulateError];
}

/// {@template fetch_remote_data}
/// Event that triggers the retry of the fetching of the next page.
/// {@endtemplate}
class PagedRemoteDataRetryFetchNextPage extends PagedRemoteDataEvent {
/// {@macro fetch_remote_data}
const PagedRemoteDataRetryFetchNextPage({this.simulateError = false});

/// If true, the [PagedRemoteDataBloc<T>] will simulate an error.
final bool? simulateError;

@override
List<Object?> get props => [simulateError];
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,11 @@ class PagedRemoteDataEventHandler<T> {
}
} catch (e) {
emit(PagedRemoteDataFirstPageFetchingFailure(e));
emit(PagedRemoteDataUninitialized());
}
}

/// Handler for [PagedRemoteDataFetchNextPage] + [PagedRemoteDataNextPageFetched] combination.
/// Handler for [PagedRemoteDataFetchNextPage] + [PagedRemoteDataNextPageFetched] combination, and
/// [PagedRemoteDataNextPageFetchingFailure] to retry the fetch.
/// Handles refetch of the remote data.
///
/// On success it emits: [PagedRemoteDataNextPageFetching],
Expand All @@ -55,14 +55,16 @@ class PagedRemoteDataEventHandler<T> {
/// [PagedRemoteDataNextPageFetchingFailure].
Future<void> mapFetchNextPageRemoteDataToState(
PagedRemoteDataFetchNextPage event,
PagedRemoteDataNextPageFetched<T> state,
PagedRemoteDataInitialized<T> state,
Emitter<PagedRemoteDataState<T>> emit,
Future<(List<T>, bool)> Function(
PagedRemoteDataState<T>,
PagedRemoteDataFetchNextPage,
) fetchAndParseNextPage,
) async {
try {
if (state is PagedRemoteDataLastPageFetched) return;

emit(PagedRemoteDataNextPageFetching(state));

if (event.simulateError ?? false) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ class PagedRemoteDataNextPageFetchingSuccess<T>
/// Temporal State that represents the failed fetch of the next page of data.
/// {@endtemplate}
class PagedRemoteDataNextPageFetchingFailure<T>
extends PagedRemoteDataInitialized<T> with PagedRemoteDataFailure<T> {
extends PagedRemoteDataInitialized<T> {
/// {@macro remote_data_refetching_failed}
PagedRemoteDataNextPageFetchingFailure(super.oldState, this.error)
: super.clone();
Expand All @@ -175,3 +175,15 @@ class PagedRemoteDataNextPageFetchingFailure<T>
@override
List<Object?> get props => [...super.props, error];
}

/// This state is emitted when the next page is being fetched again after a
/// failure.

class PagedRemoteDataRetryNextPageFetching<T>
extends PagedRemoteDataInitialized<T> {
/// {@macro remote_data_loaded}
const PagedRemoteDataRetryNextPageFetching(super.data);

/// {@macro remote_data_initialized.clone}
PagedRemoteDataRetryNextPageFetching.clone(super.oldState) : super.clone();
}
2 changes: 1 addition & 1 deletion packages/avilatek_bloc/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: avilatek_bloc
description: Avila Tek Blocs/Cubits
version: 0.2.0
version: 0.2.2
publish_to: none

environment:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,18 +44,18 @@ void main() {
act: (bloc) => bloc.add(const FetchRemoteData()),
expect: () => [
RemoteDataInitialFetching<int>(),
const RemoteDataFetched<int>(1),
const FetchRemoteData<int>(1),
],
);
blocTest<RemoteDataBloc<int>, RemoteDataState<int>>(
'''should emit RemoteDataRefetching and RemoteDataFetched states when FetchRemoteData event is added from initialized state''',
build: () => bloc,
seed: () => const RemoteDataFetched<int>(2),
seed: () => const FetchRemoteData<int>(2),
act: (bloc) => bloc.add(const FetchRemoteData()),
expect: () => [
RemoteDataRefetching<int>(const RemoteDataFetched<int>(2)),
RemoteDataRefetching<int>(const FetchRemoteData<int>(2)),
const RemoteDataRefetchingSuccess<int>(1),
const RemoteDataFetched<int>(1),
const FetchRemoteData<int>(1),
],
);
blocTest<RemoteDataBloc<int>, RemoteDataState<int>>(
Expand All @@ -73,40 +73,40 @@ void main() {
blocTest<RemoteDataBloc<int>, RemoteDataState<int>>(
'''should emit RemoteDataRefetching and RemoteDataRefetchingFailure states when refetch call fails''',
build: () => bloc,
seed: () => const RemoteDataFetched<int>(2),
seed: () => const FetchRemoteData<int>(2),
act: (bloc) => bloc.add(const FetchRemoteData(simulateError: true)),
expect: () {
// Ensure every state is emitted with the previous state.data value
// (2)
return [
RemoteDataRefetching<int>(const RemoteDataFetched<int>(2)),
RemoteDataRefetching<int>(const FetchRemoteData<int>(2)),
isA<RemoteDataRefetchingFailure<int>>()
.having((state) => state.data, 'data', equals(2)),
// Finally, the state should go back to the previous state (2)
const RemoteDataFetched<int>(2),
const FetchRemoteData<int>(2),
];
},
);

blocTest<RemoteDataBloc<int>, RemoteDataState<int>>(
'''should emit incrementing values when FetchRemoteData event is added multiple times, and persists the last value when the call fails''',
build: () => bloc,
seed: () => const RemoteDataFetched<int>(2),
seed: () => const FetchRemoteData<int>(2),
act: (bloc) {
bloc
..add(const FetchRemoteData())
..add(const FetchRemoteData(simulateError: true))
..add(const FetchRemoteData());
},
skip: 8,
expect: () => [const RemoteDataFetched<int>(4)],
expect: () => [const FetchRemoteData<int>(4)],
);
blocTest<RemoteDataBloc<int>, RemoteDataState<int>>(
'''should start in RemoteDataFetched when initialData parameter is provided''',
build: () => RemoteDataBlocImpl(initialData: 3),
verify: (bloc) {
expect(bloc.state, isA<RemoteDataFetched<int>>());
expect((bloc.state as RemoteDataFetched<int>).data, 3);
expect(bloc.state, isA<FetchRemoteData<int>>());
expect((bloc.state as FetchRemoteData<int>).data, 3);
},
);
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class EmitterMock extends Mock implements Emitter<RemoteDataState<dynamic>> {}

class RemoteDataStateFake extends Fake implements RemoteDataState {}

class RemoteDataFetchedFake extends Fake implements RemoteDataFetched {
class RemoteDataFetchedFake extends Fake implements FetchRemoteData {
@override
dynamic data = Object();
}
Expand Down Expand Up @@ -70,7 +70,7 @@ void main() async {
),
),
() => emit(
any<RemoteDataStateFake>(that: isA<RemoteDataFetched>()),
any<RemoteDataStateFake>(that: isA<FetchRemoteData>()),
),
]);
verifyNoMoreInteractions(emit);
Expand Down Expand Up @@ -120,7 +120,7 @@ void main() async {
),
),
() => emit(
any<RemoteDataStateFake>(that: isA<RemoteDataFetched>()),
any<RemoteDataStateFake>(that: isA<FetchRemoteData>()),
),
]);
verifyNoMoreInteractions(emit);
Expand All @@ -138,7 +138,7 @@ void main() async {

verifyInOrder([
() => emit(RemoteDataInitialFetching()),
() => emit(const RemoteDataFetched(1)),
() => emit(const FetchRemoteData(1)),
]);

verifyNoMoreInteractions(emit);
Expand All @@ -158,7 +158,7 @@ void main() async {
verifyInOrder([
() => emit(RemoteDataRefetching(initState)),
() => emit(const RemoteDataRefetchingSuccess(1)),
() => emit(const RemoteDataFetched(1)),
() => emit(const FetchRemoteData(1)),
]);

verifyNoMoreInteractions(emit);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ void main() {
test(
'should return correct RemoteDataFetched boolean getter values',
() async {
const state = RemoteDataFetched('');
const state = FetchRemoteData('');
expect(state.isUnititialized, isFalse);
expect(state.isFetching, isFalse);
expect(state.isInitialFetching, isFalse);
Expand Down
Loading