import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:provider/provider.dart'; import 'package:tm_app/core/theme/colors.dart'; import 'package:tm_app/data/services/model/tender_data/tender_data.dart'; import 'package:tm_app/view_models/tenders_view_model.dart'; import '../../../core/constants/assets.dart'; import '../../../core/utils/size_config.dart'; import 'tender_card.dart'; class MainTendersSlider extends StatefulWidget { const MainTendersSlider({ required this.tenders, super.key, this.isDesktop = false, }); final bool isDesktop; final List tenders; @override State createState() => _MainTendersSliderState(); } class _MainTendersSliderState extends State { final PageController pageController = PageController(initialPage: 0); int currentPage = 1; ScrollController? _scrollController; @override void initState() { super.initState(); if (widget.isDesktop) { _scrollController = ScrollController(); _scrollController!.addListener(_onScroll); } } void _onScroll() { if (!widget.isDesktop) { return; } final viewModel = context.read(); if (_scrollController!.position.pixels >= _scrollController!.position.maxScrollExtent - 200 && viewModel.hasMoreData && !viewModel.isLoadingMore) { viewModel.loadMoreTenders(); } } void _goToPreviousPage() { if (currentPage == 1) { return; } final targetIndex = currentPage - 2; if (targetIndex < 0) { return; } pageController.previousPage( duration: const Duration(milliseconds: 300), curve: Curves.easeInOutCubic, ); setState(() { currentPage = currentPage - 1; }); final viewModel = context.read(); final previousTenderId = widget.tenders[targetIndex].id!; if (!viewModel.hasFeedbackForTender(previousTenderId)) { viewModel.getTenderFeedback(previousTenderId); } if (!viewModel.hasTenderApprovalForTender(previousTenderId)) { viewModel.getTenderApprovals(previousTenderId); } } void _goToNextPage() { if (currentPage == widget.tenders.length) { return; } final targetIndex = currentPage; if (targetIndex >= widget.tenders.length) { return; } pageController.nextPage( duration: const Duration(milliseconds: 300), curve: Curves.easeInOutCubic, ); setState(() { currentPage = currentPage + 1; }); final viewModel = context.read(); final nextTenderId = widget.tenders[targetIndex].id!; if (!viewModel.hasFeedbackForTender(nextTenderId)) { viewModel.getTenderFeedback(nextTenderId); } if (!viewModel.hasTenderApprovalForTender(nextTenderId)) { viewModel.getTenderApprovals(nextTenderId); } } @override void dispose() { pageController.dispose(); _scrollController?.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final bool isLtr = Directionality.of(context) == TextDirection.ltr; return widget.isDesktop ? _tendersListView() : _tendersSlider(isLtr); } Widget _tendersSlider(bool isLtr) { return Stack( children: [_tendersPageView(), _nextPreviousArrows(isLtr: isLtr)], ); } Widget _tendersPageView() { return SizedBox( width: double.infinity, height: 722.0.h(), child: PageView.builder( controller: pageController, itemCount: widget.tenders.length, onPageChanged: (index) { setState(() { currentPage = index + 1; }); final viewModel = context.read(); final id = widget.tenders[index].id!; if (!viewModel.hasFeedbackForTender(id)) { viewModel.getTenderFeedback(id); } if (!viewModel.hasTenderApprovalForTender(id)) { viewModel.getTenderApprovals(id); } if (index >= widget.tenders.length - 1 && viewModel.hasMoreData && !viewModel.isLoadingMore) { viewModel.loadMoreTenders(); } }, itemBuilder: (context, index) { final tender = widget.tenders[index]; return TenderCard(tender: tender, isDesktop: widget.isDesktop); }, ), ); } Widget _nextPreviousArrows({required bool isLtr}) { return PositionedDirectional( top: 607.0.h(), bottom: 52.0.h(), start: 9.0.w(), end: 9.0.w(), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ _leftArrowButton(isLtr: isLtr), _pageNumberText(), _rightArrowButton(isLtr: isLtr), ], ), ); } Widget _leftArrowButton({required bool isLtr}) { return currentPage != 1 ? InkWell( borderRadius: BorderRadius.circular(100), onTap: _goToPreviousPage, child: Container( width: 32.0.h(), height: 32.0.h(), decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all(color: AppColors.iconColor, width: 1.5), ), alignment: Alignment.center, child: SvgPicture.asset( isLtr ? AssetsManager.arrowLeftSmall : AssetsManager.arrowRightSmall, colorFilter: ColorFilter.mode( AppColors.iconColor, BlendMode.srcATop, ), ), ), ) : SizedBox(width: 32.0.h(), height: 32.0.h()); } Widget _pageNumberText() { final viewModel = context.watch(); return Column( mainAxisSize: MainAxisSize.min, children: [ viewModel.isLoadingMore ? Padding( padding: EdgeInsets.only(top: 4.0.h()), child: SizedBox( width: 16.0.w(), height: 16.0.w(), child: const CircularProgressIndicator( strokeWidth: 2.0, color: AppColors.jellyBean, ), ), ) : Text( '$currentPage/${widget.tenders.length}', style: TextStyle( fontSize: 14.0.sp(), fontWeight: FontWeight.w500, color: AppColors.grey, ), ), ], ); } Widget _rightArrowButton({required bool isLtr}) { return currentPage != widget.tenders.length ? InkWell( borderRadius: BorderRadius.circular(100), onTap: _goToNextPage, child: Container( width: 32.0.h(), height: 32.0.h(), decoration: BoxDecoration( shape: BoxShape.circle, border: Border.all(color: AppColors.iconColor, width: 1.5), ), alignment: Alignment.center, child: SvgPicture.asset( isLtr ? AssetsManager.arrowRightSmall : AssetsManager.arrowLeftSmall, colorFilter: ColorFilter.mode( AppColors.iconColor, BlendMode.srcATop, ), ), ), ) : SizedBox(width: 32.0.h(), height: 32.0.h()); } Widget _tendersListView() { final viewModel = context.watch(); return Column( children: [ Expanded( child: ListView.separated( padding: EdgeInsets.all(16.0.h()), itemCount: widget.tenders.length + (widget.isDesktop && viewModel.isLoadingMore ? 1 : 0), separatorBuilder: (_, __) => SizedBox(height: widget.isDesktop ? 0 : 16.0.h()), itemBuilder: (context, index) { if (index < widget.tenders.length) { final tender = widget.tenders[index]; return TenderCard(tender: tender, isDesktop: widget.isDesktop); } else { return const Padding( padding: EdgeInsets.all(16.0), child: Center( child: CircularProgressIndicator( strokeWidth: 2.0, color: AppColors.jellyBean, ), ), ); } }, ), ), ], ); } }