💫 Message list fully animated
This commit is contained in:
@@ -561,59 +561,77 @@ class ChatRoomScreen extends HookConsumerWidget {
|
|||||||
.abs() >
|
.abs() >
|
||||||
3;
|
3;
|
||||||
|
|
||||||
final key = ValueKey('$messageKeyPrefix${message.id}');
|
// Use a stable animation key that doesn't change during message lifecycle
|
||||||
|
final animationKey = ValueKey(
|
||||||
|
'message-anim-${message.nonce ?? message.id}',
|
||||||
|
);
|
||||||
|
|
||||||
return chatIdentity.when(
|
return TweenAnimationBuilder<double>(
|
||||||
skipError: true,
|
key: animationKey,
|
||||||
data:
|
tween: Tween<double>(begin: 0.0, end: 1.0),
|
||||||
(identity) => MessageItem(
|
duration: Duration(
|
||||||
key: key,
|
milliseconds: 400 + (index % 5) * 50,
|
||||||
message: message,
|
), // Staggered delay
|
||||||
isCurrentUser: identity?.id == message.senderId,
|
curve: Curves.easeOutCubic,
|
||||||
onAction: (action) {
|
builder: (context, animationValue, child) {
|
||||||
switch (action) {
|
return Transform.translate(
|
||||||
case MessageItemAction.delete:
|
offset: Offset(
|
||||||
messagesNotifier.deleteMessage(message.id);
|
0,
|
||||||
case MessageItemAction.edit:
|
20 * (1 - animationValue),
|
||||||
messageEditingTo.value = message.toRemoteMessage();
|
), // Slide up from bottom
|
||||||
messageController.text =
|
child: Opacity(opacity: animationValue, child: child),
|
||||||
messageEditingTo.value?.content ?? '';
|
);
|
||||||
attachments.value =
|
},
|
||||||
messageEditingTo.value!.attachments
|
child: chatIdentity.when(
|
||||||
.map((e) => UniversalFile.fromAttachment(e))
|
skipError: true,
|
||||||
.toList();
|
data:
|
||||||
case MessageItemAction.forward:
|
(identity) => MessageItem(
|
||||||
messageForwardingTo.value = message.toRemoteMessage();
|
message: message,
|
||||||
case MessageItemAction.reply:
|
isCurrentUser: identity?.id == message.senderId,
|
||||||
messageReplyingTo.value = message.toRemoteMessage();
|
onAction: (action) {
|
||||||
case MessageItemAction.resend:
|
switch (action) {
|
||||||
messagesNotifier.retryMessage(message.id);
|
case MessageItemAction.delete:
|
||||||
}
|
messagesNotifier.deleteMessage(message.id);
|
||||||
},
|
case MessageItemAction.edit:
|
||||||
onJump: (messageId) {
|
messageEditingTo.value = message.toRemoteMessage();
|
||||||
scrollToMessage(
|
messageController.text =
|
||||||
messageId: messageId,
|
messageEditingTo.value?.content ?? '';
|
||||||
messageList: messageList,
|
attachments.value =
|
||||||
messagesNotifier: messagesNotifier,
|
messageEditingTo.value!.attachments
|
||||||
listController: listController,
|
.map((e) => UniversalFile.fromAttachment(e))
|
||||||
scrollController: scrollController,
|
.toList();
|
||||||
ref: ref,
|
case MessageItemAction.forward:
|
||||||
);
|
messageForwardingTo.value = message.toRemoteMessage();
|
||||||
},
|
case MessageItemAction.reply:
|
||||||
progress: attachmentProgress.value[message.id],
|
messageReplyingTo.value = message.toRemoteMessage();
|
||||||
showAvatar: isLastInGroup,
|
case MessageItemAction.resend:
|
||||||
),
|
messagesNotifier.retryMessage(message.id);
|
||||||
loading:
|
}
|
||||||
() => MessageItem(
|
},
|
||||||
key: key,
|
onJump: (messageId) {
|
||||||
message: message,
|
scrollToMessage(
|
||||||
isCurrentUser: false,
|
messageId: messageId,
|
||||||
onAction: null,
|
messageList: messageList,
|
||||||
progress: null,
|
messagesNotifier: messagesNotifier,
|
||||||
showAvatar: false,
|
listController: listController,
|
||||||
onJump: (_) {},
|
scrollController: scrollController,
|
||||||
),
|
ref: ref,
|
||||||
error: (_, _) => SizedBox.shrink(key: key),
|
);
|
||||||
|
},
|
||||||
|
progress: attachmentProgress.value[message.id],
|
||||||
|
showAvatar: isLastInGroup,
|
||||||
|
),
|
||||||
|
loading:
|
||||||
|
() => MessageItem(
|
||||||
|
message: message,
|
||||||
|
isCurrentUser: false,
|
||||||
|
onAction: null,
|
||||||
|
progress: null,
|
||||||
|
showAvatar: false,
|
||||||
|
onJump: (_) {},
|
||||||
|
),
|
||||||
|
error: (_, _) => SizedBox.shrink(),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -692,19 +710,43 @@ class ChatRoomScreen extends HookConsumerWidget {
|
|||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: [
|
||||||
Expanded(
|
Expanded(
|
||||||
child: messages.when(
|
child: AnimatedSwitcher(
|
||||||
data:
|
duration: const Duration(milliseconds: 300),
|
||||||
(messageList) =>
|
switchInCurve: Curves.easeOutCubic,
|
||||||
messageList.isEmpty
|
switchOutCurve: Curves.easeInCubic,
|
||||||
? Center(child: Text('No messages yet'.tr()))
|
transitionBuilder: (
|
||||||
: chatMessageListWidget(messageList),
|
Widget child,
|
||||||
loading:
|
Animation<double> animation,
|
||||||
() => const Center(child: CircularProgressIndicator()),
|
) {
|
||||||
error:
|
return SlideTransition(
|
||||||
(error, _) => ResponseErrorWidget(
|
position: Tween<Offset>(
|
||||||
error: error,
|
begin: const Offset(0, 0.05),
|
||||||
onRetry: () => messagesNotifier.loadInitial(),
|
end: Offset.zero,
|
||||||
),
|
).animate(animation),
|
||||||
|
child: FadeTransition(opacity: animation, child: child),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: messages.when(
|
||||||
|
data:
|
||||||
|
(messageList) =>
|
||||||
|
messageList.isEmpty
|
||||||
|
? Center(
|
||||||
|
key: const ValueKey('empty-messages'),
|
||||||
|
child: Text('No messages yet'.tr()),
|
||||||
|
)
|
||||||
|
: chatMessageListWidget(messageList),
|
||||||
|
loading:
|
||||||
|
() => const Center(
|
||||||
|
key: ValueKey('loading-messages'),
|
||||||
|
child: CircularProgressIndicator(),
|
||||||
|
),
|
||||||
|
error:
|
||||||
|
(error, _) => ResponseErrorWidget(
|
||||||
|
key: const ValueKey('error-messages'),
|
||||||
|
error: error,
|
||||||
|
onRetry: () => messagesNotifier.loadInitial(),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
chatRoom.when(
|
chatRoom.when(
|
||||||
|
Reference in New Issue
Block a user