feat(feed): add lazy loading

This commit is contained in:
mso7nueh
2024-10-02 20:01:22 +03:00
parent 9ba363e780
commit 9c5716bd5a
5 changed files with 124 additions and 66 deletions

View File

@@ -7,7 +7,7 @@ class DietsCubit extends Cubit<DietsState> {
int currentPage = 1;
late int lastPage;
bool isLoading = false;
// String? _query;
String? _query;
DietsCubit({required this.feedRepository}) : super(const DietsState.loading());
@@ -15,14 +15,49 @@ class DietsCubit extends Cubit<DietsState> {
String? query,
}) async {
try {
// _query = query;
final diets = await feedRepository.getDiets(
_query = query;
final dietsPagination = await feedRepository.getDiets(
query: query,
);
lastPage = diets.meta.lastPage;
emit(DietsState.loaded(diets: diets));
lastPage = dietsPagination.meta.lastPage;
emit(DietsState.loaded(
dietsList: dietsPagination.data,
isLoading: isLoading,
));
} catch (e) {
emit(DietsState.error(errorMessage: e.toString()));
emit(DietsState.error(
errorMessage: e.toString(),
));
}
}
loadNextPage() async {
if (currentPage < lastPage && !isLoading) {
isLoading = true;
emit((state as DietsStateLoaded).copyWith(isLoading: isLoading));
try {
if (currentPage < lastPage) {
currentPage++;
final dietPagination = await feedRepository.getDiets(
page: currentPage,
query: _query,
);
isLoading = false;
emit(
DietsState.loaded(
dietsList: [
...(state as DietsStateLoaded).dietsList,
...dietPagination.data,
],
isLoading: isLoading,
),
);
}
} catch (e) {
emit(DietsState.error(errorMessage: e.toString()));
}
}
}
}

View File

@@ -10,7 +10,8 @@ abstract class DietsState with _$DietsState {
const factory DietsState.loading() = DietsStateLoading;
const factory DietsState.loaded({
required DietsPagination diets,
required List<Diet> dietsList,
required bool isLoading,
}) = DietsStateLoaded;
const factory DietsState.error({

View File

@@ -20,7 +20,7 @@ mixin _$DietsState {
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(DietsPagination diets) loaded,
required TResult Function(List<Diet> dietsList, bool isLoading) loaded,
required TResult Function(String errorMessage) error,
}) =>
throw _privateConstructorUsedError;
@@ -28,7 +28,7 @@ mixin _$DietsState {
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(DietsPagination diets)? loaded,
TResult? Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult? Function(String errorMessage)? error,
}) =>
throw _privateConstructorUsedError;
@@ -36,7 +36,7 @@ mixin _$DietsState {
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(DietsPagination diets)? loaded,
TResult Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
}) =>
@@ -132,7 +132,7 @@ class _$DietsStateImpl implements _DietsState {
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(DietsPagination diets) loaded,
required TResult Function(List<Diet> dietsList, bool isLoading) loaded,
required TResult Function(String errorMessage) error,
}) {
return $default();
@@ -143,7 +143,7 @@ class _$DietsStateImpl implements _DietsState {
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(DietsPagination diets)? loaded,
TResult? Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult? Function(String errorMessage)? error,
}) {
return $default?.call();
@@ -154,7 +154,7 @@ class _$DietsStateImpl implements _DietsState {
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(DietsPagination diets)? loaded,
TResult Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
}) {
@@ -249,7 +249,7 @@ class _$DietsStateLoadingImpl implements DietsStateLoading {
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(DietsPagination diets) loaded,
required TResult Function(List<Diet> dietsList, bool isLoading) loaded,
required TResult Function(String errorMessage) error,
}) {
return loading();
@@ -260,7 +260,7 @@ class _$DietsStateLoadingImpl implements DietsStateLoading {
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(DietsPagination diets)? loaded,
TResult? Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult? Function(String errorMessage)? error,
}) {
return loading?.call();
@@ -271,7 +271,7 @@ class _$DietsStateLoadingImpl implements DietsStateLoading {
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(DietsPagination diets)? loaded,
TResult Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
}) {
@@ -329,9 +329,7 @@ abstract class _$$DietsStateLoadedImplCopyWith<$Res> {
$Res Function(_$DietsStateLoadedImpl) then) =
__$$DietsStateLoadedImplCopyWithImpl<$Res>;
@useResult
$Res call({DietsPagination diets});
$DietsPaginationCopyWith<$Res> get diets;
$Res call({List<Diet> dietsList, bool isLoading});
}
/// @nodoc
@@ -347,38 +345,43 @@ class __$$DietsStateLoadedImplCopyWithImpl<$Res>
@pragma('vm:prefer-inline')
@override
$Res call({
Object? diets = null,
Object? dietsList = null,
Object? isLoading = null,
}) {
return _then(_$DietsStateLoadedImpl(
diets: null == diets
? _value.diets
: diets // ignore: cast_nullable_to_non_nullable
as DietsPagination,
dietsList: null == dietsList
? _value._dietsList
: dietsList // ignore: cast_nullable_to_non_nullable
as List<Diet>,
isLoading: null == isLoading
? _value.isLoading
: isLoading // ignore: cast_nullable_to_non_nullable
as bool,
));
}
/// Create a copy of DietsState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$DietsPaginationCopyWith<$Res> get diets {
return $DietsPaginationCopyWith<$Res>(_value.diets, (value) {
return _then(_value.copyWith(diets: value));
});
}
}
/// @nodoc
class _$DietsStateLoadedImpl implements DietsStateLoaded {
const _$DietsStateLoadedImpl({required this.diets});
const _$DietsStateLoadedImpl(
{required final List<Diet> dietsList, required this.isLoading})
: _dietsList = dietsList;
final List<Diet> _dietsList;
@override
List<Diet> get dietsList {
if (_dietsList is EqualUnmodifiableListView) return _dietsList;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_dietsList);
}
@override
final DietsPagination diets;
final bool isLoading;
@override
String toString() {
return 'DietsState.loaded(diets: $diets)';
return 'DietsState.loaded(dietsList: $dietsList, isLoading: $isLoading)';
}
@override
@@ -386,11 +389,15 @@ class _$DietsStateLoadedImpl implements DietsStateLoaded {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$DietsStateLoadedImpl &&
(identical(other.diets, diets) || other.diets == diets));
const DeepCollectionEquality()
.equals(other._dietsList, _dietsList) &&
(identical(other.isLoading, isLoading) ||
other.isLoading == isLoading));
}
@override
int get hashCode => Object.hash(runtimeType, diets);
int get hashCode => Object.hash(
runtimeType, const DeepCollectionEquality().hash(_dietsList), isLoading);
/// Create a copy of DietsState
/// with the given fields replaced by the non-null parameter values.
@@ -406,10 +413,10 @@ class _$DietsStateLoadedImpl implements DietsStateLoaded {
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(DietsPagination diets) loaded,
required TResult Function(List<Diet> dietsList, bool isLoading) loaded,
required TResult Function(String errorMessage) error,
}) {
return loaded(diets);
return loaded(dietsList, isLoading);
}
@override
@@ -417,10 +424,10 @@ class _$DietsStateLoadedImpl implements DietsStateLoaded {
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(DietsPagination diets)? loaded,
TResult? Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult? Function(String errorMessage)? error,
}) {
return loaded?.call(diets);
return loaded?.call(dietsList, isLoading);
}
@override
@@ -428,12 +435,12 @@ class _$DietsStateLoadedImpl implements DietsStateLoaded {
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(DietsPagination diets)? loaded,
TResult Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
}) {
if (loaded != null) {
return loaded(diets);
return loaded(dietsList, isLoading);
}
return orElse();
}
@@ -477,10 +484,12 @@ class _$DietsStateLoadedImpl implements DietsStateLoaded {
}
abstract class DietsStateLoaded implements DietsState {
const factory DietsStateLoaded({required final DietsPagination diets}) =
_$DietsStateLoadedImpl;
const factory DietsStateLoaded(
{required final List<Diet> dietsList,
required final bool isLoading}) = _$DietsStateLoadedImpl;
DietsPagination get diets;
List<Diet> get dietsList;
bool get isLoading;
/// Create a copy of DietsState
/// with the given fields replaced by the non-null parameter values.
@@ -561,7 +570,7 @@ class _$DietsStateErrorImpl implements DietsStateError {
TResult when<TResult extends Object?>(
TResult Function() $default, {
required TResult Function() loading,
required TResult Function(DietsPagination diets) loaded,
required TResult Function(List<Diet> dietsList, bool isLoading) loaded,
required TResult Function(String errorMessage) error,
}) {
return error(errorMessage);
@@ -572,7 +581,7 @@ class _$DietsStateErrorImpl implements DietsStateError {
TResult? whenOrNull<TResult extends Object?>(
TResult? Function()? $default, {
TResult? Function()? loading,
TResult? Function(DietsPagination diets)? loaded,
TResult? Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult? Function(String errorMessage)? error,
}) {
return error?.call(errorMessage);
@@ -583,7 +592,7 @@ class _$DietsStateErrorImpl implements DietsStateError {
TResult maybeWhen<TResult extends Object?>(
TResult Function()? $default, {
TResult Function()? loading,
TResult Function(DietsPagination diets)? loaded,
TResult Function(List<Diet> dietsList, bool isLoading)? loaded,
TResult Function(String errorMessage)? error,
required TResult orElse(),
}) {

View File

@@ -13,6 +13,11 @@ class RecipesCubit extends Cubit<RecipesState> {
bool isLoading = false;
String? _query;
RecipesCubit({
required this.feedRepository,
required this.recipeRepository,
}) : super(const RecipesState.loading());
loadRecipesList({
String? query,
}) async {
@@ -35,11 +40,6 @@ class RecipesCubit extends Cubit<RecipesState> {
}
}
RecipesCubit({
required this.feedRepository,
required this.recipeRepository,
}) : super(const RecipesState.loading());
loadNextPage() async {
if (currentPage < lastPage && !isLoading) {
isLoading = true;

View File

@@ -57,11 +57,23 @@ class _FeedScreenState extends State<FeedScreen> {
}
}
_onScrollRecipes() {}
_onScrollRecipes() {
if (recipesScrollController.offset >= recipesScrollController.position.maxScrollExtent && !recipesScrollController.position.outOfRange) {
recipesCubit.loadNextPage();
}
}
_onScrollDiets() {}
_onScrollDiets() {
if (dietsScrollController.offset >= dietsScrollController.position.maxScrollExtent && !dietsScrollController.position.outOfRange) {
dietsCubit.loadNextPage();
}
}
_onScrollPlans() {}
_onScrollPlans() {
if (plansScrollController.offset >= plansScrollController.position.maxScrollExtent && !plansScrollController.position.outOfRange) {
// plansCubit.loadNextPage();
}
}
@override
void dispose() {
@@ -133,16 +145,17 @@ class _FeedScreenState extends State<FeedScreen> {
},
body: Builder(
builder: (BuildContext context) {
if (_selectedChipIndex == 0)
if (_selectedChipIndex == 0) {
return _buildAllBlogPosts();
else if (_selectedChipIndex == 1)
} else if (_selectedChipIndex == 1) {
return _buildAllRecipes();
else if (_selectedChipIndex == 2)
} else if (_selectedChipIndex == 2) {
return _buildDiets();
else if (_selectedChipIndex == 3)
} else if (_selectedChipIndex == 3) {
return _buildPlans();
else
} else {
return Center(child: Text(chipsLabelList[_selectedChipIndex]));
}
},
),
),
@@ -274,12 +287,12 @@ class _FeedScreenState extends State<FeedScreen> {
shrinkWrap: true,
physics: BouncingScrollPhysics(),
itemBuilder: (context, index) {
return DietsWidget(diet: state.diets.data[index]);
return DietsWidget(diet: state.dietsList[index]);
},
separatorBuilder: (context, index) {
return SizedBox(height: 8);
},
itemCount: state.diets.data.length,
itemCount: state.dietsList.length,
);
} else if (state is DietsStateError) {
return Scaffold(