Files
llm_chat/lib/widgets/message_bubble.dart
2025-12-30 01:06:42 +08:00

129 lines
3.9 KiB
Dart

import 'package:fluent_ui/fluent_ui.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import '../models/message.dart';
class MessageBubble extends StatelessWidget {
final Message message;
final bool showAvatar;
const MessageBubble({
super.key,
required this.message,
this.showAvatar = true,
});
@override
Widget build(BuildContext context) {
final theme = FluentTheme.of(context);
return Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: message.isUser
? MainAxisAlignment.end
: MainAxisAlignment.start,
children: [
if (!message.isUser && showAvatar) ...[
_buildAiAvatar(theme),
const SizedBox(width: 12),
],
Flexible(child: _buildMessageContent(context, theme)),
if (message.isUser && showAvatar) ...[
const SizedBox(width: 12),
_buildUserAvatar(theme),
],
],
),
);
}
Widget _buildAiAvatar(FluentThemeData theme) {
return Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: theme.accentColor,
borderRadius: BorderRadius.circular(8),
),
child: const Icon(FluentIcons.robot, color: Colors.white, size: 18),
);
}
Widget _buildUserAvatar(FluentThemeData theme) {
return Container(
width: 36,
height: 36,
decoration: BoxDecoration(
color: theme.accentColor.light,
borderRadius: BorderRadius.circular(8),
),
child: const Icon(FluentIcons.contact, color: Colors.white, size: 18),
);
}
Widget _buildMessageContent(BuildContext context, FluentThemeData theme) {
if (message.isUser) {
return Container(
constraints: const BoxConstraints(maxWidth: 600),
child: Card(
backgroundColor: theme.accentColor,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: Text(
message.content,
style: const TextStyle(color: Colors.white),
),
),
);
} else {
return Container(
constraints: const BoxConstraints(maxWidth: 600),
child: Card(
padding: const EdgeInsets.all(12),
child: MarkdownBody(
data: message.content,
selectable: true,
styleSheet: MarkdownStyleSheet(
p: theme.typography.body,
h1: theme.typography.title,
h2: theme.typography.subtitle,
h3: theme.typography.bodyLarge,
code: TextStyle(
fontFamily: 'Consolas',
fontSize: 13,
backgroundColor: theme.cardColor,
color: theme.accentColor,
),
codeblockDecoration: BoxDecoration(
color: theme.cardColor,
borderRadius: BorderRadius.circular(4),
),
blockquote: TextStyle(
color: theme.typography.body?.color?.withAlpha(180),
fontStyle: FontStyle.italic,
),
blockquoteDecoration: BoxDecoration(
border: Border(
left: BorderSide(color: theme.accentColor, width: 3),
),
),
tableHead: theme.typography.bodyStrong,
tableBody: theme.typography.body,
tableBorder: TableBorder.all(
color: theme.resources.dividerStrokeColorDefault,
),
tableCellsPadding: const EdgeInsets.all(8),
strong: theme.typography.bodyStrong,
em: TextStyle(
fontStyle: FontStyle.italic,
color: theme.typography.body?.color?.withAlpha(200),
),
),
),
),
);
}
}
}