Skip to content

Commit 9e91bfb

Browse files
committed
* Added error logs
* Optimized initializing PaginatedItemsBuilder * Added showLoaderOnResetBuilder in PaginationItemsStateHandler to update showLoaderOnReset param for builders with internal state management. * Updated example app * Updated README.md * Updated version
1 parent 8a62afd commit 9e91bfb

8 files changed

Lines changed: 122 additions & 57 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## [1.1.0] - 01/04/2022
2+
3+
* Added error logs
4+
* Optimized initializing PaginatedItemsBuilder
5+
* Added showLoaderOnResetBuilder in PaginationItemsStateHandler to update showLoaderOnReset param for builders with internal state management.
6+
* Updated example app
7+
* Updated README.md
8+
19
## [1.0.9] - 31/03/2022
210

311
* Added ignore pointer to disable onTap for loaders

README.md

Lines changed: 43 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -52,14 +52,18 @@ Future<void> updateProducts({
5252
notifyListeners();
5353
}
5454
55-
final res = await apiFunction(
56-
// startKey is optional and only required when you have pagination support in api
57-
startKey: reset ? null : _productsResponse?.paginationKey,
58-
);
59-
if (reset || _productsResponse == null) {
60-
_productsResponse = res;
61-
} else {
62-
_productsResponse!.update(res);
55+
try {
56+
final res = await apiFunction(
57+
// startKey is optional and only required when you have pagination support in api
58+
startKey: reset ? null : _productsResponse?.paginationKey,
59+
);
60+
if (reset || _productsResponse == null) {
61+
_productsResponse = res;
62+
} else {
63+
_productsResponse!.update(res);
64+
}
65+
} catch(_) {
66+
// handle error
6367
}
6468
notifyListeners();
6569
}
@@ -72,23 +76,36 @@ Future<PaginatedItemsResponse<Product>?> apiFunction({
7276
// can be string or int (page number) or any other type.
7377
dynamic startKey,
7478
}) async {
75-
// startKey necessary if pagination support
76-
final res = await _api.getProducts(startKey: startKey);
77-
78-
return PaginatedItemsResponse<Product>(
79-
80-
// list of items
81-
listItems: res.data?.products,
82-
83-
// only required to pass if pagination supported, else null. (can be of any type)
84-
paginationKey: res.data?.paginationKey,
85-
86-
// unique id, should only be passed in the repository function.
87-
// required for functions like `updateItem`, `findByUid`
88-
// and avoiding duplication of items in list (compares uid)
89-
idGetter: (product) => product.id,
90-
91-
);
79+
try {
80+
// startKey necessary if pagination support
81+
final res = await _api.getProducts(startKey: startKey);
82+
83+
return PaginatedItemsResponse<Product>(
84+
85+
// list of items
86+
listItems: res.data?.products,
87+
88+
// only required to pass if pagination supported, else null. (can be of any type)
89+
paginationKey: res.data?.paginationKey,
90+
91+
// unique id, should only be passed in the repository function.
92+
// required for functions like `updateItem`, `findByUid`
93+
// and avoiding duplication of items in list (compares uid)
94+
idGetter: (product) => product.id,
95+
96+
);
97+
} catch(_) {
98+
// handling error is important as if null is returned, the `response` becomes null,
99+
// which in turn causes infinite loading...
100+
101+
// hence, returning a response with empty list, so that screen shows the no items found text,
102+
// so that the user can refresh the contents, and you can probably have a snackBar or toast,
103+
// notifying the user of the error...
104+
return PaginatedItemsResponse<Product>(
105+
listItems: [],
106+
idGetter: (product) => product.id,
107+
);
108+
}
92109
}
93110
```
94111

@@ -145,6 +162,7 @@ Future<PaginatedItemsResponse<Post>?> updatePosts(dynamic paginationKey) async {
145162
146163
PaginationItemsStateHandler<Post>(
147164
fetchPageData: updatePosts,
165+
showLoaderOnResetBuilder: (itemsFetchScope) => itemsFetchScope == ItemsFetchScope.noItemsRefresh,
148166
builder: (response, fetchPageData) {
149167
return PaginatedItemsBuilder<Post>(
150168
response: response,

example/lib/controllers/posts_controller.dart

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,17 @@ class PostsController extends ChangeNotifier {
2525
notifyListeners();
2626
}
2727

28-
final res = await PostsRepository.getPosts(
29-
startKey: _postsResponse?.paginationKey,
30-
);
31-
if (reset || _postsResponse == null) {
32-
_postsResponse = res;
33-
} else {
34-
_postsResponse!.update(res);
28+
try {
29+
final res = await PostsRepository.getPosts(
30+
startKey: reset ? null : _postsResponse?.paginationKey,
31+
);
32+
if (reset || _postsResponse == null) {
33+
_postsResponse = res;
34+
} else {
35+
_postsResponse!.update(res);
36+
}
37+
} catch (_) {
38+
// handle error
3539
}
3640
notifyListeners();
3741
}

example/lib/screens/home_screen.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ class PostsListWithStateHandledInternally extends StatelessWidget {
7878
child: Scaffold(
7979
body: PaginationItemsStateHandler<Post>(
8080
fetchPageData: updatePosts,
81+
showLoaderOnResetBuilder: (itemsFetchScope) =>
82+
itemsFetchScope == ItemsFetchScope.noItemsRefresh,
8183
builder: (response, fetchPageData) {
8284
return PaginatedItemsBuilder<Post>(
8385
response: response,

example/pubspec.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ packages:
129129
path: ".."
130130
relative: true
131131
source: path
132-
version: "1.0.9"
132+
version: "1.1.0"
133133
path:
134134
dependency: transitive
135135
description:

lib/src/paginated_items_builder.dart

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'dart:developer' as dev;
12
import 'dart:math';
23

34
import 'package:flutter/material.dart';
@@ -182,6 +183,7 @@ class _PaginatedItemsBuilderState<T> extends State<PaginatedItemsBuilder<T>> {
182183
(widget.response != null &&
183184
!widget.response!.hasMoreData &&
184185
!_loadingMoreData)) return;
186+
185187
setState(() {
186188
// if (_initialLoading) {
187189
// _initialLoading = false;
@@ -195,10 +197,18 @@ class _PaginatedItemsBuilderState<T> extends State<PaginatedItemsBuilder<T>> {
195197

196198
try {
197199
await widget.fetchPageData(reset, itemsFetchScope);
198-
} catch (_) {}
200+
} catch (error, stackTrace) {
201+
dev.log(
202+
'\nSomething went wrong.. Most probably the fetchPageData failed due to some error! Please handle any possible errors in the fetchPageData call.',
203+
name: 'PaginationItemsBuilder<$T>',
204+
error: error,
205+
stackTrace: stackTrace,
206+
);
207+
}
199208

200209
if (_initialLoading) _initialLoading = false;
201210
if (_loadingMoreData) _loadingMoreData = false;
211+
202212
try {
203213
setState(() {});
204214
} catch (_) {}
@@ -289,11 +299,7 @@ class _PaginatedItemsBuilderState<T> extends State<PaginatedItemsBuilder<T>> {
289299
? _config?.mockItemGetter<T>()
290300
: _config?.mockItemGetter(widget.mockItemKey);
291301

292-
final itemsStateHandlerAsParent =
293-
context.findAncestorWidgetOfExactType<PaginationItemsStateHandler<T>>();
294-
if (itemsStateHandlerAsParent == null) {
295-
_fetchData(itemsFetchScope: ItemsFetchScope.initialLoad);
296-
}
302+
_fetchData(itemsFetchScope: ItemsFetchScope.initialLoad);
297303

298304
PaginatedItemsBuilder.config ??=
299305
PaginatedItemsBuilderConfig.defaultConfig();

lib/src/pagination_items_state_handler.dart

Lines changed: 44 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:developer' as dev;
2+
13
import 'package:flutter/material.dart';
24
import 'package:paginated_items_builder/paginated_items_builder.dart';
35

@@ -22,10 +24,28 @@ class PaginationItemsStateHandler<T> extends StatefulWidget {
2224
fetchPageData,
2325
) builder;
2426

27+
/// Whether to switch all the cards to their respective loaders when [reset] is true,
28+
/// i.e. if the user pulls down to refresh, or no items were found...
29+
///
30+
/// The callback value is the [ItemsFetchScope], which defines the action calling the
31+
/// fetch data function.
32+
///
33+
/// The [reset] flag will be true only when the [itemsFetchScope] is either
34+
/// [ItemsFetchScope.noItemsRefresh] i.e. no items were found, and user
35+
/// clicked the refresh icon OR [ItemsFetchScope.pullDownToRefresh] i.e.
36+
/// the user wants to refresh the list contents with pull-down action.
37+
///
38+
/// This callback will only be called if [reset] is true.
39+
///
40+
/// By default, [showLoaderOnResetBuilder] is true only if [scope] is [ItemsFetchScope.noItemsRefresh].
41+
final bool Function(ItemsFetchScope itemsFetchScope)?
42+
showLoaderOnResetBuilder;
43+
2544
const PaginationItemsStateHandler({
2645
Key? key,
2746
required this.fetchPageData,
2847
required this.builder,
48+
this.showLoaderOnResetBuilder,
2949
}) : super(key: key);
3050

3151
@override
@@ -35,36 +55,43 @@ class PaginationItemsStateHandler<T> extends StatefulWidget {
3555

3656
class _PaginationItemsStateHandlerState<T>
3757
extends State<PaginationItemsStateHandler<T>> {
38-
PaginatedItemsResponse<T>? itemsResponse;
58+
PaginatedItemsResponse<T>? _itemsResponse;
3959

4060
Future<void> _update(bool reset, ItemsFetchScope scope) async {
41-
if (reset) {
42-
itemsResponse = null;
61+
bool showLoaderOnReset = scope == ItemsFetchScope.noItemsRefresh;
62+
if (reset && widget.showLoaderOnResetBuilder != null) {
63+
showLoaderOnReset = widget.showLoaderOnResetBuilder!(scope);
64+
}
65+
66+
// showLoaderOnReset only used if reset is true...
67+
if (reset && showLoaderOnReset) {
68+
_itemsResponse = null;
4369
setState(() {});
4470
}
4571

4672
try {
47-
final res = await widget.fetchPageData(itemsResponse?.paginationKey);
48-
if (itemsResponse == null) {
49-
itemsResponse = res;
73+
final res = await widget.fetchPageData(
74+
reset ? null : _itemsResponse?.paginationKey,
75+
);
76+
if (reset || _itemsResponse == null) {
77+
_itemsResponse = res;
5078
} else {
51-
itemsResponse!.update(res);
79+
_itemsResponse!.update(res);
5280
}
53-
} catch (_) {}
81+
} catch (error, stackTrace) {
82+
dev.log(
83+
'\nSomething went wrong.. Most probably the fetchPageData failed due to some error! Please handle any possible errors in the fetchPageData call.',
84+
name: 'PaginationItemsStateHandler<$T>',
85+
error: error,
86+
stackTrace: stackTrace,
87+
);
88+
}
5489

5590
try {
5691
setState(() {});
5792
} catch (_) {}
5893
}
5994

6095
@override
61-
void initState() {
62-
_update(false, ItemsFetchScope.initialLoad);
63-
super.initState();
64-
}
65-
66-
@override
67-
Widget build(BuildContext context) {
68-
return widget.builder(itemsResponse, _update);
69-
}
96+
Widget build(BuildContext context) => widget.builder(_itemsResponse, _update);
7097
}

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: paginated_items_builder
22
description: Easier to display items in a list/grid view from your controllers directly or handling state internally with support for pagination.
3-
version: 1.0.9
3+
version: 1.1.0
44
homepage: https://github.com/rithik-dev/paginated_items_builder
55

66
environment:

0 commit comments

Comments
 (0)