animateTo method
- ChatItemId itemId, {
- ChatItem? item,
- ChatItemQuote? reply,
- ChatItemQuote? forward,
- bool ignoreElements = false,
- bool offsetBasedOnBottom = true,
- bool addToHistory = true,
- double offset = 50,
Animates listController to a ChatItem identified by the provided
item
and its reply
or forward
.
Implementation
Future<void> animateTo(
ChatItemId itemId, {
ChatItem? item,
ChatItemQuote? reply,
ChatItemQuote? forward,
bool ignoreElements = false,
bool offsetBasedOnBottom = true,
bool addToHistory = true,
double offset = 50,
}) async {
final ChatItem? original = reply?.original ?? forward?.original ?? item;
final ChatItemId animateTo = original?.id ?? itemId;
final int index = elements.values.toList().indexWhere((e) {
return e.id.id == animateTo ||
(e is ChatForwardElement &&
(e.forwards.any((e1) => e1.value.id == animateTo) ||
e.note.value?.value.id == animateTo));
});
// If [original] is within the [elements], then just [animateToIndex].
if (index != -1 && !ignoreElements) {
_highlight(elements.values.elementAt(index).id);
if (listController.hasClients) {
await listController.sliverController.animateToIndex(
index,
offsetBasedOnBottom: offsetBasedOnBottom,
offset: offset,
duration: 200.milliseconds,
curve: Curves.ease,
);
} else {
initIndex = index;
}
// And add the transition to the [history].
if (addToHistory && item != null) {
this.addToHistory(item);
}
} else {
if (original != null) {
final ListElementId elementId = ListElementId(original.at, original.id);
final ListElementId? lastId =
elements.values
.lastWhereOrNull(
(e) =>
e is ChatMessageElement ||
e is ChatInfoElement ||
e is ChatCallElement ||
e is ChatForwardElement,
)
?.id;
// If the [original] is placed before the first item, then animate to top,
// or otherwise to bottom.
if (lastId != null && elementId.compareTo(lastId) == 1) {
if (_topLoader == null) {
_topLoader = LoaderElement.top();
elements[_topLoader!.id] = _topLoader!;
}
SchedulerBinding.instance.addPostFrameCallback((_) async {
_ignorePositionChanges = true;
await listController.sliverController.animateToIndex(
elements.length - 1,
offsetBasedOnBottom: true,
offset: 0,
duration: 300.milliseconds,
curve: Curves.ease,
);
_ignorePositionChanges = false;
});
} else {
if (_bottomLoader == null) {
_bottomLoader = LoaderElement.bottom();
elements[_bottomLoader!.id] = _bottomLoader!;
}
SchedulerBinding.instance.addPostFrameCallback((_) async {
_ignorePositionChanges = true;
await listController.sliverController.animateToIndex(
0,
offsetBasedOnBottom: true,
offset: 0,
duration: 300.milliseconds,
curve: Curves.ease,
);
_ignorePositionChanges = false;
});
}
}
// And then try to fetch the items.
try {
await _fetchItemsAround(
itemId,
reply: reply?.original?.id,
forward: forward?.original?.id,
);
final int index = elements.values.toList().indexWhere((e) {
return e.id.id == animateTo ||
(e is ChatForwardElement &&
(e.forwards.any((e1) => e1.value.id == animateTo) ||
e.note.value?.value.id == animateTo));
});
if (index != -1) {
// [FlutterListView] ignores the [initIndex], if it is 0.
if (index == 0) {
initIndex = 1;
initOffset = -5000;
} else {
initIndex = index;
initOffset = offset;
}
_highlight(elements.values.elementAt(index).id);
if (addToHistory && item != null) {
this.addToHistory(item);
}
}
} finally {
SchedulerBinding.instance.addPostFrameCallback((_) {
// Stop the animation, if any.
listController.jumpTo(listController.offset);
// Ensure [FlutterListView] has correct index and offset.
listController.sliverController.jumpToIndex(
initIndex,
offset: initOffset,
offsetBasedOnBottom: offsetBasedOnBottom,
);
_ignorePositionChanges = false;
elements.remove(_topLoader?.id);
elements.remove(_bottomLoader?.id);
_topLoader = null;
_bottomLoader = null;
});
}
}
}