mirror of
https://github.com/we-promise/sure.git
synced 2026-04-13 00:57:22 +00:00
feat(mobile): render assistant messages as markdown (#1405)
* feat(mobile): render assistant messages as markdown, keep user text plain Add flutter_markdown dependency and conditionally render chat bubbles: - User messages use plain Text to avoid formatting markdown characters - Assistant messages use MarkdownBody with styled headings, bold, italic, lists and code blocks matching the existing color scheme - Bump Dart SDK constraint to >=3.3.0 to satisfy flutter_markdown 0.7.2 * fix(mobile): address markdown rendering review comments - Extract MarkdownStyleSheet into _markdownStyle() helper to avoid rebuilding TextStyles on every message render - Replace deprecated imageBuilder with sizedImageBuilder; block http/https image URIs to prevent unsolicited remote fetches from AI-generated content - Commit updated pubspec.lock with flutter_markdown 0.7.2 resolved Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix tests --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Juan José Mata <jjmata@jjmata.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import '../models/chat.dart';
|
||||
import '../providers/auth_provider.dart';
|
||||
@@ -414,6 +415,22 @@ class _MessageBubble extends StatelessWidget {
|
||||
required this.formatTime,
|
||||
});
|
||||
|
||||
/// Builds the markdown stylesheet once per render context instead of inline,
|
||||
/// avoiding redundant TextStyle allocations per message bubble.
|
||||
MarkdownStyleSheet _markdownStyle(BuildContext context) {
|
||||
final color = Theme.of(context).colorScheme.onSurfaceVariant;
|
||||
return MarkdownStyleSheet.fromTheme(Theme.of(context)).copyWith(
|
||||
p: TextStyle(color: color),
|
||||
strong: TextStyle(color: color, fontWeight: FontWeight.bold),
|
||||
em: TextStyle(color: color, fontStyle: FontStyle.italic),
|
||||
listBullet: TextStyle(color: color),
|
||||
h1: TextStyle(color: color, fontSize: 20, fontWeight: FontWeight.bold),
|
||||
h2: TextStyle(color: color, fontSize: 18, fontWeight: FontWeight.bold),
|
||||
h3: TextStyle(color: color, fontSize: 16, fontWeight: FontWeight.bold),
|
||||
code: TextStyle(color: color, fontFamily: 'monospace'),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
@@ -455,14 +472,27 @@ class _MessageBubble extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
message.content,
|
||||
style: TextStyle(
|
||||
color: isUser
|
||||
? colorScheme.onPrimary
|
||||
: colorScheme.onSurfaceVariant,
|
||||
if (isUser)
|
||||
Text(
|
||||
message.content,
|
||||
style: TextStyle(
|
||||
color: colorScheme.onPrimary,
|
||||
),
|
||||
)
|
||||
else
|
||||
MarkdownBody(
|
||||
data: message.content,
|
||||
selectable: false,
|
||||
softLineBreak: true,
|
||||
styleSheet: _markdownStyle(context),
|
||||
sizedImageBuilder: (config) {
|
||||
// Block remote images to prevent unsolicited network requests.
|
||||
if (config.uri.scheme == 'http' || config.uri.scheme == 'https') {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Image.asset(config.uri.toString());
|
||||
},
|
||||
),
|
||||
),
|
||||
if (message.toolCalls != null &&
|
||||
message.toolCalls!.isNotEmpty)
|
||||
Padding(
|
||||
|
||||
@@ -69,10 +69,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
version: "1.4.1"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -198,6 +198,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
flutter_markdown:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_markdown
|
||||
sha256: "08fb8315236099ff8e90cb87bb2b935e0a724a3af1623000a9cec930468e0f27"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.7+1"
|
||||
flutter_secure_storage:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -344,22 +352,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.0"
|
||||
markdown:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: markdown
|
||||
sha256: ee85086ad7698b42522c6ad42fe195f1b9898e4d974a1af4576c1a3a176cada9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.3.1"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
sha256: "12956d0ad8390bbcc63ca2e1469c0619946ccb52809807067a7020d57e647aa6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
version: "0.12.18"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
version: "0.13.0"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -657,10 +673,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.7"
|
||||
version: "0.7.9"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -4,7 +4,7 @@ publish_to: 'none'
|
||||
version: 0.6.9+20260402
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
flutter: '>=3.27.0'
|
||||
|
||||
dependencies:
|
||||
@@ -24,6 +24,7 @@ dependencies:
|
||||
url_launcher: ^6.2.5
|
||||
flutter_svg: ^2.2.0
|
||||
package_info_plus: ^8.0.0
|
||||
flutter_markdown: ^0.7.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user