-
-
Notifications
You must be signed in to change notification settings - Fork 214
feat: Add offline mode support for ParseLive and new live query widgets #1101
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
pastordee
wants to merge
100
commits into
parse-community:master
Choose a base branch
from
pastordee:master
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 92 commits
Commits
Show all changes
100 commits
Select commit
Hold shift + click to select a range
44c584c
update pubspec
pastordee 311a2ef
Added Optional Index
pastordee d7cec0f
Update parse_live_list.dart
pastordee 8768bb3
Parse Live List PageView
pastordee 2b22a2e
use Last
pastordee 9d58cc1
Update parse_live_page_view.dart
pastordee bb500f2
Update pubspec.yaml
pastordee edddb2e
Added Pagination to ParseLiveListWidget and ParseLiveGridWidget
pastordee 0c03f6f
Update parse_live_list.dart
pastordee 44b601a
Update parse_live_list.dart
pastordee e573e8d
Update parse_live_list.dart
pastordee 6e9256a
useAnimations
pastordee 6572007
added cached
pastordee e61f294
Update parse_server_sdk_flutter.dart
pastordee f355032
Update parse_cached_live_list.dart
pastordee eec1a7b
Update parse_live_grid.dart
pastordee e4b73d2
Update parse_live_grid.dart
pastordee 5162d46
Adding CachedParseLiveList to ParseLiveListPageView
pastordee f170d94
ok
pastordee 97e3193
Update pubspec.yaml
pastordee 81ce90d
ok
pastordee 45fc594
LazyLoading in CachedParseLiveList
pastordee e014aaf
CachedParseLiveList
pastordee 2410adb
ok
pastordee 827351f
Fixing Lazy Loading
pastordee af6c282
offline mode
pastordee 6635518
added ParseObjectOffline
pastordee e5ad704
ParseObjectOffline
pastordee c60801e
Update parse_live_list.dart
pastordee ed8ef30
Update parse_offline_object.dart
pastordee 4039201
Update pubspec.yaml
pastordee e1693e9
Update pubspec.yaml
pastordee 83e88c9
Update parse_offline_object.dart
pastordee c6dec80
ok
pastordee 7705f0c
Update pubspec.yaml
pastordee 857bfa9
Update core_store_memory.dart
pastordee e4cbaaf
Update core_store_memory.dart
pastordee bcbb4b1
Update pubspec.yaml
pastordee bef3058
Update pubspec.yaml
pastordee 34aa3fd
ok
pastordee 2631004
offlineMode
pastordee 2571ad3
Use the factory in _loadFromCache:
pastordee 4b4055c
ConnectivityHandlerMixin
pastordee 7257b4c
Update parse_live_list.dart
pastordee 1686c81
Update parse_live_grid.dart
pastordee a771fe7
proactivelyCacheNextPage
pastordee 3719f0a
ParseLiveSliverListWidget
pastordee f0c5edc
Update parse_live_sliver_list.dart
pastordee db74d47
ParseLiveSliverGridWidget
pastordee 5c1059b
Parse Dashboard Analytics Integration
pastordee a7d111c
ok
pastordee 0fd55d0
Refactor analytics integration and remove clean files
pastordee 3406e46
Update pubspec.yaml
pastordee d58166f
Update parse_offline_object.dart
pastordee c27d40f
Update dependencies and improve live list handling
pastordee aac8ab1
Remove trailing whitespace in pubspec.yaml files
pastordee b92b33c
Add offline extension tests and update Dart SDK version
pastordee 69f12da
Update pubspec.yaml
pastordee c1ca15e
Fix meta dependency compatibility and use path dependency for local d…
pastordee de1b270
Update Parse dependencies in pubspec.yaml files
pastordee 4b25476
Update Parse SDK source and remove connectivity_plus plugin
pastordee a7f080d
Merge remote-tracking branch 'upstream/master'
pastordee 436a1dc
Merge remote-tracking branch 'upstream/master' into pc_server
pastordee ef4b8ed
Merge branch 'pc_server'
pastordee 1c3b57b
Update dependencies and clean up pubspec files
pastordee 287f94f
Merge remote-tracking branch 'upstream/master' into pc_server
pastordee b16529b
Replace parse_server_sdk_flutter with parse_server_sdk imports
pastordee afa779d
Merge branch 'pc_server'
pastordee 182c05b
Switch to parse_server_sdk_flutter and update dependencies
pastordee e266199
Switch to local parse_server_sdk_flutter package
pastordee 24ee249
Merge remote-tracking branch 'upstream/master' into pc_server
pastordee e5d912a
Merge branch 'pc_server'
pastordee cbd4c04
Merge remote-tracking branch 'upstream/master' into pc_server
pastordee 15919de
Merge branch 'pc_server'
pastordee 117c560
Update pubspec.yaml
pastordee 73aef09
Update pubspec.yaml
pastordee e154603
Merge remote-tracking branch 'upstream/master' into pc_server
pastordee c07d849
Merge branch 'pc_server'
pastordee 508318f
Merge remote-tracking branch 'upstream/master' into pc_server
pastordee af2dc38
Remove ethernet connectivity support
pastordee d5ca699
Merge branch 'pc_server'
pastordee bcbdc2c
Update pubspec.yaml
pastordee 4459d53
Update pubspec.yaml
pastordee 3b84021
Merge remote-tracking branch 'upstream/master' into pc_server
pastordee 5887abf
Merge branch 'pc_server'
pastordee 730d651
Merge remote-tracking branch 'upstream/master' into pc_server
pastordee b9f5513
Merge branch 'pc_server'
pastordee 3bbcd99
Add offline mode tests and update documentation
pastordee c2ff6d8
Merge branch 'pc_server'
pastordee 62e80b7
Expose public state for sliver widgets and improve docs
pastordee 609289a
fix: resolve linting issues in example app
pastordee da21fc8
fix: resolve linting issues in example app
pastordee 5a14b1b
fix: resolve linting issues in example app
pastordee 3550eb0
style: format dart files
pastordee c1abf93
fix: use local dart package override and remove sdk prefix from Parse…
pastordee 17a4262
style: run dart format on flutter package
pastordee 1df6083
chore: remove accidentally added screenshot
pastordee 38a89c5
fix: add curly braces to if statements for lint compliance
pastordee 09bcd4b
test: add unit tests for offline mode, analytics, connectivity mixin,…
pastordee 57342bd
test: Add tests for improved coverage
pastordee File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,117 @@ | ||
| # Pre-Pull Request Checklist for Parse SDK Flutter | ||
|
|
||
| ## ✅ Offline Mode Verification | ||
|
|
||
| ### Core Offline Functionality | ||
| - [x] **Single Object Caching**: `saveToLocalCache()` works correctly | ||
| - [x] **Load Single Object**: `loadFromLocalCache()` retrieves cached objects | ||
| - [x] **Batch Save**: `saveAllToLocalCache()` efficiently saves multiple objects | ||
| - [x] **Load All Objects**: `loadAllFromLocalCache()` retrieves all cached objects | ||
| - [x] **Object Existence Check**: `existsInLocalCache()` correctly identifies cached objects | ||
| - [x] **Update in Cache**: `updateInLocalCache()` modifies cached objects | ||
| - [x] **Get Object IDs**: `getAllObjectIdsInLocalCache()` retrieves all IDs | ||
| - [x] **Remove from Cache**: `removeFromLocalCache()` removes objects correctly | ||
| - [x] **Clear Cache**: `clearLocalCacheForClass()` clears all objects of a class | ||
| - [x] **Sync with Server**: `syncLocalCacheWithServer()` syncs data to server | ||
|
|
||
| ### Widget Offline Support | ||
| - [x] **ParseLiveListWidget**: `offlineMode` parameter enables local caching | ||
| - [x] **ParseLiveSliverListWidget**: `offlineMode` parameter enables local caching | ||
| - [x] **ParseLiveSliverGridWidget**: `offlineMode` parameter enables local caching | ||
| - [x] **ParseLiveListPageView**: `offlineMode` parameter enables local caching | ||
|
|
||
| ### Offline Features | ||
| - [x] **Cache Configuration**: `cacheSize` parameter controls memory usage | ||
| - [x] **Lazy Loading**: `lazyLoading` parameter loads data on-demand | ||
| - [x] **Preloaded Columns**: `preloadedColumns` parameter specifies initial fields | ||
| - [x] **Connectivity Detection**: Automatic detection of online/offline status via mixin | ||
| - [x] **Fallback to Cache**: Uses cached data when offline | ||
|
|
||
| ## ✅ Code Quality | ||
|
|
||
| ### Static Analysis | ||
| - [x] `dart analyze` - No issues in dart package | ||
| - [x] `flutter analyze` - No issues in flutter package | ||
| - [x] Linting fixes applied (unnecessary brace in string interpolation) | ||
| - [x] Removed unnecessary import | ||
|
|
||
| ### Tests | ||
| - [x] All 17 flutter package tests pass | ||
| - [x] All 167 dart package tests pass | ||
|
|
||
| ## ✅ New Widgets Documentation | ||
|
|
||
| ### README Updates | ||
| - [x] Added "Features" section with Live Queries and Offline Support | ||
| - [x] Added "Usage" section with examples for all 4 live widgets | ||
| - [x] Added "Offline Mode" section with API documentation | ||
| - [x] Added Table of Contents with proper anchors | ||
| - [x] Comprehensive offline caching method examples | ||
| - [x] Configuration parameter documentation | ||
| - [x] GlobalKey pattern for controlling sliver widgets | ||
|
|
||
| ### Documented Widgets | ||
| - [x] **ParseLiveListWidget**: Traditional ListView widget example | ||
| - [x] **ParseLiveSliverListWidget**: Sliver-based list widget example with GlobalKey | ||
| - [x] **ParseLiveSliverGridWidget**: Sliver-based grid widget example with GlobalKey | ||
| - [x] **ParseLiveListPageView**: PageView widget example | ||
|
|
||
| ### Public API Exposed | ||
| - [x] `ParseLiveSliverListWidgetState` - Public state class for list control | ||
| - [x] `ParseLiveSliverGridWidgetState` - Public state class for grid control | ||
| - [x] `refreshData()` - Public method to refresh widget data | ||
| - [x] `loadMoreData()` - Public method to load more data when paginated | ||
| - [x] `hasMoreData` - Public getter for pagination status | ||
| - [x] `loadMoreStatus` - Public getter for load more status | ||
|
|
||
| ## ✅ File Status | ||
|
|
||
| ### New Files (Flutter Package) | ||
| - [x] `lib/src/utils/parse_live_sliver_list.dart` - Sliver list widget | ||
| - [x] `lib/src/utils/parse_live_sliver_grid.dart` - Sliver grid widget | ||
| - [x] `lib/src/utils/parse_live_page_view.dart` - PageView widget | ||
| - [x] `lib/src/utils/parse_cached_live_list.dart` - LRU cache implementation | ||
| - [x] `lib/src/mixins/connectivity_handler_mixin.dart` - Connectivity handling mixin | ||
|
|
||
| ### New Files (Dart Package) | ||
| - [x] `lib/src/objects/parse_offline_object.dart` - Offline extension methods | ||
|
|
||
| ### Modified Files | ||
| - [x] `packages/flutter/README.md` - Updated with comprehensive documentation | ||
| - [x] `packages/flutter/lib/parse_server_sdk_flutter.dart` - Exports new files | ||
|
|
||
| ## 📋 Ready for Pull Request | ||
|
|
||
| This implementation is ready for submission with the following features: | ||
|
|
||
| ### New Capabilities | ||
| 1. **Three New Live Query Widgets**: ParseLiveSliverList, ParseLiveSliverGrid, ParseLivePageView | ||
| 2. **Comprehensive Offline Support**: Full caching system with LRU memory management | ||
| 3. **Connectivity Aware**: Automatic fallback to cached data when offline | ||
| 4. **Performance Optimized**: Batch operations and lazy loading support | ||
| 5. **Well Documented**: Complete README with examples for all features | ||
|
|
||
| ### Breaking Changes | ||
| - None | ||
|
|
||
| ### Deprecations | ||
| - None | ||
|
|
||
| ### Migration Required | ||
| - No breaking changes, fully backward compatible | ||
|
|
||
| ## 🚀 Deployment Notes | ||
|
|
||
| For users adopting this version: | ||
|
|
||
| 1. **Optional Offline Mode**: Set `offlineMode: true` on live widgets to enable caching | ||
| 2. **No Required Changes**: Existing code continues to work without modification | ||
| 3. **New Widgets**: Can be used alongside existing ParseLiveList | ||
| 4. **Manual Caching**: Advanced users can use ParseObjectOffline extension methods directly | ||
|
|
||
| --- | ||
|
|
||
| **Status**: ✅ READY FOR PULL REQUEST | ||
| **Version**: 10.2.0+ | ||
| **Breaking Changes**: None | ||
| **New Dependencies**: None |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
165 changes: 165 additions & 0 deletions
165
packages/dart/lib/src/objects/parse_offline_object.dart
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,165 @@ | ||
| part of '../../parse_server_sdk.dart'; | ||
|
|
||
| extension ParseObjectOffline on ParseObject { | ||
| /// Load a single object by objectId from local storage. | ||
| static Future<ParseObject?> loadFromLocalCache(String className, String objectId) async { | ||
| final CoreStore coreStore = ParseCoreData().getStore(); | ||
| final String cacheKey = 'offline_cache_$className'; | ||
| final List<String> cached = await _getStringListAsStrings(coreStore, cacheKey); | ||
| for (final s in cached) { | ||
| final jsonObj = json.decode(s); | ||
| if (jsonObj['objectId'] == objectId) { | ||
| print('Loaded object $objectId from local cache for $className'); | ||
| return ParseObject(className).fromJson(jsonObj); | ||
| } | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| /// Save this object to local storage (CoreStore) for offline access. | ||
| Future<void> saveToLocalCache() async { | ||
| final CoreStore coreStore = ParseCoreData().getStore(); | ||
| final String cacheKey = 'offline_cache_$parseClassName'; | ||
| final List<String> cached = await _getStringListAsStrings(coreStore, cacheKey); | ||
| // Remove any existing object with the same objectId | ||
| cached.removeWhere((s) { | ||
| final jsonObj = json.decode(s); | ||
| return jsonObj['objectId'] == objectId; | ||
| }); | ||
| cached.add(json.encode(toJson(full: true))); | ||
| await coreStore.setStringList(cacheKey, cached); | ||
| print('Saved object ${objectId ?? "(no objectId)"} to local cache for $parseClassName'); | ||
| } | ||
|
|
||
| /// Save a list of objects to local storage efficiently. | ||
| static Future<void> saveAllToLocalCache(String className, List<ParseObject> objectsToSave) async { | ||
| if (objectsToSave.isEmpty) return; | ||
|
|
||
| final CoreStore coreStore = ParseCoreData().getStore(); | ||
| final String cacheKey = 'offline_cache_$className'; | ||
| final List<String> cachedStrings = await _getStringListAsStrings(coreStore, cacheKey); | ||
|
|
||
| // Use a Map for efficient lookup and update of existing objects | ||
| final Map<String, String> objectMap = {}; | ||
| for (final s in cachedStrings) { | ||
| try { | ||
| final jsonObj = json.decode(s); | ||
| final objectId = jsonObj['objectId'] as String?; | ||
| if (objectId != null) { | ||
| objectMap[objectId] = s; // Store the original JSON string | ||
| } | ||
| } catch (e) { | ||
| print('Error decoding cached object string during batch save: $e'); | ||
| } | ||
| } | ||
|
|
||
| int added = 0; | ||
| int updated = 0; | ||
|
|
||
| // Update the map with the new objects | ||
| for (final obj in objectsToSave) { | ||
| final objectId = obj.objectId; | ||
| if (objectId != null) { | ||
| if (objectMap.containsKey(objectId)) { | ||
| updated++; | ||
| } else { | ||
| added++; | ||
| } | ||
| // Encode the new object data and replace/add it in the map | ||
| objectMap[objectId] = json.encode(obj.toJson(full: true)); | ||
| } else { | ||
| print('Skipping object without objectId during batch save for $className'); | ||
| } | ||
| } | ||
|
|
||
| // Convert the map values back to a list and save | ||
| final List<String> updatedCachedStrings = objectMap.values.toList(); | ||
| await coreStore.setStringList(cacheKey, updatedCachedStrings); | ||
| print('Batch saved to local cache for $className. Added: $added, Updated: $updated, Total: ${updatedCachedStrings.length}'); | ||
| } | ||
|
|
||
| /// Remove this object from local storage (CoreStore). | ||
| Future<void> removeFromLocalCache() async { | ||
| final CoreStore coreStore = ParseCoreData().getStore(); | ||
| final String cacheKey = 'offline_cache_$parseClassName'; | ||
| final List<String> cached = await _getStringListAsStrings(coreStore, cacheKey); | ||
| cached.removeWhere((s) { | ||
| final jsonObj = json.decode(s); | ||
| return jsonObj['objectId'] == objectId; | ||
| }); | ||
| await coreStore.setStringList(cacheKey, cached); | ||
| print('Removed object ${objectId ?? "(no objectId)"} from local cache for $parseClassName'); | ||
| } | ||
|
|
||
| /// Load all objects of this class from local storage. | ||
| static Future<List<ParseObject>> loadAllFromLocalCache(String className) async { | ||
| final CoreStore coreStore = ParseCoreData().getStore(); | ||
| final String cacheKey = 'offline_cache_$className'; | ||
| final List<String> cached = await _getStringListAsStrings(coreStore, cacheKey); | ||
| print('Loaded ${cached.length} objects from local cache for $className'); | ||
| return cached.map<ParseObject>((s) { | ||
| final jsonObj = json.decode(s); | ||
| return ParseObject(className).fromJson(jsonObj); | ||
| }).toList(); | ||
| } | ||
|
|
||
| Future<void> updateInLocalCache(Map<String, dynamic> updates) async { | ||
| final CoreStore coreStore = ParseCoreData().getStore(); | ||
| final String cacheKey = 'offline_cache_$parseClassName'; | ||
| final List<String> cached = await _getStringListAsStrings(coreStore, cacheKey); | ||
| for (int i = 0; i < cached.length; i++) { | ||
| final jsonObj = json.decode(cached[i]); | ||
| if (jsonObj['objectId'] == objectId) { | ||
| jsonObj.addAll(updates); | ||
| cached[i] = json.encode(jsonObj); | ||
| break; | ||
| } | ||
| } | ||
| await coreStore.setStringList(cacheKey, cached); | ||
| print('Updated object ${objectId ?? "(no objectId)"} in local cache for $parseClassName'); | ||
| } | ||
|
|
||
| static Future<void> clearLocalCacheForClass(String className) async { | ||
| final CoreStore coreStore = ParseCoreData().getStore(); | ||
| final String cacheKey = 'offline_cache_$className'; | ||
| await coreStore.setStringList(cacheKey, []); | ||
| print('Cleared local cache for $className'); | ||
| } | ||
|
|
||
| static Future<bool> existsInLocalCache(String className, String objectId) async { | ||
| final CoreStore coreStore = ParseCoreData().getStore(); | ||
| final String cacheKey = 'offline_cache_$className'; | ||
| final List<String> cached = await _getStringListAsStrings(coreStore, cacheKey); | ||
| for (final s in cached) { | ||
| final jsonObj = json.decode(s); | ||
| if (jsonObj['objectId'] == objectId) { | ||
| print('Object $objectId exists in local cache for $className'); | ||
| return true; | ||
| } | ||
| } | ||
| print('Object $objectId does not exist in local cache for $className'); | ||
| return false; | ||
| } | ||
|
|
||
| static Future<List<String>> getAllObjectIdsInLocalCache(String className) async { | ||
| final CoreStore coreStore = ParseCoreData().getStore(); | ||
| final String cacheKey = 'offline_cache_$className'; | ||
| final List<String> cached = await _getStringListAsStrings(coreStore, cacheKey); | ||
| print('Fetched all objectIds from local cache for $className'); | ||
| return cached.map((s) => json.decode(s)['objectId'] as String).toList(); | ||
| } | ||
|
|
||
| static Future<void> syncLocalCacheWithServer(String className) async { | ||
| final objects = await loadAllFromLocalCache(className); | ||
| for (final obj in objects) { | ||
| await obj.save(); | ||
| } | ||
| print('Synced local cache with server for $className'); | ||
| } | ||
|
|
||
| static Future<List<String>> _getStringListAsStrings(CoreStore coreStore, String cacheKey) async { | ||
| final rawList = await coreStore.getStringList(cacheKey); | ||
| if (rawList == null) return []; | ||
| return List<String>.from(rawList.map((e) => e.toString())); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| import 'package:parse_server_sdk/parse_server_sdk.dart'; | ||
|
|
||
| Future<void> main() async { | ||
| // Initialize Parse | ||
| await Parse().initialize("keyApplicationId", "keyParseServerUrl", | ||
| clientKey: "keyParseClientKey", | ||
| debug: true, | ||
| autoSendSessionId: true, | ||
| coreStore: CoreStoreMemoryImp()); | ||
|
|
||
| // Test if ParseObjectOffline extension is available | ||
| var dietPlan = ParseObject('DietPlan') | ||
| ..set('Name', 'Test') | ||
| ..set('Fat', 50); | ||
|
|
||
| try { | ||
| // Test static method from extension | ||
| var cachedObjects = await ParseObjectOffline.loadAllFromLocalCache('DietPlan'); | ||
| print('Extension static method works! Found ${cachedObjects.length} cached objects'); | ||
|
|
||
| // Test instance method from extension | ||
| await dietPlan.saveToLocalCache(); | ||
| print('Extension instance method works! Saved object to cache'); | ||
|
|
||
| } catch (e) { | ||
| print('Extension methods not available: $e'); | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
publish_to: noneshould be removed before merging. This prevents the dart package from being published to pub.dev.