diff --git a/example_livelist/lib/application_constants.dart b/example_livelist/lib/application_constants.dart index 472dce179..73292218e 100644 --- a/example_livelist/lib/application_constants.dart +++ b/example_livelist/lib/application_constants.dart @@ -1,6 +1,6 @@ const String keyApplicationName = ''; const String keyParseApplicationId = ''; -const String keyParseMasterKey = ''; +const String keyParseClientKey = ''; const String keyParseServerUrl = ''; const String keyParseLiveServerUrl = ''; const bool keyDebug = true; diff --git a/example_livelist/lib/main.dart b/example_livelist/lib/main.dart index 546c3dfe9..1c07d3639 100644 --- a/example_livelist/lib/main.dart +++ b/example_livelist/lib/main.dart @@ -23,8 +23,8 @@ class _MyAppState extends State { initFailed = !success; if (success) _queryBuilder = QueryBuilder(ParseObject('Test')) - ..orderByAscending('order'); - ; + ..orderByAscending('order') + ..whereNotEqualTo('show', false); }); }).catchError((dynamic _) { setState(() { @@ -80,23 +80,24 @@ class _MyAppState extends State { child: ParseLiveListWidget( query: _queryBuilder, duration: const Duration(seconds: 1), - childBuilder: - (BuildContext context, bool failed, ParseObject loadedData) { - if (failed) { + childBuilder: (BuildContext context, + ParseLiveListElementSnapshot snapshot) { + if (snapshot.failed) { return const Text('something went wrong!'); - } else if (loadedData != null) { + } else if (snapshot.hasData) { return ListTile( title: Row( children: [ Flexible( - child: Text(loadedData.get('order').toString()), + child: Text( + snapshot.loadedData.get('order').toString()), flex: 1, ), Flexible( child: Container( alignment: Alignment.center, child: Text( - loadedData.get('text'), + snapshot.loadedData.get('text'), ), ), flex: 10, @@ -104,7 +105,7 @@ class _MyAppState extends State { ], ), onLongPress: () { - objectFormKey.currentState.setObject(loadedData); + objectFormKey.currentState.setObject(snapshot.loadedData); }, ); } else { @@ -179,6 +180,7 @@ class _ObjectFormState extends State { setState(() { _formKey.currentState.save(); final ParseObject object = _currentObject; + //Delay to highlight the animation. Future.delayed(const Duration(seconds: 1)) .then((_) { object.save(); diff --git a/lib/src/utils/parse_live_list.dart b/lib/src/utils/parse_live_list.dart index cab7187d2..9f4dd1955 100644 --- a/lib/src/utils/parse_live_list.dart +++ b/lib/src/utils/parse_live_list.dart @@ -160,27 +160,28 @@ class ParseLiveList { for (int i = 0; i < _list.length; i++) { if (after(object, _list[i].object) != true) { _list.insert(i, ParseLiveListElement(object, loaded: loaded)); - _eventStreamController.sink.add(ParseLiveListAddEvent(i, object)); + _eventStreamController.sink.add(ParseLiveListAddEvent( + i, object?.clone(object?.toJson(full: true)))); return; } } _list.add(ParseLiveListElement(object, loaded: loaded)); - _eventStreamController.sink - .add(ParseLiveListAddEvent(_list.length - 1, object)); + _eventStreamController.sink.add(ParseLiveListAddEvent( + _list.length - 1, object?.clone(object?.toJson(full: true)))); } void _objectUpdated(T object) { for (int i = 0; i < _list.length; i++) { if (_list[i].object.get(keyVarObjectId) == object.get(keyVarObjectId)) { - //TODO: better soulution -// if (after(_list[i].object, object) == null) { -// _list[i].object = object; -// } else { - _list.removeAt(i).dispose(); - _eventStreamController.sink.add(ParseLiveListDeleteEvent(i, object)); - _objectAdded(object); -// } + if (after(_list[i].object, object) == null) { + _list[i].object = object; + } else { + _list.removeAt(i).dispose(); + _eventStreamController.sink.add(ParseLiveListDeleteEvent( + i, object?.clone(object?.toJson(full: true)))); + _objectAdded(object); + } break; } } @@ -191,7 +192,8 @@ class ParseLiveList { if (_list[i].object.get(keyVarObjectId) == object.get(keyVarObjectId)) { _list.removeAt(i).dispose(); - _eventStreamController.sink.add(ParseLiveListDeleteEvent(i, object)); + _eventStreamController.sink.add(ParseLiveListDeleteEvent( + i, object?.clone(object?.toJson(full: true)))); break; } } @@ -205,9 +207,10 @@ class ParseLiveList { keyVarObjectId, _list[index].object.get(keyVarObjectId)) ..setLimit(1); final ParseResponse response = await queryBuilder.query(); - if (response.success) { + if (response.success && response.results != null) { _list[index].object = response.results.first; } else { + _list[index].object = null; throw response.error; } } @@ -256,12 +259,12 @@ class ParseLiveListElement { Stream get stream => _streamController?.stream; - T get object => _object; + T get object => _object?.clone(_object?.toJson(full: true)); set object(T value) { _loaded = true; _object = value; - _streamController?.add(object); + _streamController?.add(_object?.clone(_object?.toJson(full: true))); } bool get loaded => _loaded; @@ -291,12 +294,20 @@ class ParseLiveListDeleteEvent ParseLiveListDeleteEvent(int index, T object) : super(index, object); } -typedef Stream StreamGetter(); -typedef T DataGetter(); -typedef Widget ChildBuilder( - BuildContext context, bool failed, T loadedData); -typedef Widget RemovedItemBuilder( - BuildContext context, int index, T oldObject); +typedef StreamGetter = Stream Function(); +typedef DataGetter = T Function(); +typedef ChildBuilder = Widget Function( + BuildContext context, ParseLiveListElementSnapshot snapshot); + +class ParseLiveListElementSnapshot { + ParseLiveListElementSnapshot({this.loadedData, this.error}); + + final T loadedData; + final ParseError error; + + bool get hasData => loadedData != null; + bool get failed => error != null; +} class ParseLiveListWidget extends StatefulWidget { const ParseLiveListWidget( @@ -327,8 +338,8 @@ class ParseLiveListWidget extends StatefulWidget { final bool reverse; final bool shrinkWrap; - final ChildBuilder childBuilder; - final RemovedItemBuilder removedItemBuilder; + final ChildBuilder childBuilder; + final ChildBuilder removedItemBuilder; @override _ParseLiveListWidgetState createState() => @@ -390,7 +401,7 @@ class _ParseLiveListWidgetState ParseLiveList _liveList; final GlobalKey _animatedListKey = GlobalKey(); - final RemovedItemBuilder removedItemBuilder; + final ChildBuilder removedItemBuilder; @override Widget build(BuildContext context) { @@ -446,7 +457,7 @@ class ParseLiveListElementWidget extends StatefulWidget { final DataGetter loadedData; final Animation sizeFactor; final Duration duration; - final ChildBuilder childBuilder; + final ChildBuilder childBuilder; @override _ParseLiveListElementWidgetState createState() { @@ -459,23 +470,38 @@ class _ParseLiveListElementWidgetState with SingleTickerProviderStateMixin { _ParseLiveListElementWidgetState( DataGetter loadedDataGetter, StreamGetter stream) { - loadedData = loadedDataGetter(); +// loadedData = loadedDataGetter(); + _snapshot = ParseLiveListElementSnapshot(loadedData: loadedDataGetter()); if (stream != null) { - _streamSubscription = stream().listen((T data) { - if (widget != null) { - setState(() { - loadedData = data; - }); - } else { - loadedData = data; - } - }); + _streamSubscription = stream().listen( + (T data) { + if (widget != null) { + setState(() { + _snapshot = ParseLiveListElementSnapshot(loadedData: data); + }); + } else { + _snapshot = ParseLiveListElementSnapshot(loadedData: data); + } + }, + onError: (Object error) { + if (error is ParseError) { + if (widget != null) { + setState(() { + _snapshot = ParseLiveListElementSnapshot(error: error); + }); + } else { + _snapshot = ParseLiveListElementSnapshot(error: error); + } + } + }, + cancelOnError: false, + ); } } - T loadedData; - bool failed = false; + + ParseLiveListElementSnapshot _snapshot; + StreamSubscription _streamSubscription; - bool firstBuild = true; @override void dispose() { @@ -491,10 +517,9 @@ class _ParseLiveListElementWidgetState child: AnimatedSize( duration: widget.duration, vsync: this, - child: widget.childBuilder(context, failed, loadedData), + child: widget.childBuilder(context, _snapshot), ), ); - firstBuild = false; return result; } }