Skip to content

Commit 62e80b7

Browse files
committed
Expose public state for sliver widgets and improve docs
ParseLiveSliverListWidget and ParseLiveSliverGridWidget now expose public State classes with refreshData and loadMoreData methods, accessible via GlobalKey for parent control. Documentation and README updated to reflect new usage patterns. Minor code cleanup and formatting improvements applied throughout.
1 parent c2ff6d8 commit 62e80b7

File tree

7 files changed

+178
-75
lines changed

7 files changed

+178
-75
lines changed

PR_CHECKLIST.md

Lines changed: 37 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -15,30 +15,29 @@
1515
- [x] **Sync with Server**: `syncLocalCacheWithServer()` syncs data to server
1616

1717
### Widget Offline Support
18-
- [x] **ParseLiveList**: `offlineMode` parameter enables local caching
19-
- [x] **ParseLiveSliverList**: `offlineMode` parameter enables local caching
20-
- [x] **ParseLiveSliverGrid**: `offlineMode` parameter enables local caching
21-
- [x] **ParseLivePageView**: `offlineMode` parameter enables local caching
18+
- [x] **ParseLiveListWidget**: `offlineMode` parameter enables local caching
19+
- [x] **ParseLiveSliverListWidget**: `offlineMode` parameter enables local caching
20+
- [x] **ParseLiveSliverGridWidget**: `offlineMode` parameter enables local caching
21+
- [x] **ParseLiveListPageView**: `offlineMode` parameter enables local caching
2222

2323
### Offline Features
2424
- [x] **Cache Configuration**: `cacheSize` parameter controls memory usage
2525
- [x] **Lazy Loading**: `lazyLoading` parameter loads data on-demand
2626
- [x] **Preloaded Columns**: `preloadedColumns` parameter specifies initial fields
27-
- [x] **Connectivity Detection**: Automatic detection of online/offline status
27+
- [x] **Connectivity Detection**: Automatic detection of online/offline status via mixin
2828
- [x] **Fallback to Cache**: Uses cached data when offline
2929

3030
## ✅ Code Quality
3131

32-
### Compilation
33-
- [x] No compilation errors
34-
- [x] Only harmless unused method warnings (4 total)
35-
- [x] No type errors or mismatches
32+
### Static Analysis
33+
- [x] `dart analyze` - No issues in dart package
34+
- [x] `flutter analyze` - No issues in flutter package
35+
- [x] Linting fixes applied (unnecessary brace in string interpolation)
36+
- [x] Removed unnecessary import
3637

37-
### Dependencies
38-
- [x] Git dependency correctly configured: `parse_server_sdk 8.1.0`
39-
- [x] Meta dependency compatible: `^1.16.0`
40-
- [x] All transitive dependencies resolved
41-
- [x] Compatible with Flutter test framework
38+
### Tests
39+
- [x] All 17 flutter package tests pass
40+
- [x] All 167 dart package tests pass
4241

4342
## ✅ New Widgets Documentation
4443

@@ -49,54 +48,37 @@
4948
- [x] Added Table of Contents with proper anchors
5049
- [x] Comprehensive offline caching method examples
5150
- [x] Configuration parameter documentation
51+
- [x] GlobalKey pattern for controlling sliver widgets
5252

5353
### Documented Widgets
54-
- [x] **ParseLiveList**: Traditional ListView widget example
55-
- [x] **ParseLiveSliverList**: Sliver-based list widget example
56-
- [x] **ParseLiveSliverGrid**: Sliver-based grid widget example
57-
- [x] **ParseLivePageView**: PageView widget example
58-
59-
### Documented Features
60-
- [x] Real-time updates via live query subscriptions
61-
- [x] Pagination support
62-
- [x] Lazy loading support
63-
- [x] Custom child builders
64-
- [x] Error handling and loading states
65-
- [x] Offline caching capabilities
66-
- [x] LRU memory management
54+
- [x] **ParseLiveListWidget**: Traditional ListView widget example
55+
- [x] **ParseLiveSliverListWidget**: Sliver-based list widget example with GlobalKey
56+
- [x] **ParseLiveSliverGridWidget**: Sliver-based grid widget example with GlobalKey
57+
- [x] **ParseLiveListPageView**: PageView widget example
58+
59+
### Public API Exposed
60+
- [x] `ParseLiveSliverListWidgetState` - Public state class for list control
61+
- [x] `ParseLiveSliverGridWidgetState` - Public state class for grid control
62+
- [x] `refreshData()` - Public method to refresh widget data
63+
- [x] `loadMoreData()` - Public method to load more data when paginated
64+
- [x] `hasMoreData` - Public getter for pagination status
65+
- [x] `loadMoreStatus` - Public getter for load more status
6766

6867
## ✅ File Status
6968

70-
### New Files
71-
- [x] `parse_live_sliver_list.dart` - Sliver list widget
72-
- [x] `parse_live_sliver_grid.dart` - Sliver grid widget
73-
- [x] `parse_live_page_view.dart` - PageView widget
74-
- [x] `parse_cached_live_list.dart` - LRU cache implementation
75-
- [x] `parse_offline_object.dart` (dart package) - Offline extension methods
69+
### New Files (Flutter Package)
70+
- [x] `lib/src/utils/parse_live_sliver_list.dart` - Sliver list widget
71+
- [x] `lib/src/utils/parse_live_sliver_grid.dart` - Sliver grid widget
72+
- [x] `lib/src/utils/parse_live_page_view.dart` - PageView widget
73+
- [x] `lib/src/utils/parse_cached_live_list.dart` - LRU cache implementation
74+
- [x] `lib/src/mixins/connectivity_handler_mixin.dart` - Connectivity handling mixin
75+
76+
### New Files (Dart Package)
77+
- [x] `lib/src/objects/parse_offline_object.dart` - Offline extension methods
7678

7779
### Modified Files
78-
- [x] `README.md` - Updated with comprehensive documentation
79-
- [x] `pubspec.yaml` (dart) - Fixed meta dependency version
80-
- [x] `parse_live_list.dart` - Enhanced with offline support
81-
82-
## ✅ Testing
83-
84-
### Offline Mode Tests
85-
- [x] Single object save/load
86-
- [x] Batch object save
87-
- [x] Load all objects
88-
- [x] Object existence check
89-
- [x] Object update in cache
90-
- [x] Get all object IDs
91-
- [x] Remove from cache
92-
- [x] Clear cache
93-
- [x] Sync with server
94-
95-
### Widget Tests
96-
- [x] All widgets compile without errors
97-
- [x] Offline mode parameter properly implemented
98-
- [x] Cache size parameter properly implemented
99-
- [x] Lazy loading parameter properly implemented
80+
- [x] `packages/flutter/README.md` - Updated with comprehensive documentation
81+
- [x] `packages/flutter/lib/parse_server_sdk_flutter.dart` - Exports new files
10082

10183
## 📋 Ready for Pull Request
10284

packages/dart/lib/src/objects/parse_offline_object.dart

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ extension ParseObjectOffline on ParseObject {
1919
/// Save this object to local storage (CoreStore) for offline access.
2020
Future<void> saveToLocalCache() async {
2121
final CoreStore coreStore = ParseCoreData().getStore();
22-
final String cacheKey = 'offline_cache_${parseClassName}';
22+
final String cacheKey = 'offline_cache_$parseClassName';
2323
final List<String> cached = await _getStringListAsStrings(coreStore, cacheKey);
2424
// Remove any existing object with the same objectId
2525
cached.removeWhere((s) {
@@ -81,7 +81,7 @@ extension ParseObjectOffline on ParseObject {
8181
/// Remove this object from local storage (CoreStore).
8282
Future<void> removeFromLocalCache() async {
8383
final CoreStore coreStore = ParseCoreData().getStore();
84-
final String cacheKey = 'offline_cache_${parseClassName}';
84+
final String cacheKey = 'offline_cache_$parseClassName';
8585
final List<String> cached = await _getStringListAsStrings(coreStore, cacheKey);
8686
cached.removeWhere((s) {
8787
final jsonObj = json.decode(s);
@@ -105,7 +105,7 @@ extension ParseObjectOffline on ParseObject {
105105

106106
Future<void> updateInLocalCache(Map<String, dynamic> updates) async {
107107
final CoreStore coreStore = ParseCoreData().getStore();
108-
final String cacheKey = 'offline_cache_${parseClassName}';
108+
final String cacheKey = 'offline_cache_$parseClassName';
109109
final List<String> cached = await _getStringListAsStrings(coreStore, cacheKey);
110110
for (int i = 0; i < cached.length; i++) {
111111
final jsonObj = json.decode(cached[i]);

packages/flutter/README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,35 @@ CustomScrollView(
156156
)
157157
```
158158

159+
#### Controlling Sliver Widgets with GlobalKey
160+
161+
For sliver widgets, you can use a `GlobalKey` to control refresh and pagination from a parent widget:
162+
163+
```dart
164+
final gridKey = GlobalKey<ParseLiveSliverGridWidgetState<MyObject>>();
165+
166+
// In your CustomScrollView
167+
ParseLiveSliverGridWidget<MyObject>(
168+
key: gridKey,
169+
query: query,
170+
pagination: true,
171+
offlineMode: true,
172+
fromJson: (json) => MyObject().fromJson(json),
173+
)
174+
175+
// To refresh
176+
gridKey.currentState?.refreshData();
177+
178+
// To load more (if pagination is enabled)
179+
gridKey.currentState?.loadMoreData();
180+
181+
// Access status
182+
final hasMore = gridKey.currentState?.hasMoreData ?? false;
183+
final status = gridKey.currentState?.loadMoreStatus;
184+
```
185+
186+
The same pattern works for `ParseLiveSliverListWidget` using `ParseLiveSliverListWidgetState`.
187+
159188
### ParseLivePageView
160189

161190
A PageView widget for swiping through Parse objects:

packages/flutter/lib/src/mixins/connectivity_handler_mixin.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import 'dart:async';
22
import 'package:connectivity_plus/connectivity_plus.dart';
3-
import 'package:flutter/foundation.dart';
4-
import 'package:flutter/material.dart'; // Needed for State
3+
import 'package:flutter/material.dart'; // Needed for State and debugPrint
54

65
/// Mixin to handle connectivity checks and state updates for Parse Live Widgets.
76
///

packages/flutter/lib/src/utils/parse_live_sliver_grid.dart

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,26 @@
11
part of 'package:parse_server_sdk_flutter/parse_server_sdk_flutter.dart';
22

33
/// A widget that displays a live sliver grid of Parse objects.
4+
///
5+
/// This widget is designed to be used inside a [CustomScrollView].
6+
/// To control refresh and pagination from a parent widget, use a [GlobalKey]:
7+
///
8+
/// ```dart
9+
/// final gridKey = GlobalKey<ParseLiveSliverGridWidgetState<MyObject>>();
10+
///
11+
/// // In your CustomScrollView
12+
/// ParseLiveSliverGridWidget<MyObject>(
13+
/// key: gridKey,
14+
/// query: query,
15+
/// fromJson: MyObject.fromJson,
16+
/// ),
17+
///
18+
/// // To refresh
19+
/// gridKey.currentState?.refreshData();
20+
///
21+
/// // To load more (if pagination is enabled)
22+
/// gridKey.currentState?.loadMoreData();
23+
/// ```
424
class ParseLiveSliverGridWidget<T extends sdk.ParseObject> extends StatefulWidget {
525
const ParseLiveSliverGridWidget({
626
super.key,
@@ -63,7 +83,7 @@ class ParseLiveSliverGridWidget<T extends sdk.ParseObject> extends StatefulWidge
6383
final T Function(Map<String, dynamic> json) fromJson;
6484

6585
@override
66-
State<ParseLiveSliverGridWidget<T>> createState() => _ParseLiveSliverGridWidgetState<T>();
86+
State<ParseLiveSliverGridWidget<T>> createState() => ParseLiveSliverGridWidgetState<T>();
6787

6888
static Widget defaultChildBuilder<T extends sdk.ParseObject>(
6989
BuildContext context, sdk.ParseLiveListElementSnapshot<T> snapshot, [int? index]) {
@@ -84,7 +104,11 @@ class ParseLiveSliverGridWidget<T extends sdk.ParseObject> extends StatefulWidge
84104
}
85105
}
86106

87-
class _ParseLiveSliverGridWidgetState<T extends sdk.ParseObject>
107+
/// State class for [ParseLiveSliverGridWidget].
108+
///
109+
/// Exposes [refreshData] and [loadMoreData] methods that can be called
110+
/// via a [GlobalKey] to control the widget from a parent.
111+
class ParseLiveSliverGridWidgetState<T extends sdk.ParseObject>
88112
extends State<ParseLiveSliverGridWidget<T>> with ConnectivityHandlerMixin<ParseLiveSliverGridWidget<T>> {
89113
CachedParseLiveList<T>? _liveGrid;
90114
final ValueNotifier<bool> _noDataNotifier = ValueNotifier<bool>(true);
@@ -96,6 +120,12 @@ class _ParseLiveSliverGridWidgetState<T extends sdk.ParseObject>
96120

97121
final Set<int> _loadingIndices = {}; // Used for lazy loading specific items
98122

123+
/// Whether more data can be loaded.
124+
bool get hasMoreData => _hasMoreData;
125+
126+
/// Current load more status.
127+
LoadMoreStatus get loadMoreStatus => _loadMoreStatus;
128+
99129
// --- Implement Mixin Requirements ---
100130
@override
101131
Future<void> loadDataFromServer() => _loadData();
@@ -183,7 +213,11 @@ class _ParseLiveSliverGridWidgetState<T extends sdk.ParseObject>
183213
}
184214
}
185215

186-
Future<void> _loadMoreData() async {
216+
/// Loads more data when pagination is enabled.
217+
///
218+
/// Call this method when the user scrolls near the end of the grid.
219+
/// Does nothing if offline, already loading, or no more data available.
220+
Future<void> loadMoreData() async {
187221
if (isOffline) {
188222
debugPrint('$connectivityLogPrefix Cannot load more data while offline.');
189223
return;
@@ -408,7 +442,11 @@ class _ParseLiveSliverGridWidgetState<T extends sdk.ParseObject>
408442
debugPrint('$connectivityLogPrefix Finished batch save processing in ${stopwatch.elapsedMilliseconds}ms.');
409443
}
410444

411-
Future<void> _refreshData() async {
445+
/// Refreshes the data by disposing the current live grid and reloading.
446+
///
447+
/// Use this method when implementing pull-to-refresh or manual refresh.
448+
/// Loads from cache if offline, otherwise from server.
449+
Future<void> refreshData() async {
412450
debugPrint('$connectivityLogPrefix Refreshing Grid data...');
413451
disposeLiveList();
414452

packages/flutter/lib/src/utils/parse_live_sliver_list.dart

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,26 @@
11
part of 'package:parse_server_sdk_flutter/parse_server_sdk_flutter.dart';
22

33
/// A widget that displays a live sliver list of Parse objects.
4+
///
5+
/// This widget is designed to be used inside a [CustomScrollView].
6+
/// To control refresh and pagination from a parent widget, use a [GlobalKey]:
7+
///
8+
/// ```dart
9+
/// final listKey = GlobalKey<ParseLiveSliverListWidgetState<MyObject>>();
10+
///
11+
/// // In your CustomScrollView
12+
/// ParseLiveSliverListWidget<MyObject>(
13+
/// key: listKey,
14+
/// query: query,
15+
/// fromJson: MyObject.fromJson,
16+
/// ),
17+
///
18+
/// // To refresh
19+
/// listKey.currentState?.refreshData();
20+
///
21+
/// // To load more (if pagination is enabled)
22+
/// listKey.currentState?.loadMoreData();
23+
/// ```
424
class ParseLiveSliverListWidget<T extends sdk.ParseObject> extends StatefulWidget {
525
const ParseLiveSliverListWidget({
626
super.key,
@@ -51,7 +71,7 @@ class ParseLiveSliverListWidget<T extends sdk.ParseObject> extends StatefulWidge
5171
final T Function(Map<String, dynamic> json) fromJson;
5272

5373
@override
54-
State<ParseLiveSliverListWidget<T>> createState() => _ParseLiveSliverListWidgetState<T>();
74+
State<ParseLiveSliverListWidget<T>> createState() => ParseLiveSliverListWidgetState<T>();
5575

5676
static Widget defaultChildBuilder<T extends sdk.ParseObject>(
5777
BuildContext context, sdk.ParseLiveListElementSnapshot<T> snapshot, [int? index]) {
@@ -72,7 +92,11 @@ class ParseLiveSliverListWidget<T extends sdk.ParseObject> extends StatefulWidge
7292
}
7393
}
7494

75-
class _ParseLiveSliverListWidgetState<T extends sdk.ParseObject>
95+
/// State class for [ParseLiveSliverListWidget].
96+
///
97+
/// Exposes [refreshData] and [loadMoreData] methods that can be called
98+
/// via a [GlobalKey] to control the widget from a parent.
99+
class ParseLiveSliverListWidgetState<T extends sdk.ParseObject>
76100
extends State<ParseLiveSliverListWidget<T>> with ConnectivityHandlerMixin<ParseLiveSliverListWidget<T>> {
77101
CachedParseLiveList<T>? _liveList;
78102
final ValueNotifier<bool> _noDataNotifier = ValueNotifier<bool>(true);
@@ -82,6 +106,12 @@ class _ParseLiveSliverListWidgetState<T extends sdk.ParseObject>
82106
int _currentPage = 0;
83107
bool _hasMoreData = true;
84108

109+
/// Whether more data can be loaded.
110+
bool get hasMoreData => _hasMoreData;
111+
112+
/// Current load more status.
113+
LoadMoreStatus get loadMoreStatus => _loadMoreStatus;
114+
85115
@override
86116
String get connectivityLogPrefix => 'ParseLiveSliverListWidget';
87117

@@ -357,7 +387,11 @@ class _ParseLiveSliverListWidgetState<T extends sdk.ParseObject>
357387
}
358388
}
359389

360-
Future<void> _loadMoreData() async {
390+
/// Loads more data when pagination is enabled.
391+
///
392+
/// Call this method when the user scrolls near the end of the list.
393+
/// Does nothing if offline, already loading, or no more data available.
394+
Future<void> loadMoreData() async {
361395
if (isOffline) {
362396
debugPrint('$connectivityLogPrefix Cannot load more data while offline.');
363397
return;
@@ -419,7 +453,11 @@ class _ParseLiveSliverListWidgetState<T extends sdk.ParseObject>
419453
}
420454
}
421455

422-
Future<void> _refreshData() async {
456+
/// Refreshes the data by disposing the current live list and reloading.
457+
///
458+
/// Use this method when implementing pull-to-refresh or manual refresh.
459+
/// Loads from cache if offline, otherwise from server.
460+
Future<void> refreshData() async {
423461
debugPrint('$connectivityLogPrefix Refreshing data...');
424462
disposeLiveList();
425463

0 commit comments

Comments
 (0)