Skip to content

Commit b5b9e90

Browse files
committed
fix: add support for linksDecoration, sentLinksDecorationColor, receivedLinksDecorationColor
1 parent bb9f814 commit b5b9e90

File tree

1 file changed

+113
-27
lines changed

1 file changed

+113
-27
lines changed

packages/flyer_chat_text_message/lib/src/flyer_chat_text_message.dart

Lines changed: 113 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,39 @@ typedef _LocalTheme =
1515
Color surfaceContainer,
1616
});
1717

18+
// This can be removed when this issue is resolved https://github.com/Infinitix-LLC/gpt_markdown/issues/85
19+
InlineSpan _applyLinkStyle(
20+
InlineSpan original,
21+
TextStyle? paragraphStyle,
22+
Color? color,
23+
TextDecoration? decoration,
24+
Color? decorationColor,
25+
) {
26+
if (original is TextSpan) {
27+
return TextSpan(
28+
text: original.text,
29+
children:
30+
original.children
31+
?.map(
32+
(child) => _applyLinkStyle(
33+
child,
34+
paragraphStyle,
35+
color,
36+
decoration,
37+
decorationColor,
38+
),
39+
)
40+
.toList(),
41+
style: (original.style ?? paragraphStyle ?? const TextStyle()).copyWith(
42+
color: color,
43+
decoration: decoration,
44+
decorationColor: decorationColor,
45+
),
46+
);
47+
}
48+
return original;
49+
}
50+
1851
/// A widget that displays a regular text message.
1952
///
2053
/// Supports markdown rendering via [GptMarkdown].
@@ -55,8 +88,14 @@ class FlyerChatTextMessage extends StatelessWidget {
5588
/// The color of the links in the received messages.
5689
final Color? receivedLinksColor;
5790

58-
/// The color of the links in the received messages.
59-
final TextDecoration linksDecoration;
91+
/// The color of the links decoration in the sent messages.
92+
final Color? sentLinksDecorationColor;
93+
94+
/// The color of the links decoration in the received messages.
95+
final Color? receivedLinksDecorationColor;
96+
97+
/// The decoration of the links.
98+
final TextDecoration? linksDecoration;
6099

61100
/// Text style for the message timestamp and status.
62101
final TextStyle? timeStyle;
@@ -99,7 +138,9 @@ class FlyerChatTextMessage extends StatelessWidget {
99138
this.receivedTextStyle,
100139
this.sentLinksColor,
101140
this.receivedLinksColor,
102-
this.linksDecoration = TextDecoration.underline,
141+
this.sentLinksDecorationColor,
142+
this.receivedLinksDecorationColor,
143+
this.linksDecoration,
103144
this.timeStyle,
104145
this.showTime = true,
105146
this.showStatus = true,
@@ -130,6 +171,12 @@ class FlyerChatTextMessage extends StatelessWidget {
130171
final paragraphStyle = _resolveParagraphStyle(isSentByMe, theme);
131172
final timeStyle = _resolveTimeStyle(isSentByMe, theme);
132173

174+
final linksColor = isSentByMe ? sentLinksColor : receivedLinksColor;
175+
final linksDecorationColor = _resolveLinksDecorationColor(
176+
isSentByMe,
177+
theme,
178+
);
179+
133180
final timeAndStatus =
134181
showTime || (isSentByMe && showStatus)
135182
? TimeAndStatus(
@@ -145,27 +192,31 @@ class FlyerChatTextMessage extends StatelessWidget {
145192
gptThemeData: GptMarkdownTheme.of(context),
146193
child: GptMarkdown(
147194
message.text,
148-
followLinkColor: true,
149-
style: _isOnlyEmoji ? paragraphStyle?.copyWith(fontSize: onlyEmojiFontSize) : paragraphStyle,
195+
style:
196+
_isOnlyEmoji
197+
? paragraphStyle?.copyWith(fontSize: onlyEmojiFontSize)
198+
: paragraphStyle,
150199
onLinkTap: onLinkTap,
151-
linkBuilder: (context, span, href, onTap) {
152-
return GestureDetector(
153-
onTap: () => onLinkTap?.call(href, span.toStringShort()),
154-
child: Text.rich(
155-
span,
156-
style: paragraphStyle?.copyWith(
157-
decorationColor: isSentByMe ? sentLinksColor : receivedLinksColor,
158-
decoration: linksDecoration,
200+
linkBuilder:
201+
(_, span, _, _) => Text.rich(
202+
_applyLinkStyle(
203+
span,
204+
paragraphStyle,
205+
linksColor,
206+
linksDecoration,
207+
linksDecorationColor,
159208
),
160209
),
161-
);
162-
},
163210
),
164211
);
165212

166213
final linkPreviewWidget =
167214
linkPreviewPosition != LinkPreviewPosition.none
168-
? context.read<Builders>().linkPreviewBuilder?.call(context, message, isSentByMe)
215+
? context.read<Builders>().linkPreviewBuilder?.call(
216+
context,
217+
message,
218+
isSentByMe,
219+
)
169220
: null;
170221

171222
return ClipRRect(
@@ -180,7 +231,10 @@ class FlyerChatTextMessage extends StatelessWidget {
180231
Container(
181232
padding:
182233
_isOnlyEmoji
183-
? EdgeInsets.symmetric(horizontal: (padding?.horizontal ?? 0) / 2, vertical: 0)
234+
? EdgeInsets.symmetric(
235+
horizontal: (padding?.horizontal ?? 0) / 2,
236+
vertical: 0,
237+
)
184238
: padding,
185239
child: _buildContentBasedOnPosition(
186240
context: context,
@@ -204,7 +258,10 @@ class FlyerChatTextMessage extends StatelessWidget {
204258
Widget? linkPreviewWidget,
205259
}) {
206260
final textDirection = Directionality.of(context);
207-
final effectiveLinkPreviewPosition = linkPreviewWidget != null ? linkPreviewPosition : LinkPreviewPosition.none;
261+
final effectiveLinkPreviewPosition =
262+
linkPreviewWidget != null
263+
? linkPreviewPosition
264+
: LinkPreviewPosition.none;
208265

209266
return Stack(
210267
children: [
@@ -213,30 +270,38 @@ class FlyerChatTextMessage extends StatelessWidget {
213270
crossAxisAlignment: CrossAxisAlignment.start,
214271
children: [
215272
if (topWidget != null) topWidget!,
216-
if (effectiveLinkPreviewPosition == LinkPreviewPosition.top) linkPreviewWidget!,
273+
if (effectiveLinkPreviewPosition == LinkPreviewPosition.top)
274+
linkPreviewWidget!,
217275
timeAndStatusPosition == TimeAndStatusPosition.inline
218276
? Row(
219277
mainAxisSize: MainAxisSize.min,
220278
crossAxisAlignment: CrossAxisAlignment.end,
221279
children: [
222280
Flexible(child: textContent),
223281
SizedBox(width: 4),
224-
Padding(padding: timeAndStatusPositionInlineInsets ?? EdgeInsets.zero, child: timeAndStatus),
282+
Padding(
283+
padding:
284+
timeAndStatusPositionInlineInsets ?? EdgeInsets.zero,
285+
child: timeAndStatus,
286+
),
225287
],
226288
)
227289
: textContent,
228-
if (effectiveLinkPreviewPosition == LinkPreviewPosition.bottom) linkPreviewWidget!,
290+
if (effectiveLinkPreviewPosition == LinkPreviewPosition.bottom)
291+
linkPreviewWidget!,
229292
if (timeAndStatusPosition != TimeAndStatusPosition.inline)
230293
// Ensure the width is not smaller than the timeAndStatus widget
231294
// Ensure the height accounts for it's height
232295
Opacity(opacity: 0, child: timeAndStatus),
233296
],
234297
),
235-
if (timeAndStatusPosition != TimeAndStatusPosition.inline && timeAndStatus != null)
298+
if (timeAndStatusPosition != TimeAndStatusPosition.inline &&
299+
timeAndStatus != null)
236300
Positioned.directional(
237301
textDirection: textDirection,
238302
end: timeAndStatusPosition == TimeAndStatusPosition.end ? 0 : null,
239-
start: timeAndStatusPosition == TimeAndStatusPosition.start ? 0 : null,
303+
start:
304+
timeAndStatusPosition == TimeAndStatusPosition.start ? 0 : null,
240305
bottom: 0,
241306
child: timeAndStatus,
242307
),
@@ -251,16 +316,29 @@ class FlyerChatTextMessage extends StatelessWidget {
251316
return receivedBackgroundColor ?? theme.surfaceContainer;
252317
}
253318

319+
Color? _resolveLinksDecorationColor(bool isSentByMe, _LocalTheme theme) {
320+
if (isSentByMe) {
321+
return sentLinksDecorationColor ?? sentLinksColor ?? theme.onPrimary;
322+
}
323+
return receivedLinksDecorationColor ??
324+
receivedLinksColor ??
325+
theme.onSurface;
326+
}
327+
254328
TextStyle? _resolveParagraphStyle(bool isSentByMe, _LocalTheme theme) {
255329
if (isSentByMe) {
256330
return sentTextStyle ?? theme.bodyMedium.copyWith(color: theme.onPrimary);
257331
}
258-
return receivedTextStyle ?? theme.bodyMedium.copyWith(color: theme.onSurface);
332+
return receivedTextStyle ??
333+
theme.bodyMedium.copyWith(color: theme.onSurface);
259334
}
260335

261336
TextStyle? _resolveTimeStyle(bool isSentByMe, _LocalTheme theme) {
262337
if (isSentByMe) {
263-
return timeStyle ?? theme.labelSmall.copyWith(color: _isOnlyEmoji ? theme.onSurface : theme.onPrimary);
338+
return timeStyle ??
339+
theme.labelSmall.copyWith(
340+
color: _isOnlyEmoji ? theme.onSurface : theme.onPrimary,
341+
);
264342
}
265343
return timeStyle ?? theme.labelSmall.copyWith(color: theme.onSurface);
266344
}
@@ -301,10 +379,18 @@ class TimeAndStatus extends StatelessWidget {
301379
spacing: 2,
302380
mainAxisSize: MainAxisSize.min,
303381
children: [
304-
if (showTime && time != null) Text(timeFormat.format(time!.toLocal()), style: textStyle),
382+
if (showTime && time != null)
383+
Text(timeFormat.format(time!.toLocal()), style: textStyle),
305384
if (showStatus && status != null)
306385
if (status == MessageStatus.sending)
307-
SizedBox(width: 6, height: 6, child: CircularProgressIndicator(color: textStyle?.color, strokeWidth: 2))
386+
SizedBox(
387+
width: 6,
388+
height: 6,
389+
child: CircularProgressIndicator(
390+
color: textStyle?.color,
391+
strokeWidth: 2,
392+
),
393+
)
308394
else
309395
Icon(getIconForStatus(status!), color: textStyle?.color, size: 12),
310396
],

0 commit comments

Comments
 (0)