|
|
|
|
|
|
using System; |
|
|
|
using System.Collections.Generic; |
|
|
|
using System.Linq; |
|
|
|
using uiwidgets; |
|
|
|
using Unity.UIWidgets.animation; |
|
|
|
using Unity.UIWidgets.async; |
|
|
|
|
|
|
using Unity.UIWidgets.ui; |
|
|
|
using Unity.UIWidgets.scheduler; |
|
|
|
using UnityEngine; |
|
|
|
using Color = Unity.UIWidgets.ui.Color; |
|
|
|
|
|
|
|
namespace UIWidgetsSample |
|
|
|
{ |
|
|
|
|
|
|
{ |
|
|
|
/// Used for pagination (infinite scroll) together with [onEndReached].
|
|
|
|
/// When true, indicates that there are no more pages to load and
|
|
|
|
/// pagination will not be triggered.
|
|
|
|
public readonly bool? isLastPage; |
|
|
|
/// Used for pagination (infinite scroll) together with [onEndReached].
|
|
|
|
/// When true, indicates that there are no more pages to load and
|
|
|
|
/// pagination will not be triggered.
|
|
|
|
public readonly bool? isLastPage; |
|
|
|
|
|
|
|
/// Item builder
|
|
|
|
public readonly ItemBuilder itemBuilder; |
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
/// [ChatList] widget state
|
|
|
|
public class _ChatListState : SingleTickerProviderStateMixin<ChatList> |
|
|
|
public class _ChatListState : SingleTickerProviderStateMixin<ChatList> |
|
|
|
public AnimationController _controller ; |
|
|
|
|
|
|
|
public Animation<float> _animation; |
|
|
|
|
|
|
|
private bool _isNextPageLoading; |
|
|
|
public List<object> _oldData ; |
|
|
|
public Animation<float> _animation; |
|
|
|
public AnimationController _controller; |
|
|
|
public bool _isNextPageLoading; |
|
|
|
public List<object> _oldData; |
|
|
|
|
|
|
|
public override void initState() |
|
|
|
{ |
|
|
|
base.initState(); |
|
|
|
|
|
|
curve: Curves.easeOutQuad, |
|
|
|
parent: _controller |
|
|
|
); |
|
|
|
_isNextPageLoading = false; |
|
|
|
_oldData = new List<object>(); |
|
|
|
_oldData.AddRange(widget.items); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
private void _calculateDiffs(List<object> oldList) |
|
|
|
{ |
|
|
|
_oldData = new List<object>(widget.items); |
|
|
|
/*var diffResult = calculateListDiff<object>( |
|
|
|
oldList, |
|
|
|
widget.items, |
|
|
|
equalityChecker: (item1, item2) => |
|
|
|
{ |
|
|
|
if (item1 is Dictionary<string, object> && item2 is Dictionary<string, object>) |
|
|
|
{ |
|
|
|
var message1 = item1["message"]! as ChatComponents.Message; |
|
|
|
var message2 = item2["message"]! as ChatComponents.Message; |
|
|
|
|
|
|
|
return message1.id == message2.id; |
|
|
|
} |
|
|
|
|
|
|
|
return item1 == item2; |
|
|
|
} |
|
|
|
); |
|
|
|
foreach (var update in diffResult.getUpdates(batch: false)) |
|
|
|
update.when( |
|
|
|
insert: (pos, count) => { _listKey.currentState?.insertItem(pos); }, |
|
|
|
remove: (pos, count) => |
|
|
|
{ |
|
|
|
var item = oldList[pos]; |
|
|
|
_listKey.currentState?.removeItem( |
|
|
|
pos, |
|
|
|
(_, animation) => _buildRemovedMessage(item, animation) |
|
|
|
); |
|
|
|
}, |
|
|
|
change: (pos, payload) => { }, |
|
|
|
move: (from, to) => { } |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
foreach (var item1 in widget.items) |
|
|
|
{ |
|
|
|
if (item1 is Dictionary<string, object> ) |
|
|
|
{ |
|
|
|
var message1 = ((Dictionary<string, object>)item1)["message"] as ChatComponents.Message; |
|
|
|
if(message1!=null) |
|
|
|
_listKey.currentState?.insertItem(0); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var item1 in oldList) |
|
|
|
{ |
|
|
|
foreach (var item2 in widget.items) |
|
|
|
{ |
|
|
|
if (item1 is Dictionary<string, object> && item2 is Dictionary<string, object>) |
|
|
|
{ |
|
|
|
var message1 = ((Dictionary<string, object>)item1)["message"]! as ChatComponents.Message; |
|
|
|
var message2 = ((Dictionary<string, object>)item2)["message"]! as ChatComponents.Message; |
|
|
|
|
|
|
|
return message1.id == message2.id; |
|
|
|
} |
|
|
|
|
|
|
|
return item1 == item2; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
}*/ |
|
|
|
|
|
|
|
|
|
|
|
// Hacky solution to reconsider
|
|
|
|
private void _scrollToBottomIfNeeded(List<object> oldList) |
|
|
|
|
|
|
{ |
|
|
|
if (widget.onEndReached == null || widget.isLastPage == true) return false; |
|
|
|
|
|
|
|
if (notification.metrics.pixels >= |
|
|
|
notification.metrics.maxScrollExtent * |
|
|
|
(widget.onEndReachedThreshold ?? 0.75)) |
|
|
|
var _onEndReachedThreshold = |
|
|
|
widget.onEndReachedThreshold == null ? 0.75f : (float) widget.onEndReachedThreshold; |
|
|
|
if (notification.metrics.pixels >= notification.metrics.maxScrollExtent * _onEndReachedThreshold) |
|
|
|
if (widget.items.isEmpty() || _isNextPageLoading) return false; |
|
|
|
if (widget.items.isEmpty() || _isNextPageLoading) |
|
|
|
return false; |
|
|
|
_controller.duration = new TimeSpan(); |
|
|
|
_controller.forward(); |
|
|
|
SchedulerBinding.instance.addPostFrameCallback(stamp => |
|
|
|
{ |
|
|
|
_controller.duration = TimeSpan.Zero; |
|
|
|
_controller.forward(); |
|
|
|
setState(() => { _isNextPageLoading = true; }); |
|
|
|
setState(() => { _isNextPageLoading = true; }); |
|
|
|
widget.onEndReached().whenComplete(() => |
|
|
|
{ |
|
|
|
_controller.duration = TimeSpan.FromMilliseconds(300); |
|
|
|
_controller.reverse(); |
|
|
|
|
|
|
|
setState(() => { _isNextPageLoading = false; }); |
|
|
|
widget.onEndReached().whenComplete(() => |
|
|
|
{ |
|
|
|
_controller.duration = TimeSpan.FromMilliseconds(300); |
|
|
|
_controller.reverse(); |
|
|
|
setState(() => { _isNextPageLoading = false; }); |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
initialItemCount: widget.items.Count, |
|
|
|
key: _listKey, |
|
|
|
itemBuilder: (_, index, animation) => |
|
|
|
_buildNewMessage(index, animation) |
|
|
|
{ |
|
|
|
return _buildNewMessage(index, animation); |
|
|
|
} |
|
|
|
) |
|
|
|
), |
|
|
|
new SliverPadding( |
|
|
|