refactor: Use own highlight rendering with working scrollbar and text selection
This commit is contained in:
parent
8e4c61f03b
commit
d8d0abf27c
5 changed files with 117 additions and 45 deletions
|
|
@ -2,13 +2,13 @@ import 'package:flutter/cupertino.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter_highlighter/flutter_highlighter.dart';
|
||||
import 'package:flutter_highlighter/themes/shades-of-purple.dart';
|
||||
import 'package:flutter_linkify/flutter_linkify.dart';
|
||||
import 'package:highlight/highlight.dart' show highlight;
|
||||
import 'package:html/dom.dart' as dom;
|
||||
import 'package:html/parser.dart' as parser;
|
||||
import 'package:matrix/matrix.dart';
|
||||
|
||||
import 'package:fluffychat/utils/code_highlight_theme.dart';
|
||||
import 'package:fluffychat/utils/event_checkbox_extension.dart';
|
||||
import 'package:fluffychat/widgets/avatar.dart';
|
||||
import 'package:fluffychat/widgets/future_loading_dialog.dart';
|
||||
|
|
@ -137,6 +137,19 @@ class HtmlMessage extends StatelessWidget {
|
|||
];
|
||||
}
|
||||
|
||||
InlineSpan _renderCodeBlockNode(dom.Node node) {
|
||||
if (node is! dom.Element) {
|
||||
return TextSpan(text: node.text);
|
||||
}
|
||||
final style = atomOneDarkTheme[node.className.split('-').last] ??
|
||||
atomOneDarkTheme['root'];
|
||||
|
||||
return TextSpan(
|
||||
children: node.nodes.map(_renderCodeBlockNode).toList(),
|
||||
style: style,
|
||||
);
|
||||
}
|
||||
|
||||
/// Transforms a Node to an InlineSpan.
|
||||
InlineSpan _renderHtml(
|
||||
dom.Node node,
|
||||
|
|
@ -334,33 +347,60 @@ class HtmlMessage extends StatelessWidget {
|
|||
);
|
||||
case 'code':
|
||||
final isInline = node.parent?.localName != 'pre';
|
||||
final lang = node.className
|
||||
.split(' ')
|
||||
.singleWhereOrNull(
|
||||
(className) => className.startsWith('language-'),
|
||||
)
|
||||
?.split('language-')
|
||||
.last ??
|
||||
'md';
|
||||
final highlightedHtml =
|
||||
highlight.parse(node.text, language: lang).toHtml();
|
||||
final element = parser.parse(highlightedHtml).body;
|
||||
if (element == null) {
|
||||
return const TextSpan(text: 'Unable to render code block!');
|
||||
}
|
||||
final controller = isInline ? null : ScrollController();
|
||||
|
||||
return WidgetSpan(
|
||||
child: Material(
|
||||
clipBehavior: Clip.hardEdge,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: HighlightView(
|
||||
node.text,
|
||||
language: node.className
|
||||
.split(' ')
|
||||
.singleWhereOrNull(
|
||||
(className) => className.startsWith('language-'),
|
||||
)
|
||||
?.split('language-')
|
||||
.last ??
|
||||
'md',
|
||||
theme: shadesOfPurpleTheme,
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: isInline ? 0 : 8,
|
||||
),
|
||||
textStyle: TextStyle(
|
||||
fontSize: fontSize,
|
||||
fontFamily: 'RobotoMono',
|
||||
),
|
||||
),
|
||||
color: atomOneBackgroundColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
side: const BorderSide(color: hightlightTextColor),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: isInline
|
||||
? Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [_renderCodeBlockNode(element)],
|
||||
),
|
||||
selectionColor: hightlightTextColor.withAlpha(128),
|
||||
),
|
||||
)
|
||||
: RawScrollbar(
|
||||
thumbVisibility: true,
|
||||
trackVisibility: true,
|
||||
controller: controller,
|
||||
thumbColor: hightlightTextColor,
|
||||
trackColor: hightlightTextColor.withAlpha(128),
|
||||
thickness: 8,
|
||||
child: SingleChildScrollView(
|
||||
controller: controller,
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
children: [_renderCodeBlockNode(element)],
|
||||
),
|
||||
selectionColor: hightlightTextColor.withAlpha(212),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
case 'img':
|
||||
|
|
|
|||
40
lib/utils/code_highlight_theme.dart
Normal file
40
lib/utils/code_highlight_theme.dart
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import 'package:flutter/widgets.dart';
|
||||
|
||||
const hightlightTextColor = Color(0xffabb2bf);
|
||||
const atomOneBackgroundColor = Color(0xff282c34);
|
||||
const atomOneDarkTheme = {
|
||||
'root': TextStyle(color: hightlightTextColor),
|
||||
'comment': TextStyle(color: Color(0xff5c6370), fontStyle: FontStyle.italic),
|
||||
'quote': TextStyle(color: Color(0xff5c6370), fontStyle: FontStyle.italic),
|
||||
'doctag': TextStyle(color: Color(0xffc678dd)),
|
||||
'keyword': TextStyle(color: Color(0xffc678dd)),
|
||||
'formula': TextStyle(color: Color(0xffc678dd)),
|
||||
'section': TextStyle(color: Color(0xffe06c75)),
|
||||
'name': TextStyle(color: Color(0xffe06c75)),
|
||||
'selector-tag': TextStyle(color: Color(0xffe06c75)),
|
||||
'deletion': TextStyle(color: Color(0xffe06c75)),
|
||||
'subst': TextStyle(color: Color(0xffe06c75)),
|
||||
'literal': TextStyle(color: Color(0xff56b6c2)),
|
||||
'string': TextStyle(color: Color(0xff98c379)),
|
||||
'regexp': TextStyle(color: Color(0xff98c379)),
|
||||
'addition': TextStyle(color: Color(0xff98c379)),
|
||||
'attribute': TextStyle(color: Color(0xff98c379)),
|
||||
'meta-string': TextStyle(color: Color(0xff98c379)),
|
||||
'built_in': TextStyle(color: Color(0xffe6c07b)),
|
||||
'attr': TextStyle(color: Color(0xffd19a66)),
|
||||
'variable': TextStyle(color: Color(0xffd19a66)),
|
||||
'template-variable': TextStyle(color: Color(0xffd19a66)),
|
||||
'type': TextStyle(color: Color(0xffd19a66)),
|
||||
'selector-class': TextStyle(color: Color(0xffd19a66)),
|
||||
'selector-attr': TextStyle(color: Color(0xffd19a66)),
|
||||
'selector-pseudo': TextStyle(color: Color(0xffd19a66)),
|
||||
'number': TextStyle(color: Color(0xffd19a66)),
|
||||
'symbol': TextStyle(color: Color(0xff61aeee)),
|
||||
'bullet': TextStyle(color: Color(0xff61aeee)),
|
||||
'link': TextStyle(color: Color(0xff61aeee)),
|
||||
'meta': TextStyle(color: Color(0xff61aeee)),
|
||||
'selector-id': TextStyle(color: Color(0xff61aeee)),
|
||||
'title': TextStyle(color: Color(0xff61aeee)),
|
||||
'emphasis': TextStyle(fontStyle: FontStyle.italic),
|
||||
'strong': TextStyle(fontWeight: FontWeight.bold),
|
||||
};
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
import 'package:flutter_highlighter/flutter_highlighter.dart';
|
||||
import 'package:flutter_highlighter/themes/shades-of-purple.dart';
|
||||
import 'package:matrix/matrix.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
|
|
@ -40,10 +38,12 @@ class ErrorReporter {
|
|||
height: 256,
|
||||
width: 256,
|
||||
child: SingleChildScrollView(
|
||||
child: HighlightView(
|
||||
child: Text(
|
||||
text,
|
||||
language: 'sh',
|
||||
theme: shadesOfPurpleTheme,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontFamily: 'RobotoMono',
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
18
pubspec.lock
18
pubspec.lock
|
|
@ -483,14 +483,6 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.1.0"
|
||||
flutter_highlighter:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_highlighter
|
||||
sha256: "93173afd47a9ada53f3176371755e7ea4a1065362763976d06d6adfb4d946e10"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.1"
|
||||
flutter_linkify:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
@ -799,14 +791,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.0"
|
||||
highlighter:
|
||||
dependency: transitive
|
||||
highlight:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: highlighter
|
||||
sha256: "92180c72b9da8758e1acf39a45aa305a97dcfe2fdc8f3d1d2947c23f2772bfbc"
|
||||
name: highlight
|
||||
sha256: "5353a83ffe3e3eca7df0abfb72dcf3fa66cc56b953728e7113ad4ad88497cf21"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.1"
|
||||
version: "0.7.0"
|
||||
html:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ dependencies:
|
|||
flutter:
|
||||
sdk: flutter
|
||||
flutter_foreground_task: ^9.1.0
|
||||
flutter_highlighter: ^0.1.1
|
||||
flutter_linkify: ^6.0.0
|
||||
flutter_local_notifications: ^19.5.0
|
||||
flutter_localizations:
|
||||
|
|
@ -45,6 +44,7 @@ dependencies:
|
|||
geolocator: ^14.0.2
|
||||
go_router: ^17.0.0
|
||||
handy_window: ^0.4.0
|
||||
highlight: ^0.7.0
|
||||
html: ^0.15.4
|
||||
http: ^1.6.0
|
||||
image: ^4.1.7
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue