From fa38ca7dc156154ba580028f7b191a6ceefb4aa2 Mon Sep 17 00:00:00 2001 From: Mohamed dawood Date: Fri, 18 Feb 2022 20:41:49 +0200 Subject: [PATCH] 0.0.4 --- ready/example/lib/ready_grid.dart | 16 +- ready/example/lib/ready_list.dart | 17 +- ready/example/lib/responsive.dart | 18 +- ready/example/pubspec.lock | 2 +- ready/lib/src/controllers/controllers.dart | 2 +- .../lib/src/controllers/loading_handler.dart | 145 +++++++++++++++ .../controllers/ready_list_controller.dart | 171 +----------------- ...sh_indicators.dart => footer_loading.dart} | 2 +- ready/lib/src/ready_list/ready_list.dart | 27 ++- .../src/responsive_data_table/data_table.dart | 19 +- .../data_table_source.dart | 4 +- ready/pubspec.yaml | 2 +- 12 files changed, 206 insertions(+), 219 deletions(-) create mode 100644 ready/lib/src/controllers/loading_handler.dart rename ready/lib/src/ready_list/{refresh_indicators.dart => footer_loading.dart} (96%) diff --git a/ready/example/lib/ready_grid.dart b/ready/example/lib/ready_grid.dart index 9acfd16..356712b 100644 --- a/ready/example/lib/ready_grid.dart +++ b/ready/example/lib/ready_grid.dart @@ -34,15 +34,15 @@ class ReadyGridExample extends StatelessWidget { } class ReadyListCubit extends Cubit> - implements RemoteReadyListController { + implements ReadyListController { ReadyListCubit(ReadyListState initialState) : super(initialState); @override - Future> loadData( - {ICancelToken? cancelToken, - required int skip, - required int pageSize}) async { - var list = await FakeRepo.asyncList(pageSize); - return ReadyListResponse.success(items: list, total: 100); - } + ListLoadingHandler? get handler => DefaultListLoadingHandler( + loadData: (skip, pageSize, cancelToken) async { + var list = await FakeRepo.asyncList(pageSize); + return ReadyListResponse.success(items: list, total: 100); + }, + controller: this, + ); } diff --git a/ready/example/lib/ready_list.dart b/ready/example/lib/ready_list.dart index 07ae506..179e9e3 100644 --- a/ready/example/lib/ready_list.dart +++ b/ready/example/lib/ready_list.dart @@ -56,14 +56,15 @@ class ReadyListExample extends StatelessWidget { } class ReadyListCubit extends Cubit> - implements RemoteReadyListController { + implements ReadyListController { ReadyListCubit(ReadyListState initialState) : super(initialState); + @override - Future> loadData( - {ICancelToken? cancelToken, - required int skip, - required int pageSize}) async { - var list = await FakeRepo.asyncList(30, const Duration(seconds: 3)); - return ReadyListResponse.success(items: list, total: 100); - } + ListLoadingHandler? get handler => DefaultListLoadingHandler( + loadData: (skip, pageSize, cancelToken) async { + var list = await FakeRepo.asyncList(30, const Duration(seconds: 3)); + return ReadyListResponse.success(items: list, total: 100); + }, + controller: this, + ); } diff --git a/ready/example/lib/responsive.dart b/ready/example/lib/responsive.dart index 5522f6e..234b1de 100644 --- a/ready/example/lib/responsive.dart +++ b/ready/example/lib/responsive.dart @@ -35,7 +35,7 @@ class ResponsiveList extends StatelessWidget { SearchFilter( decoration: const InputDecoration(hintText: 'Search here'), onChange: (String? value) { - controller.loadInitialData(16); + controller.handler?.loadInitialData(16); }, ), ], @@ -75,15 +75,15 @@ class ResponsiveList extends StatelessWidget { } class ReadyListCubit extends Cubit> - implements RemoteReadyListController { + implements ReadyListController { ReadyListCubit() : super(ReadyListState()); @override - Future> loadData( - {ICancelToken? cancelToken, - required int skip, - required int pageSize}) async { - var list = await FakeRepo.asyncList(pageSize); - return ReadyListResponse.success(items: list, total: 100); - } + ListLoadingHandler? get handler => DefaultListLoadingHandler( + loadData: (skip, pageSize, cancelToken) async { + var list = await FakeRepo.asyncList(pageSize); + return ReadyListResponse.success(items: list, total: 100); + }, + controller: this, + ); } diff --git a/ready/example/pubspec.lock b/ready/example/pubspec.lock index 31988aa..dd0abc5 100644 --- a/ready/example/pubspec.lock +++ b/ready/example/pubspec.lock @@ -169,7 +169,7 @@ packages: path: ".." relative: true source: path - version: "0.0.2" + version: "0.0.4" sky_engine: dependency: transitive description: flutter diff --git a/ready/lib/src/controllers/controllers.dart b/ready/lib/src/controllers/controllers.dart index 9d04d70..74303cd 100644 --- a/ready/lib/src/controllers/controllers.dart +++ b/ready/lib/src/controllers/controllers.dart @@ -3,11 +3,11 @@ library controllers; import 'dart:async'; import 'package:equatable/equatable.dart'; -import 'package:flutter/material.dart'; import 'package:ready/ready.dart'; import '../enums.dart'; +part 'loading_handler.dart'; part 'ready_list_controller.dart'; part 'ready_list_state.dart'; diff --git a/ready/lib/src/controllers/loading_handler.dart b/ready/lib/src/controllers/loading_handler.dart new file mode 100644 index 0000000..47808bc --- /dev/null +++ b/ready/lib/src/controllers/loading_handler.dart @@ -0,0 +1,145 @@ +part of controllers; + +abstract class ListLoadingHandler { + /// used to load initial data + Future loadInitialData(int pageSize); + + ///used to refresh data + Future refreshData(int pageSize); + + /// used to load next data + Future nextData(int pageSize); +} + +/// this is default handler for loading data into [ReadyListController] +class DefaultListLoadingHandler extends ListLoadingHandler { + final Future> Function( + int skip, int pageSize, ICancelToken? cancelToken) loadData; + final ICancelToken Function()? generateCancelToken; + final ReadyListController controller; + DefaultListLoadingHandler({ + required this.loadData, + required this.controller, + this.generateCancelToken, + }); + + ReadyListState get state => controller.state; + Function(ReadyListState state) get emit => controller.emit; + + void _emitSuccess(_Success result) { + state.whenOrNull( + initialLoading: (cancelToken) { + if (result.items.isEmpty) { + emit(state.empty()); + } else { + emit(state.loaded(result.items, result.total)); + } + }, + refreshing: (items, total, cancelToken) { + if (result.items.isEmpty) { + emit(state.empty()); + } else { + emit(state.loaded(result.items, result.total)); + } + }, + loadingNext: (items, total, cancelToken) { + emit(state.loaded([...items, ...result.items], result.total)); + }, + ); + } + + void _emitResults( + ReadyListResponse result, ReadyListState previousState) { + if (result is _Success) { + _emitSuccess(result); + } else if (result is _Cancel) { + emit(previousState); + } else if (result is _Error) { + emit(state.error(result.error)); + } else { + throw UnsupportedError('Unsupported response'); + } + } + + void _checkDuplicatedLoading() { + state.whenOrNull( + initialLoading: (cancelToken) { + if (cancelToken != null) { + throw Exception( + "You can not make multiple load you should cancel running one first"); + } + }, + loadingNext: (items, total, cancelToken) { + if (cancelToken != null) { + throw Exception( + "You can not make multiple load you should cancel running one first"); + } + }, + refreshing: (items, total, cancelToken) { + if (cancelToken != null) { + throw Exception( + "You can not make multiple load you should cancel running one first"); + } + }, + ); + } + + @override + Future loadInitialData(int pageSize) async { + _checkDuplicatedLoading(); + var previousState = state; + var _cancelToken = generateCancelToken?.call(); + emit(state.initialLoading(_cancelToken)); + try { + var results = await loadData(0, pageSize, _cancelToken); + _emitResults(results, previousState); + } catch (e) { + emit(previousState); + rethrow; + } + } + + @override + Future refreshData(int pageSize) async { + _checkDuplicatedLoading(); + var previousState = state; + if (previousState.type != ListStateType.loaded) { + throw Exception( + "Refreshing must be called when state is Loaded try call loadInitial"); + } + var _cancelToken = generateCancelToken?.call(); + + emit(state.refreshing(_cancelToken)); + try { + var results = await loadData(0, pageSize, _cancelToken); + _emitResults(results, previousState); + } catch (e) { + emit(previousState); + rethrow; + } + } + + @override + Future nextData(int pageSize) async { + _checkDuplicatedLoading(); + var previousState = state; + if (previousState.type != ListStateType.loaded) { + throw Exception( + "Load next must be called when state is Loaded try call loadInitial"); + } + if (previousState.items.length >= previousState.total) { + throw Exception("There is no data to load"); + } + var _cancelToken = generateCancelToken?.call(); + + emit(state.loadingNext(_cancelToken)); + try { + var results = + await loadData(previousState.items.length, pageSize, _cancelToken); + _emitResults(results, previousState); + } catch (e) { + emit(previousState); + rethrow; + } + } +} diff --git a/ready/lib/src/controllers/ready_list_controller.dart b/ready/lib/src/controllers/ready_list_controller.dart index 92c6a39..10a341f 100644 --- a/ready/lib/src/controllers/ready_list_controller.dart +++ b/ready/lib/src/controllers/ready_list_controller.dart @@ -9,175 +9,14 @@ abstract class ReadyListController { /// emitting new state to stream void emit(ReadyListState state); -} - -extension ReadyListControllerExt on ReadyListController { - bool get isRemoteController => this is RemoteReadyListController; - RemoteReadyListController? get remote => - this as RemoteReadyListController?; -} -abstract class RemoteReadyListController extends ReadyListController { - /// use current state.copyWith to return the - @protected - Future> loadData({ - ICancelToken? cancelToken, - required int skip, - required int pageSize, - }); -} - -mixin CancelHandlerMixin on RemoteReadyListController { - ICancelToken generateCancelToken(); - void cancelRunning([dynamic reason]) { - state.whenOrNull( - initialLoading: (cancelToken) { - cancelToken?.cancel(reason); - }, - loadingNext: (items, total, cancelToken) { - cancelToken?.cancel(reason); - }, - refreshing: (items, total, cancelToken) { - cancelToken?.cancel(reason); - }, - ); - } + /// this will manage loading and refreshing data + /// you can use [DefaultListLoadingHandler] + ListLoadingHandler? get handler; } -extension ReadyListRemoteControllerExt on RemoteReadyListController { - void _emitSuccess(_Success result) { - state.whenOrNull( - initialLoading: (cancelToken) { - if (result.items.isEmpty) { - emit(state.empty()); - } else { - emit(state.loaded(result.items, result.total)); - } - }, - refreshing: (items, total, cancelToken) { - if (result.items.isEmpty) { - emit(state.empty()); - } else { - emit(state.loaded(result.items, result.total)); - } - }, - loadingNext: (items, total, cancelToken) { - emit(state.loaded([...items, ...result.items], result.total)); - }, - ); - } - - void _emitResults( - ReadyListResponse result, ReadyListState previousState) { - if (result is _Success) { - _emitSuccess(result); - } else if (result is _Cancel) { - emit(previousState); - } else if (result is _Error) { - emit(state.error(result.error)); - } else { - throw UnsupportedError('Unsupported response'); - } - } - - void _checkDuplicatedLoading() { - state.whenOrNull( - initialLoading: (cancelToken) { - if (cancelToken != null) { - throw Exception( - "You can not make multiple load you should cancel running one first"); - } - }, - loadingNext: (items, total, cancelToken) { - if (cancelToken != null) { - throw Exception( - "You can not make multiple load you should cancel running one first"); - } - }, - refreshing: (items, total, cancelToken) { - if (cancelToken != null) { - throw Exception( - "You can not make multiple load you should cancel running one first"); - } - }, - ); - } - - ICancelToken? _getCancelToken() { - var controller = this; - if (controller is CancelHandlerMixin) { - return controller.generateCancelToken(); - } - return null; - } - - Future loadInitialData(int pageSize) async { - _checkDuplicatedLoading(); - var previousState = state; - var _cancelToken = _getCancelToken(); - emit(state.initialLoading(_cancelToken)); - try { - var results = await loadData( - cancelToken: _cancelToken, - skip: 0, - pageSize: pageSize, - ); - _emitResults(results, previousState); - } catch (e) { - emit(previousState); - rethrow; - } - } - - Future refreshData(int pageSize) async { - _checkDuplicatedLoading(); - var previousState = state; - if (previousState.type != ListStateType.loaded) { - throw Exception( - "Refreshing must be called when state is Loaded try call loadInitial"); - } - var _cancelToken = _getCancelToken(); - - emit(state.refreshing(_cancelToken)); - try { - var results = await loadData( - cancelToken: _cancelToken, - skip: 0, - pageSize: pageSize, - ); - _emitResults(results, previousState); - } catch (e) { - emit(previousState); - rethrow; - } - } - - Future nextData(int pageSize) async { - _checkDuplicatedLoading(); - var previousState = state; - if (previousState.type != ListStateType.loaded) { - throw Exception( - "Load next must be called when state is Loaded try call loadInitial"); - } - if (previousState.items.length >= previousState.total) { - throw Exception("There is no data to load"); - } - var _cancelToken = _getCancelToken(); - - emit(state.loadingNext(_cancelToken)); - try { - var results = await loadData( - cancelToken: _cancelToken, - skip: previousState.items.length, - pageSize: pageSize, - ); - _emitResults(results, previousState); - } catch (e) { - emit(previousState); - rethrow; - } - } - +extension ReadyListRemoteControllerExt on ReadyListController { + bool get hasHandler => handler != null; void addItem(T item) { state.whenOrNull( loaded: (items, total) { diff --git a/ready/lib/src/ready_list/refresh_indicators.dart b/ready/lib/src/ready_list/footer_loading.dart similarity index 96% rename from ready/lib/src/ready_list/refresh_indicators.dart rename to ready/lib/src/ready_list/footer_loading.dart index bf449b8..7f722e0 100644 --- a/ready/lib/src/ready_list/refresh_indicators.dart +++ b/ready/lib/src/ready_list/footer_loading.dart @@ -26,7 +26,7 @@ class _FooterLoading> return _buildWidget( TextButton( onPressed: () { - controller.remote?.nextData(config.pageSize); + controller.handler?.nextData(config.pageSize); }, child: Text(config.loadMoreText), ), diff --git a/ready/lib/src/ready_list/ready_list.dart b/ready/lib/src/ready_list/ready_list.dart index 1e9ece8..dac8f3d 100644 --- a/ready/lib/src/ready_list/ready_list.dart +++ b/ready/lib/src/ready_list/ready_list.dart @@ -15,9 +15,9 @@ import '../shimmers/shimmers.dart'; import '../utils.dart'; part 'config.dart'; +part 'footer_loading.dart'; part 'grids.dart'; part 'ready_screen_loader.dart'; -part 'refresh_indicators.dart'; class ReadyList> extends StatefulWidget implements ReadyListConfigOptions { @@ -186,8 +186,8 @@ class _ReadyListState> var _config = _ReadyListConfigOptionsDefaults.effective(widget, context); state.whenOrNull( needInitialLoading: () { - if (widget.controller.isRemoteController) { - widget.controller.remote!.loadInitialData(_config.pageSize); + if (widget.controller.hasHandler) { + widget.controller.handler!.loadInitialData(_config.pageSize); } }, ); @@ -199,8 +199,8 @@ class _ReadyListState> var _config = _ReadyListConfigOptionsDefaults.effective(widget, context); state.whenOrNull( needInitialLoading: () { - if (widget.controller.isRemoteController) { - widget.controller.remote!.loadInitialData(_config.pageSize); + if (widget.controller.hasHandler) { + widget.controller.handler!.loadInitialData(_config.pageSize); } }, ); @@ -239,9 +239,8 @@ class _ReadyListState> if (scrollInfo.metrics.pixels > 0) { if (scrollInfo.metrics.pixels >= scrollInfo.metrics.maxScrollExtent - 200) { - if (widget.controller - is! RemoteReadyListController) { - (widget.controller as RemoteReadyListController) + if (widget.controller.hasHandler) { + widget.controller.handler! .nextData(_config.pageSize); } } @@ -265,7 +264,7 @@ class _ReadyListState> } Future _onRefresh(_ReadyListConfigOptionsDefaults _config) async { - if (widget.controller.isRemoteController) { + if (widget.controller.hasHandler) { if (state.mayWhen( orElse: () => true, loaded: (_, __) => false, @@ -274,7 +273,7 @@ class _ReadyListState> } var isVisible = Ready.isVisible(context); if (isVisible) { - await (widget.controller.remote!).refreshData(_config.pageSize); + await (widget.controller.handler!).refreshData(_config.pageSize); } } } @@ -282,7 +281,7 @@ class _ReadyListState> _buildRefresh(_ReadyListConfigOptionsDefaults _config, SliverOverlapAbsorberHandle? absorber) { double edgeOffset = absorber?.layoutExtent ?? 0; - if (widget.controller.isRemoteController) { + if (widget.controller.hasHandler) { return RefreshIndicator( onRefresh: () => _onRefresh(_config), edgeOffset: edgeOffset, @@ -350,7 +349,7 @@ class _ReadyListState> ), if (widget.innerFooterSlivers != null) ...widget.innerFooterSlivers!(state), - if (showFooterLoading && widget.controller.isRemoteController) + if (showFooterLoading && widget.controller.hasHandler) _FooterLoading( shrinkWrap: shrinkWrap, config: _config, @@ -445,8 +444,8 @@ class _ReadyListState> loading: loading, error: error, config: _config.placeholdersConfig, - onReload: ctrl.isRemoteController - ? () => ctrl.remote!.loadInitialData(_config.pageSize) + onReload: ctrl.hasHandler + ? () => ctrl.handler!.loadInitialData(_config.pageSize) : null, ); } diff --git a/ready/lib/src/responsive_data_table/data_table.dart b/ready/lib/src/responsive_data_table/data_table.dart index 5a7e261..317cc8b 100644 --- a/ready/lib/src/responsive_data_table/data_table.dart +++ b/ready/lib/src/responsive_data_table/data_table.dart @@ -21,10 +21,10 @@ class _DataTableState> @override void initState() { - if (widget.source.controller.isRemoteController) { + if (widget.source.controller.hasHandler) { widget.source.controller.state.whenOrNull( needInitialLoading: () { - widget.source.controller.remote! + widget.source.controller.handler! .loadInitialData(widget.source.paging.rowsPerPage); }, ); @@ -34,10 +34,10 @@ class _DataTableState> @override void didUpdateWidget(covariant _DataTable oldWidget) { - if (widget.source.controller.isRemoteController) { + if (widget.source.controller.hasHandler) { widget.source.controller.state.whenOrNull( needInitialLoading: () { - widget.source.controller.remote! + widget.source.controller.handler! .loadInitialData(widget.source.paging.rowsPerPage); }, ); @@ -128,19 +128,20 @@ class _DataTableState> orElse: () => false, loaded: (items, total) => true, empty: () => true, + error: (message) => true, ), onRefresh: () { controller.state.whenOrNull( empty: () { - controller.remote + controller.handler ?.loadInitialData(widget.source.paging.rowsPerPage); }, error: (e) { - controller.remote + controller.handler ?.loadInitialData(widget.source.paging.rowsPerPage); }, loaded: (_, __) { - controller.remote?.refreshData(widget.source.paging.rowsPerPage); + controller.handler?.refreshData(widget.source.paging.rowsPerPage); }, ); }, @@ -170,7 +171,9 @@ class _DataTableState> filters: filters, controller: widget.source.controller, ), - if (widget.options.dataTable?.refreshButton != null) _buildRefreshIcon(), + if (widget.options.dataTable?.refreshButton != null && + controller.hasHandler) + _buildRefreshIcon(), ]; } } diff --git a/ready/lib/src/responsive_data_table/data_table_source.dart b/ready/lib/src/responsive_data_table/data_table_source.dart index 3773aef..645f563 100644 --- a/ready/lib/src/responsive_data_table/data_table_source.dart +++ b/ready/lib/src/responsive_data_table/data_table_source.dart @@ -103,7 +103,7 @@ class _DataTableSource> controller.state.whenOrNull( loaded: (items, total) { if (paging.rowsPerPage * paging.currentPage > items.length) { - controller.remote?.nextData(paging.rowsPerPage); + controller.handler?.nextData(paging.rowsPerPage); } }, ); @@ -119,7 +119,7 @@ class _DataTableSource> controller.state.whenOrNull( loaded: (items, total) { if (paging.rowsPerPage * paging.currentPage > items.length) { - controller.remote?.nextData(paging.rowsPerPage); + controller.handler?.nextData(paging.rowsPerPage); } }, ); diff --git a/ready/pubspec.yaml b/ready/pubspec.yaml index 156d973..c940f5a 100644 --- a/ready/pubspec.yaml +++ b/ready/pubspec.yaml @@ -1,6 +1,6 @@ name: ready description: Package that contains complete list solution and admin dashboard -version: 0.0.2 +version: 0.0.4 homepage: https://github.com/mo-ah-dawood/ready environment: