Chepuhagram/lib/presentation/widgets/message_bubble.dart

162 lines
5.8 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import 'package:flutter/material.dart';
import '/data/models/message_model.dart';
class MessageBubble extends StatelessWidget {
final MessageModel message;
final VoidCallback? onTap;
const MessageBubble({
super.key,
required this.message,
this.onTap,
});
@override
Widget build(BuildContext context) {
final isMe = message.isMe;
return Align(
// Выравниваем вправо, если это мое сообщение, и влево — если чужое
alignment: isMe ? Alignment.centerRight : Alignment.centerLeft,
child: Material(
color: Colors.transparent,
child: InkWell(
onTap: onTap,
// На телефонах иногда удобнее/надежнее long-press (как в мессенджерах),
// поэтому поддерживаем оба жеста.
onLongPress: onTap,
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(16),
topRight: const Radius.circular(16),
bottomLeft: Radius.circular(isMe ? 16 : 0),
bottomRight: Radius.circular(isMe ? 0 : 16),
),
child: Container(
margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 14),
constraints: BoxConstraints(
// Чтобы баббл не растягивался на весь экран, если текста мало
maxWidth: MediaQuery.of(context).size.width * 0.75,
),
decoration: BoxDecoration(
color: isMe
? Theme.of(context).colorScheme.primary
: Colors.grey[300],
borderRadius: BorderRadius.only(
topLeft: const Radius.circular(16),
topRight: const Radius.circular(16),
// Скругляем углы по-разному для "хвостика" сообщения
bottomLeft: Radius.circular(isMe ? 16 : 0),
bottomRight: Radius.circular(isMe ? 0 : 16),
),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
if (message.replyToText != null) ...[
Container(
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
margin: const EdgeInsets.only(bottom: 4),
decoration: BoxDecoration(
color: (isMe ? Colors.white : Colors.black).withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border(
left: BorderSide(
color: isMe ? Colors.white70 : Colors.black38,
width: 2,
),
),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
Icons.reply,
size: 14,
color: isMe ? Colors.white70 : Colors.black54,
),
const SizedBox(width: 4),
Expanded(
child: Text(
message.replyToText!,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: isMe ? Colors.white70 : Colors.black54,
fontSize: 12,
fontStyle: FontStyle.italic,
),
),
),
],
),
),
],
Text(
message.text,
style: TextStyle(
color: isMe ? Colors.white : Colors.black87,
fontSize: 16,
),
),
const SizedBox(height: 4),
Row(
mainAxisSize: MainAxisSize.min,
children: [
Text(
_formatTime(message.createdAt),
style: TextStyle(
color: isMe ? Colors.white70 : Colors.black54,
fontSize: 10,
),
),
if (isMe) ...[
const SizedBox(width: 6),
Icon(
_statusIcon(message.status),
size: 12,
color: _statusColor(message.status, isMe),
),
],
],
),
],
),
),
),
),
);
}
IconData _statusIcon(MessageStatus status) {
switch (status) {
case MessageStatus.sending:
return Icons.access_time;
case MessageStatus.sent:
return Icons.done;
case MessageStatus.delivered:
return Icons.done_all;
case MessageStatus.read:
return Icons.done_all;
case MessageStatus.failed:
return Icons.error;
}
}
Color _statusColor(MessageStatus status, bool isMe) {
switch (status) {
case MessageStatus.read:
return isMe ? Colors.blue : Colors.blue;
case MessageStatus.failed:
return Colors.red;
default:
return isMe ? Colors.white70 : Colors.black54;
}
}
String _formatTime(DateTime time) {
final hour = time.hour.toString().padLeft(2, '0');
final minute = time.minute.toString().padLeft(2, '0');
return '$hour:$minute';
}
}