diff --git a/packages/dart/lib/src/utils/parse_live_list.dart b/packages/dart/lib/src/utils/parse_live_list.dart index db4e3a1cc..fd29f9235 100644 --- a/packages/dart/lib/src/utils/parse_live_list.dart +++ b/packages/dart/lib/src/utils/parse_live_list.dart @@ -2,34 +2,47 @@ part of flutter_parse_sdk; // ignore_for_file: invalid_use_of_protected_member class ParseLiveList { - ParseLiveList._(this._query, this._listeningIncludes, this._lazyLoading) { + ParseLiveList._(this._query, this._listeningIncludes, this._lazyLoading, + {List preloadedColumns = const []}) + : _preloadedColumns = preloadedColumns { _debug = isDebugEnabled(); } static Future> create( - QueryBuilder _query, - {bool listenOnAllSubItems, - List listeningIncludes, - bool lazyLoading = true}) { + QueryBuilder _query, { + bool listenOnAllSubItems, + List listeningIncludes, + bool lazyLoading = true, + List preloadedColumns, + }) { final ParseLiveList parseLiveList = ParseLiveList._( - _query, - listenOnAllSubItems == true - ? _toIncludeMap( - _query.limiters['include']?.toString()?.split(',') ?? - []) - : _toIncludeMap(listeningIncludes ?? []), - lazyLoading); + _query, + listenOnAllSubItems == true + ? _toIncludeMap( + _query.limiters['include']?.toString()?.split(',') ?? []) + : _toIncludeMap(listeningIncludes ?? []), + lazyLoading, + preloadedColumns: preloadedColumns, + ); return parseLiveList._init().then((_) { return parseLiveList; }); } + final QueryBuilder _query; + //The included Items, where LiveList should look for updates. + final Map _listeningIncludes; + final bool _lazyLoading; + final List _preloadedColumns; + List> _list = List>(); StreamController> _eventStreamController; int _nextID = 0; bool _debug; + int get nextID => _nextID++; + /// is object1 listed after object2? /// can return null bool after(T object1, T object2) { @@ -84,14 +97,6 @@ class ParseLiveList { return null; } - int get nextID => _nextID++; - - final QueryBuilder _query; - //The included Items, where LiveList should look for updates. - final Map _listeningIncludes; - - final bool _lazyLoading; - int get size { return _list.length; } @@ -128,17 +133,17 @@ class ParseLiveList { if (_debug) print('ParseLiveList: lazyLoading is ${_lazyLoading ? 'on' : 'off'}'); if (_lazyLoading) { - if (query.limiters.containsKey('order')) { - query.keysToReturn( - query.limiters['order'].toString().split(',').map((String string) { - if (string.startsWith('-')) { - return string.substring(1); - } - return string; - }).toList()); - } else { - query.keysToReturn(List()); - } + final List keys = _preloadedColumns?.toList() ?? []; + if (_lazyLoading && query.limiters.containsKey('order')) + keys.addAll( + query.limiters['order'].toString().split(',').map((String string) { + if (string.startsWith('-')) { + return string.substring(1); + } + return string; + }), + ); + query.keysToReturn(keys); } return await query.query(); } @@ -449,6 +454,13 @@ class ParseLiveList { return null; } + T getPreLoadedAt(int index) { + if (index < _list.length) { + return _list[index].object; + } + return null; + } + void dispose() { if (_liveQuerySubscription != null) { LiveQuery().client.unSubscribe(_liveQuerySubscription); @@ -739,12 +751,17 @@ typedef StreamGetter = Stream Function(); typedef DataGetter = T Function(); class ParseLiveListElementSnapshot { - ParseLiveListElementSnapshot({this.loadedData, this.error}); + ParseLiveListElementSnapshot( + {this.loadedData, this.error, this.preLoadedData}); final T loadedData; + final T preLoadedData; + final ParseError error; bool get hasData => loadedData != null; + bool get hasPreLoadedData => preLoadedData != null; + bool get failed => error != null; } diff --git a/packages/flutter/README.md b/packages/flutter/README.md index 03e2094c5..7fd06cba4 100644 --- a/packages/flutter/README.md +++ b/packages/flutter/README.md @@ -597,6 +597,38 @@ To activate listening for updates on all included objects, add `listenOnAllSubIt If you want ParseLiveList to listen for updates on only some sub-objects, use `listeningIncludes: const [/*all the included sub-objects*/]` instead. Just as QueryBuilder, ParseLiveList supports nested sub-objects too. +### Lazy loading +By default, ParseLiveList lazy loads the content. +You can avoid that by setting `lazyLoading: false`. +In case you want to use lazyLoading but you need some columns to be preloaded, you can provide a list of `preloadedColumns`. +Preloading fields of a pointer is supported by using the dot-notation. +You can access the preloaded data is stored in the `preLoadedData` field of the `ParseLiveListElementSnapshot`. +```dart +ParseLiveListWidget( + query: query, + lazyLoading: true, + preloadedColumns: ["test1", "sender.username"], + childBuilder: + (BuildContext context, ParseLiveListElementSnapshot snapshot) { + if (snapshot.failed) { + return const Text('something went wrong!'); + } else if (snapshot.hasData) { + return ListTile( + title: Text( + snapshot.loadedData.get("text"), + ), + ); + } else { + return ListTile( + title: Text( + "loading comment from: ${snapshot.preLoadedData?.get("sender")?.get("username")}", + ), + ); + } + }, +); +``` + **NOTE:** To use this features you have to enable [Live Queries](#live-queries) first. ## Users diff --git a/packages/flutter/lib/src/utils/parse_live_list.dart b/packages/flutter/lib/src/utils/parse_live_list.dart index a4674dac5..d80efed77 100644 --- a/packages/flutter/lib/src/utils/parse_live_list.dart +++ b/packages/flutter/lib/src/utils/parse_live_list.dart @@ -21,6 +21,7 @@ class ParseLiveListWidget extends StatefulWidget { this.listenOnAllSubItems, this.listeningIncludes, this.lazyLoading = true, + this.preloadedColumns, }) : super(key: key); final sdk.QueryBuilder query; @@ -42,6 +43,7 @@ class ParseLiveListWidget extends StatefulWidget { final List listeningIncludes; final bool lazyLoading; + final List preloadedColumns; @override _ParseLiveListWidgetState createState() => _ParseLiveListWidgetState( @@ -50,6 +52,7 @@ class ParseLiveListWidget extends StatefulWidget { listenOnAllSubItems: listenOnAllSubItems, listeningIncludes: listeningIncludes, lazyLoading: lazyLoading, + preloadedColumns: preloadedColumns, ); static Widget defaultChildBuilder( @@ -79,12 +82,14 @@ class _ParseLiveListWidgetState @required this.removedItemBuilder, bool listenOnAllSubItems, List listeningIncludes, - bool lazyLoading = true}) { + bool lazyLoading = true, + List preloadedColumns}) { sdk.ParseLiveList.create( query, listenOnAllSubItems: listenOnAllSubItems, listeningIncludes: listeningIncludes, lazyLoading: lazyLoading, + preloadedColumns: preloadedColumns, ).then((sdk.ParseLiveList value) { setState(() { _liveList = value; @@ -107,6 +112,7 @@ class _ParseLiveListWidgetState sizeFactor: animation, duration: widget.duration, loadedData: () => event.object, + preLoadedData: () => event.object, ), duration: widget.duration); } @@ -146,6 +152,7 @@ class _ParseLiveListWidgetState _liveList?.getIdentifier(index) ?? '_NotFound'), stream: () => _liveList?.getAt(index), loadedData: () => _liveList?.getLoadedAt(index), + preLoadedData: () => _liveList?.getPreLoadedAt(index), sizeFactor: animation, duration: widget.duration, childBuilder: @@ -168,6 +175,7 @@ class ParseLiveListElementWidget {Key key, this.stream, this.loadedData, + this.preLoadedData, @required this.sizeFactor, @required this.duration, @required this.childBuilder}) @@ -175,32 +183,36 @@ class ParseLiveListElementWidget final sdk.StreamGetter stream; final sdk.DataGetter loadedData; + final sdk.DataGetter preLoadedData; final Animation sizeFactor; final Duration duration; final ChildBuilder childBuilder; @override _ParseLiveListElementWidgetState createState() { - return _ParseLiveListElementWidgetState(loadedData, stream); + return _ParseLiveListElementWidgetState( + loadedData, preLoadedData, stream); } } class _ParseLiveListElementWidgetState extends State> with SingleTickerProviderStateMixin { - _ParseLiveListElementWidgetState( - sdk.DataGetter loadedDataGetter, sdk.StreamGetter stream) { - _snapshot = - sdk.ParseLiveListElementSnapshot(loadedData: loadedDataGetter()); + _ParseLiveListElementWidgetState(sdk.DataGetter loadedDataGetter, + sdk.DataGetter preLoadedDataGetter, sdk.StreamGetter stream) { + _snapshot = sdk.ParseLiveListElementSnapshot( + loadedData: loadedDataGetter(), preLoadedData: preLoadedDataGetter()); if (stream != null) { _streamSubscription = stream().listen( (T data) { if (widget != null) { setState(() { - _snapshot = sdk.ParseLiveListElementSnapshot(loadedData: data); + _snapshot = sdk.ParseLiveListElementSnapshot( + loadedData: data, preLoadedData: data); }); } else { - _snapshot = sdk.ParseLiveListElementSnapshot(loadedData: data); + _snapshot = sdk.ParseLiveListElementSnapshot( + loadedData: data, preLoadedData: data); } }, onError: (Object error) {