From 9b5181d68c650825bc8e3afd523dbca26e0c1bbb Mon Sep 17 00:00:00 2001 From: nkhanh44 Date: Wed, 30 Aug 2023 00:40:45 +0700 Subject: [PATCH 1/3] [#17] Implement load more surveys --- lib/screens/home/home_pages_widget.dart | 52 +++++++++++++++++---- lib/screens/home/home_screen.dart | 14 +++++- lib/screens/home/home_state.dart | 3 +- lib/screens/home/home_view_model.dart | 39 ++++++++++++---- lib/usecases/base/use_case_result.dart | 4 ++ test/screens/home/home_view_model_test.dart | 2 +- 6 files changed, 92 insertions(+), 22 deletions(-) diff --git a/lib/screens/home/home_pages_widget.dart b/lib/screens/home/home_pages_widget.dart index 091ab77..e1e06cc 100644 --- a/lib/screens/home/home_pages_widget.dart +++ b/lib/screens/home/home_pages_widget.dart @@ -7,23 +7,59 @@ import '../../gen/assets.gen.dart'; const _imageOpacity = 0.6; -class HomePagesWidget extends StatelessWidget { +class HomePagesWidget extends StatefulWidget { final List surveys; final ValueNotifier currentPage; final PageController _pageController = PageController(); final VoidCallback onNextButtonPressed; + final bool isRefreshing; + final VoidCallback onLoadMore; - HomePagesWidget({ + const HomePagesWidget({ Key? key, required this.surveys, required this.currentPage, required this.onNextButtonPressed, + required this.onLoadMore, + required this.isRefreshing, }) : super(key: key); + @override + State createState() => _HomePagesWidgetState(); +} + +class _HomePagesWidgetState extends State { + final PageController _pageController = PageController(initialPage: 0); + + @override + void dispose() { + _pageController.dispose(); + super.dispose(); + } + + void _handleNextButtonPressed(int index) { + if (widget.currentPage.value < widget.surveys.length - 1) { + widget.currentPage.value = index + 1; + _pageController.jumpToPage(widget.currentPage.value); + } + } + + void _handlePageChanged(int index) { + widget.currentPage.value = index; + + if (index == widget.surveys.length - 1) { + widget.onLoadMore(); + } + } + @override Widget build(BuildContext context) { + if (widget.isRefreshing && widget.surveys.isNotEmpty) { + _pageController.jumpToPage(0); + } + return PageView.builder( - itemCount: surveys.length, + itemCount: widget.surveys.length, controller: _pageController, itemBuilder: (_, int index) { return Container( @@ -34,7 +70,7 @@ class HomePagesWidget extends StatelessWidget { opacity: _imageOpacity, child: FadeInImage.assetNetwork( placeholder: Assets.images.placeholder.path, - image: surveys[index].coverImageUrl, + image: widget.surveys[index].coverImageUrl, fit: BoxFit.cover, width: double.infinity, height: double.infinity, @@ -51,8 +87,8 @@ class HomePagesWidget extends StatelessWidget { right: 0, ), child: HomeFooterWidget( - survey: surveys[index], - onNextButtonPressed: onNextButtonPressed, + survey: widget.surveys[index], + onNextButtonPressed: widget.onNextButtonPressed, ), ), ), @@ -62,9 +98,7 @@ class HomePagesWidget extends StatelessWidget { ), ); }, - onPageChanged: (int index) { - currentPage.value = index; - }, + onPageChanged: _handlePageChanged, ); } } diff --git a/lib/screens/home/home_screen.dart b/lib/screens/home/home_screen.dart index 580fa54..4a21744 100644 --- a/lib/screens/home/home_screen.dart +++ b/lib/screens/home/home_screen.dart @@ -34,6 +34,10 @@ class _HomeScreenState extends ConsumerState { } Future _initData() async { + _loadSurveys(); + } + + Future _loadSurveys({bool isRefreshing = false}) async { ref.read(homeViewModelProvider.notifier).loadSurveys(isRefreshing: false); } @@ -43,11 +47,15 @@ class _HomeScreenState extends ConsumerState { loading: () => _buildHomeScreen(isLoading: true), error: () => _buildHomeScreen(), loadCachedSurveysSuccess: () => _buildHomeScreen(), - loadSurveysSuccess: () => _buildHomeScreen(), + loadSurveysSuccess: (isRefreshing) => + _buildHomeScreen(isRefreshing: isRefreshing), ); } - Widget _buildHomeScreen({bool isLoading = false}) { + Widget _buildHomeScreen({ + bool isLoading = false, + bool isRefreshing = false, + }) { final surveys = ref.watch(_surveysStreamProvider).value ?? []; final errorMessage = ref.watch(_errorStreamProvider).value ?? ""; @@ -92,6 +100,8 @@ class _HomeScreenState extends ConsumerState { extra: survey, ); }, + onLoadMore: _loadSurveys, + isRefreshing: isRefreshing ), const HomeHeaderWidget(), Align( diff --git a/lib/screens/home/home_state.dart b/lib/screens/home/home_state.dart index 7d666de..4369283 100644 --- a/lib/screens/home/home_state.dart +++ b/lib/screens/home/home_state.dart @@ -9,7 +9,8 @@ class HomeState with _$HomeState { const factory HomeState.loadCachedSurveysSuccess() = _LoadCachedSurveysSuccess; - const factory HomeState.loadSurveysSuccess() = _LoadSurveysSuccess; + const factory HomeState.loadSurveysSuccess(bool isRefreshing) = + _LoadSurveysSuccess; const factory HomeState.error() = _error; } diff --git a/lib/screens/home/home_view_model.dart b/lib/screens/home/home_view_model.dart index 6c9faac..92d7019 100644 --- a/lib/screens/home/home_view_model.dart +++ b/lib/screens/home/home_view_model.dart @@ -7,9 +7,9 @@ import 'package:survey_flutter/usecases/base/base_use_case.dart'; import 'package:survey_flutter/usecases/get_cached_surveys_use_case.dart'; import 'package:survey_flutter/usecases/get_surveys_use_case.dart'; -// TODO: Integrate load more -const _pageNumber = 1; -const _pageSize = 10; +int _pageNumber = 1; +const _pageSize = 5; +List _loadedSurveys = []; final homeViewModelProvider = StateNotifierProvider.autoDispose((ref) { @@ -39,25 +39,46 @@ class HomeViewModel extends StateNotifier { final _error = StreamController(); Stream get error => _error.stream; + void _handleError(Failed result) { + var errorMessage = result.getErrorMessage(); + var isNotFoundError = result.isNotFoundError(); + + if (isNotFoundError) { + _surveys.add(_loadedSurveys); + state = const HomeState.loadSurveysSuccess(false); + } else { + _error.add(errorMessage); + state = const HomeState.error(); + } + } + Future loadSurveys({required bool isRefreshing}) async { if (!isRefreshing) { _loadSurveysFromCache(); } - _loadSurveysFromRemote(); + _loadSurveysFromRemote(isRefreshing: isRefreshing); } - void _loadSurveysFromRemote() async { + void _loadSurveysFromRemote({required bool isRefreshing}) async { + if (isRefreshing) { + _loadedSurveys.clear(); + _pageNumber = 1; + } final result = await _getSurveysUseCase.call(SurveysParams( pageNumber: _pageNumber, pageSize: _pageSize, )); + if (result is Success>) { final newSurveys = result.value; - _surveys.add(newSurveys); - state = const HomeState.loadSurveysSuccess(); + if (newSurveys.isNotEmpty) { + _loadedSurveys.addAll(newSurveys); + _surveys.add(_loadedSurveys); + state = HomeState.loadSurveysSuccess(isRefreshing); + _pageNumber++; + } } else if (result is Failed) { - _error.add((result as Failed).getErrorMessage()); - state = const HomeState.error(); + _handleError(result as Failed); } } diff --git a/lib/usecases/base/use_case_result.dart b/lib/usecases/base/use_case_result.dart index 976794f..c456ed1 100644 --- a/lib/usecases/base/use_case_result.dart +++ b/lib/usecases/base/use_case_result.dart @@ -23,4 +23,8 @@ class Failed extends Result { String getErrorMessage() => NetworkExceptions.getErrorMessage(exception.actualException); + + bool isNotFoundError() => + this.exception.actualException == + const NetworkExceptions.notFound("Not found"); } diff --git a/test/screens/home/home_view_model_test.dart b/test/screens/home/home_view_model_test.dart index c486072..e7004af 100644 --- a/test/screens/home/home_view_model_test.dart +++ b/test/screens/home/home_view_model_test.dart @@ -56,7 +56,7 @@ void main() { stateStream, emitsInOrder([ const HomeState.loadCachedSurveysSuccess(), - const HomeState.loadSurveysSuccess() + const HomeState.loadSurveysSuccess(false), ])); }, ); From 0ae502cecf48170a0b5a6c8866c43cb6b8729c4c Mon Sep 17 00:00:00 2001 From: nkhanh44 Date: Wed, 30 Aug 2023 10:08:50 +0700 Subject: [PATCH 2/3] [#17] Refactor --- lib/screens/home/home_pages_widget.dart | 8 -------- lib/screens/home/home_screen.dart | 4 ++-- lib/screens/home/home_view_model.dart | 9 +++------ 3 files changed, 5 insertions(+), 16 deletions(-) diff --git a/lib/screens/home/home_pages_widget.dart b/lib/screens/home/home_pages_widget.dart index e1e06cc..abd6583 100644 --- a/lib/screens/home/home_pages_widget.dart +++ b/lib/screens/home/home_pages_widget.dart @@ -10,7 +10,6 @@ const _imageOpacity = 0.6; class HomePagesWidget extends StatefulWidget { final List surveys; final ValueNotifier currentPage; - final PageController _pageController = PageController(); final VoidCallback onNextButtonPressed; final bool isRefreshing; final VoidCallback onLoadMore; @@ -37,13 +36,6 @@ class _HomePagesWidgetState extends State { super.dispose(); } - void _handleNextButtonPressed(int index) { - if (widget.currentPage.value < widget.surveys.length - 1) { - widget.currentPage.value = index + 1; - _pageController.jumpToPage(widget.currentPage.value); - } - } - void _handlePageChanged(int index) { widget.currentPage.value = index; diff --git a/lib/screens/home/home_screen.dart b/lib/screens/home/home_screen.dart index 4a21744..eab9cbd 100644 --- a/lib/screens/home/home_screen.dart +++ b/lib/screens/home/home_screen.dart @@ -100,8 +100,8 @@ class _HomeScreenState extends ConsumerState { extra: survey, ); }, - onLoadMore: _loadSurveys, - isRefreshing: isRefreshing + onLoadMore: _loadSurveys, + isRefreshing: isRefreshing, ), const HomeHeaderWidget(), Align( diff --git a/lib/screens/home/home_view_model.dart b/lib/screens/home/home_view_model.dart index 92d7019..25b2b66 100644 --- a/lib/screens/home/home_view_model.dart +++ b/lib/screens/home/home_view_model.dart @@ -8,7 +8,7 @@ import 'package:survey_flutter/usecases/get_cached_surveys_use_case.dart'; import 'package:survey_flutter/usecases/get_surveys_use_case.dart'; int _pageNumber = 1; -const _pageSize = 5; +const _pageSize = 10; List _loadedSurveys = []; final homeViewModelProvider = @@ -40,14 +40,11 @@ class HomeViewModel extends StateNotifier { Stream get error => _error.stream; void _handleError(Failed result) { - var errorMessage = result.getErrorMessage(); - var isNotFoundError = result.isNotFoundError(); - - if (isNotFoundError) { + if (result.isNotFoundError()) { _surveys.add(_loadedSurveys); state = const HomeState.loadSurveysSuccess(false); } else { - _error.add(errorMessage); + _error.add(result.getErrorMessage()); state = const HomeState.error(); } } From 0f3a00be29e7718111d39be827ed1ec2da631dbd Mon Sep 17 00:00:00 2001 From: nkhanh44 Date: Thu, 31 Aug 2023 13:46:43 +0700 Subject: [PATCH 3/3] [#17] Refactor --- lib/screens/home/home_screen.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/screens/home/home_screen.dart b/lib/screens/home/home_screen.dart index eab9cbd..feaf64c 100644 --- a/lib/screens/home/home_screen.dart +++ b/lib/screens/home/home_screen.dart @@ -33,12 +33,14 @@ class _HomeScreenState extends ConsumerState { _initData(); } - Future _initData() async { + void _initData() { _loadSurveys(); } Future _loadSurveys({bool isRefreshing = false}) async { - ref.read(homeViewModelProvider.notifier).loadSurveys(isRefreshing: false); + ref + .read(homeViewModelProvider.notifier) + .loadSurveys(isRefreshing: isRefreshing); } @override