Skip to content

Commit 6261e5e

Browse files
Replace rich_typewriter with typewritertext and enhance message rendering
- Migrated from rich_typewriter to typewritertext package - Added dynamic text rendering with typewriter effect - Implemented new state management for message display - Enhanced ChatMessageBubble with progressive text reveal - Updated pubspec.yaml and pubspec.lock dependencies
1 parent b7246c3 commit 6261e5e

File tree

4 files changed

+65
-24
lines changed

4 files changed

+65
-24
lines changed

lib/data/view/chat/chat_screen.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ class _ChatScreenState extends ConsumerState<ChatScreen> {
143143
return ChatMessageBubble(
144144
message: message,
145145
onShare: () => Utils.share(message.response),
146+
isNewMessage: index == currentChat.messages.length - 1 && _isLoading,
146147
);
147148
},
148149
),

lib/data/view/chat/widgets/chat_message_bubble.dart

Lines changed: 55 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,50 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter/services.dart';
33
import 'package:flutter_chatgpt_text_and_image_processing/configs/utils.dart';
4-
import 'package:rich_typewriter/rich_typewriter.dart';
4+
import 'package:typewritertext/typewritertext.dart';
55
import '../../../models/chat_message.dart';
66

7-
class ChatMessageBubble extends StatelessWidget {
7+
class ChatMessageBubble extends StatefulWidget {
88
final ChatMessage message;
99
final VoidCallback onShare;
10+
final bool isNewMessage;
1011

1112
const ChatMessageBubble({
1213
super.key,
1314
required this.message,
1415
required this.onShare,
16+
this.isNewMessage = false,
1517
});
1618

19+
@override
20+
State<ChatMessageBubble> createState() => _ChatMessageBubbleState();
21+
}
22+
23+
class _ChatMessageBubbleState extends State<ChatMessageBubble> {
24+
bool _showFullText = false;
25+
26+
@override
27+
void initState() {
28+
super.initState();
29+
if (!widget.isNewMessage) {
30+
_showFullText = true;
31+
}
32+
}
33+
1734
@override
1835
Widget build(BuildContext context) {
1936
return Column(
2037
crossAxisAlignment: CrossAxisAlignment.stretch,
2138
children: [
2239
_buildMessageBubble(
2340
context,
24-
message.query,
41+
widget.message.query,
2542
isUser: true,
2643
),
2744
const SizedBox(height: 8),
2845
_buildMessageBubble(
2946
context,
30-
message.response,
47+
widget.message.response,
3148
isUser: false,
3249
),
3350
],
@@ -39,13 +56,23 @@ class ChatMessageBubble extends StatelessWidget {
3956
final paragraphs = text.split('\n\n');
4057

4158
for (final paragraph in paragraphs) {
42-
if (paragraph.startsWith('**') && paragraph.endsWith('**')) {
43-
// Headers
59+
if (paragraph.startsWith('# ')) {
60+
// H1 Headers
61+
spans.add(TextSpan(
62+
text: '${paragraph.substring(2)}\n',
63+
style: const TextStyle(
64+
color: Colors.white,
65+
fontSize: 24,
66+
fontWeight: FontWeight.bold,
67+
),
68+
));
69+
} else if (paragraph.startsWith('## ')) {
70+
// H2 Headers
4471
spans.add(TextSpan(
45-
text: '${paragraph.replaceAll('**', '')}\n',
72+
text: '${paragraph.substring(3)}\n',
4673
style: const TextStyle(
4774
color: Colors.white,
48-
fontSize: 18,
75+
fontSize: 20,
4976
fontWeight: FontWeight.bold,
5077
),
5178
));
@@ -103,14 +130,27 @@ class ChatMessageBubble extends StatelessWidget {
103130
text,
104131
style: const TextStyle(color: Colors.white),
105132
)
133+
else if (_showFullText)
134+
SelectableText(
135+
text,
136+
style: const TextStyle(
137+
color: Color(0xFFD1D5DB),
138+
fontSize: 16,
139+
),
140+
)
106141
else
107-
RichTypewriter(
108-
symbolDelay: (symbol) =>
109-
switch (symbol) { TextSpan(text: ' ') => 50, TextSpan(text: '\n') => 200, _ => 20 },
110-
onCompleted: () => debugPrint('Finished typing response'),
111-
child: SelectableText.rich(
112-
TextSpan(children: _processText(text)),
142+
TypeWriter(
143+
text: text,
144+
duration: const Duration(milliseconds: 30),
145+
textStyle: const TextStyle(
146+
color: Color(0xFFD1D5DB),
147+
fontSize: 16,
113148
),
149+
onEnd: () {
150+
setState(() {
151+
_showFullText = true;
152+
});
153+
},
114154
),
115155
if (!isUser) ...[
116156
const Divider(color: Color(0xFF4D4D4D)),
@@ -134,7 +174,7 @@ class ChatMessageBubble extends StatelessWidget {
134174
size: 20,
135175
color: Color(0xFFD1D5DB),
136176
),
137-
onPressed: onShare,
177+
onPressed: widget.onShare,
138178
),
139179
],
140180
),

pubspec.lock

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -536,14 +536,6 @@ packages:
536536
url: "https://pub.dev"
537537
source: hosted
538538
version: "6.1.2"
539-
rich_typewriter:
540-
dependency: "direct main"
541-
description:
542-
name: rich_typewriter
543-
sha256: "6d05f889e11d1ffa42268b713f01f7a47a654f142f46d3f09949934bbf6be5ba"
544-
url: "https://pub.dev"
545-
source: hosted
546-
version: "1.1.0"
547539
riverpod:
548540
dependency: transitive
549541
description:
@@ -773,6 +765,14 @@ packages:
773765
url: "https://pub.dev"
774766
source: hosted
775767
version: "1.4.0"
768+
typewritertext:
769+
dependency: "direct main"
770+
description:
771+
name: typewritertext
772+
sha256: c6b90cdc9f7fd7ae3235d9c926012000f7a1765cd49d67b78370dc61123212b2
773+
url: "https://pub.dev"
774+
source: hosted
775+
version: "3.0.9"
776776
url_launcher:
777777
dependency: transitive
778778
description:

pubspec.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ dependencies:
4949
provider: ^6.1.1
5050
flutter_riverpod: ^2.5.1
5151
shared_preferences: ^2.2.2
52-
rich_typewriter: ^1.0.1
5352
uuid: ^4.3.3 # For unique chat IDs
5453
markdown_widget: ^2.3.2+3
54+
typewritertext: ^3.0.9
5555

5656
dev_dependencies:
5757
flutter_test:

0 commit comments

Comments
 (0)