Added pagination to recommand in home

This commit is contained in:
llsajjad
2025-08-18 15:30:22 +03:30
parent d9d5502e79
commit 95e7b516b3
6 changed files with 127 additions and 37 deletions
+6 -3
View File
@@ -6,7 +6,7 @@ import 'package:tm_app/data/services/model/recommended_tenders_response/recommen
class HomeRepository {
HomeRepository({required HomeService homeService})
: _homeService = homeService;
: _homeService = homeService;
final HomeService _homeService;
Future<Result<HomeResponseModel>> getHome({
@@ -15,7 +15,10 @@ class HomeRepository {
return _homeService.getHome(homeRequestModel: homeRequestModel);
}
Future<Result<RecommendedTendersResponse>> getHomeRecommendedTenders() async {
return _homeService.getRecommendedTenders();
Future<Result<RecommendedTendersResponse>> getHomeRecommendedTenders({
required int limit,
required int offset,
}) async {
return _homeService.getRecommendedTenders(limit: limit, offset: offset);
}
}
+6 -3
View File
@@ -12,7 +12,7 @@ import '../../core/network/network_manager.dart';
class HomeService {
HomeService({required NetworkManager networkManager})
: _networkManager = networkManager;
: _networkManager = networkManager;
final NetworkManager _networkManager;
Future<Result<HomeResponseModel>> getHome({
@@ -39,9 +39,12 @@ class HomeService {
}
}
Future<Result<RecommendedTendersResponse>> getRecommendedTenders() async {
Future<Result<RecommendedTendersResponse>> getRecommendedTenders({
required int limit,
required int offset,
}) async {
final result = await _networkManager.makeRequest(
'/api/v1/tenders/recommend',
'/api/v1/tenders/recommend?limit=$limit&offset=$offset',
method: 'GET',
(json) => RecommendedTendersResponse.fromJson(json),
);
+37 -8
View File
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:tm_app/core/utils/result.dart';
import 'package:tm_app/data/services/model/home/home_request/home_request_model.dart';
import 'package:tm_app/data/services/model/recommended_tenders_response/recommended_tenders_response.dart';
import 'package:tm_app/data/services/model/tender_data/tender_data.dart';
import '../data/repositories/home_repository.dart';
import '../data/services/model/home/home_response/home_response_model.dart';
@@ -10,9 +11,9 @@ class HomeViewModel with ChangeNotifier {
final HomeRepository _homeRepository;
HomeViewModel({required HomeRepository homeRepository})
: _homeRepository = homeRepository {
: _homeRepository = homeRepository {
getHome(homeRequestModel: HomeRequestModel(id: '1'));
getHomeRecommandTenders();
getHomeRecommandTenders(reset: true);
}
bool _isLoading = false;
@@ -24,8 +25,14 @@ class HomeViewModel with ChangeNotifier {
HomeResponseModel? get homeResponse => _homeResponse;
RecommendedTendersResponse? _recommendedTendersResponse;
RecommendedTendersResponse? get recommendedTendersResponse =>
_recommendedTendersResponse;
final List<TenderData> _tenders = [];
final int _pageSize = 20;
int _currentPage = 0;
bool _isLoadingMore = false;
bool _hasMore = true;
List<TenderData> get tenders => _tenders;
bool get isLoadingMore => _isLoadingMore;
bool get hasMore => _hasMore;
Future<void> getHome({HomeRequestModel? homeRequestModel}) async {
_isLoading = true;
@@ -49,23 +56,45 @@ class HomeViewModel with ChangeNotifier {
notifyListeners();
}
Future<void> getHomeRecommandTenders() async {
_isLoading = true;
Future<void> getHomeRecommandTenders({bool reset = false}) async {
if (_isLoadingMore || (!_hasMore && !reset)) {
return;
}
if (reset) {
_currentPage = 0;
_hasMore = true;
_tenders.clear();
}
_isLoadingMore = true;
_errorMessage = null;
notifyListeners();
final result = await _homeRepository.getHomeRecommendedTenders();
final result = await _homeRepository.getHomeRecommendedTenders(
limit: _pageSize,
offset: _currentPage * _pageSize,
);
switch (result) {
case Ok<RecommendedTendersResponse>():
_recommendedTendersResponse = result.value;
if (_recommendedTendersResponse?.data?.tenders.isNotEmpty ?? false) {
_tenders.addAll(
_recommendedTendersResponse!.data!.tenders.whereType<TenderData>(),
);
_currentPage++;
} else {
_hasMore = false;
}
break;
case Error<RecommendedTendersResponse>():
_errorMessage = result.error.toString();
break;
}
_isLoading = false;
_isLoadingMore = false;
notifyListeners();
}
}
+26 -9
View File
@@ -5,7 +5,6 @@ import 'package:tm_app/core/routes/app_routes.dart';
import 'package:tm_app/core/theme/colors.dart';
import 'package:tm_app/core/utils/size_config.dart';
import 'package:tm_app/data/services/model/home/home_response/home_response_model.dart';
import 'package:tm_app/data/services/model/recommended_tenders_response/recommended_tenders_response.dart';
import 'package:tm_app/view_models/home_view_model.dart';
import 'package:tm_app/views/home/widgets/notification_card.dart';
@@ -42,7 +41,7 @@ class DesktopHomePage extends StatelessWidget {
SizedBox(height: 32.0.h()),
_yourTenderText(),
SizedBox(height: 8.0.h()),
_bottomListView(homeViewModel.recommendedTendersResponse!),
_bottomListView(homeViewModel),
],
),
);
@@ -162,20 +161,38 @@ class DesktopHomePage extends StatelessWidget {
);
}
Widget _bottomListView(
RecommendedTendersResponse recommendedTendersResponse,
) {
Widget _bottomListView(HomeViewModel homeViewModel) {
final controller = ScrollController();
controller.addListener(() {
if (controller.position.pixels >=
controller.position.maxScrollExtent - 200) {
if (!homeViewModel.isLoadingMore && homeViewModel.hasMore) {
homeViewModel.getHomeRecommandTenders();
}
}
});
return SizedBox(
width: 740,
height: 387.0,
child: ListView.builder(
controller: controller,
padding: EdgeInsets.only(bottom: 20),
itemCount: recommendedTendersResponse.data!.tenders.length,
itemCount:
homeViewModel.tenders.length +
(homeViewModel.isLoadingMore ? 1 : 0),
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return TendersListItem(
tender: recommendedTendersResponse.data!.tenders[index]!,
);
if (index < homeViewModel.tenders.length) {
return TendersListItem(tender: homeViewModel.tenders[index]);
} else {
return Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: CircularProgressIndicator(color: AppColors.secondary50),
),
);
}
},
),
);
+27 -9
View File
@@ -6,7 +6,6 @@ import 'package:tm_app/core/routes/app_routes.dart';
import 'package:tm_app/core/theme/colors.dart';
import 'package:tm_app/core/utils/size_config.dart';
import 'package:tm_app/data/services/model/home/home_response/home_response_model.dart';
import 'package:tm_app/data/services/model/recommended_tenders_response/recommended_tenders_response.dart';
import 'package:tm_app/view_models/home_view_model.dart';
import 'package:tm_app/views/home/widgets/notification_card.dart';
import 'package:tm_app/views/shared/tender_app_bar.dart';
@@ -46,7 +45,7 @@ class MobileHomePage extends StatelessWidget {
_secondTenderCardsRow(homeViewModel.homeResponse!),
SizedBox(height: 40.0.h()),
_yourTenderText(),
_bottomListView(homeViewModel.recommendedTendersResponse!),
_bottomListView(homeViewModel),
],
),
);
@@ -164,20 +163,39 @@ class MobileHomePage extends StatelessWidget {
);
}
Widget _bottomListView(
RecommendedTendersResponse recommendedTendersResponse,
) {
Widget _bottomListView(HomeViewModel homeViewModel) {
final controller = ScrollController();
controller.addListener(() {
if (controller.position.pixels >=
controller.position.maxScrollExtent - 200) {
if (!homeViewModel.isLoadingMore && homeViewModel.hasMore) {
homeViewModel.getHomeRecommandTenders();
}
}
});
return Container(
constraints: BoxConstraints(maxHeight: 387.0.h()),
width: double.infinity,
child: ListView.builder(
controller: controller,
padding: EdgeInsets.symmetric(horizontal: 24.0.w(), vertical: 15.0.h()),
itemCount: recommendedTendersResponse.data!.tenders.length,
itemCount:
homeViewModel.tenders.length +
(homeViewModel.isLoadingMore ? 1 : 0),
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return TendersListItem(
tender: recommendedTendersResponse.data!.tenders[index]!,
);
if (index < homeViewModel.tenders.length) {
return TendersListItem(tender: homeViewModel.tenders[index]);
} else {
return Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: CircularProgressIndicator(color: AppColors.secondary50),
),
);
}
},
),
);
+25 -5
View File
@@ -5,7 +5,6 @@ import 'package:tm_app/core/routes/app_routes.dart';
import 'package:tm_app/core/theme/colors.dart';
import 'package:tm_app/core/utils/size_config.dart';
import 'package:tm_app/data/services/model/home/home_response/home_response_model.dart';
import 'package:tm_app/data/services/model/recommended_tenders_response/recommended_tenders_response.dart';
import 'package:tm_app/view_models/home_view_model.dart';
import 'package:tm_app/views/home/widgets/notification_card.dart';
@@ -50,7 +49,7 @@ class TabletHomePage extends StatelessWidget {
SizedBox(height: 32.0.h()),
_yourTenderText(),
SizedBox(height: 8.0.h()),
_bottomListView(homeViewModel.recommendedTendersResponse!),
_bottomListView(homeViewModel),
],
),
),
@@ -180,15 +179,36 @@ class TabletHomePage extends StatelessWidget {
);
}
Widget _bottomListView(RecommendedTendersResponse recommendedTendersResponse) {
Widget _bottomListView(HomeViewModel homeViewModel) {
final controller = ScrollController();
controller.addListener(() {
if (controller.position.pixels >=
controller.position.maxScrollExtent - 200) {
if (!homeViewModel.isLoadingMore && homeViewModel.hasMore) {
homeViewModel.getHomeRecommandTenders();
}
}
});
return SizedBox(
width: double.infinity,
height: 387.0.h(),
child: ListView.builder(
itemCount: recommendedTendersResponse.data!.tenders.length,
itemCount:
homeViewModel.tenders.length +
(homeViewModel.isLoadingMore ? 1 : 0),
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return TendersListItem(tender: recommendedTendersResponse.data!.tenders[index]!);
if (index < homeViewModel.tenders.length) {
return TendersListItem(tender: homeViewModel.tenders[index]);
} else {
return Center(
child: Padding(
padding: EdgeInsets.all(16.0),
child: CircularProgressIndicator(color: AppColors.secondary50),
),
);
}
},
),
);