Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 17 additions & 13 deletions lib/widget/blocks/container/table.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:markdown_widget/widget/selection_transformer.dart';
import '../../../config/configs.dart';
import '../../proxy_rich_text.dart';
import '../../span_node.dart';
Expand Down Expand Up @@ -63,19 +64,22 @@ class TableNode extends ElementNode {
}
}

final tableWidget = Table(
columnWidths: tbConfig.columnWidths,
defaultColumnWidth: tbConfig.defaultColumnWidth ?? IntrinsicColumnWidth(),
textBaseline: tbConfig.textBaseline,
textDirection: tbConfig.textDirection,
border: tbConfig.border ??
TableBorder.all(
color: parentStyle?.color ??
config.p.textStyle.color ??
Colors.grey),
defaultVerticalAlignment: tbConfig.defaultVerticalAlignment ??
TableCellVerticalAlignment.middle,
children: rows,
final tableWidget = SelectionTransformer.tabular(
columns: cellCount,
child: Table(
columnWidths: tbConfig.columnWidths,
defaultColumnWidth: tbConfig.defaultColumnWidth ?? IntrinsicColumnWidth(),
textBaseline: tbConfig.textBaseline,
textDirection: tbConfig.textDirection,
border: tbConfig.border ??
TableBorder.all(
color: parentStyle?.color ??
config.p.textStyle.color ??
Colors.grey),
defaultVerticalAlignment: tbConfig.defaultVerticalAlignment ??
TableCellVerticalAlignment.middle,
children: rows,
),
);

return WidgetSpan(
Expand Down
31 changes: 17 additions & 14 deletions lib/widget/blocks/leaf/code_block.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import 'package:flutter_highlight/themes/a11y-dark.dart';
import 'package:flutter_highlight/themes/a11y-light.dart';
import 'package:highlight/highlight.dart' as hi;
import 'package:markdown_widget/markdown_widget.dart';
import 'package:markdown_widget/widget/selection_transformer.dart';

///Tag: [MarkdownTag.pre]
///
Expand All @@ -26,20 +27,22 @@ class CodeBlockNode extends ElementNode {
width: double.infinity,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: List.generate(splitContents.length, (index) {
final currentContent = splitContents[index];
return ProxyRichText(TextSpan(
children: highLightSpans(
currentContent,
language: preConfig.language,
theme: preConfig.theme,
textStyle: style,
styleNotMatched: preConfig.styleNotMatched,
),
));
}),
child: SelectionTransformer.separated(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: List.generate(splitContents.length, (index) {
final currentContent = splitContents[index];
return ProxyRichText(TextSpan(
children: highLightSpans(
currentContent,
language: preConfig.language,
theme: preConfig.theme,
textStyle: style,
styleNotMatched: preConfig.styleNotMatched,
),
));
}),
),
),
),
);
Expand Down
112 changes: 112 additions & 0 deletions lib/widget/inlines/data_url_image.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import 'dart:convert';
import 'dart:typed_data';
import 'package:flutter/material.dart';

class Base64DataUrlImage extends StatefulWidget {
final String imageUrl;
final double? width;
final double? height;
final BoxFit? fit;
final Widget Function(BuildContext, Object, StackTrace?)? errorBuilder;
final String? semanticLabel;
final bool excludeFromSemantics;
final Color? color;
final Animation<double>? opacity;
final BlendMode? colorBlendMode;
final AlignmentGeometry alignment;
final ImageRepeat repeat;
final Rect? centerSlice;
final bool matchTextDirection;
final bool gaplessPlayback;
final bool isAntiAlias;
final FilterQuality filterQuality;
final int? cacheWidth;
final int? cacheHeight;

const Base64DataUrlImage(
this.imageUrl, {
Key? key,
this.width,
this.height,
this.fit,
this.errorBuilder,
this.semanticLabel,
this.excludeFromSemantics = false,
this.color,
this.opacity,
this.colorBlendMode,
this.alignment = Alignment.center,
this.repeat = ImageRepeat.noRepeat,
this.centerSlice,
this.matchTextDirection = false,
this.gaplessPlayback = false,
this.isAntiAlias = false,
this.filterQuality = FilterQuality.low,
this.cacheWidth,
this.cacheHeight,
}) : super(key: key);
@override
_Base64DataUrlImageState createState() => _Base64DataUrlImageState();

static Uint8List? _dataUrlToBytes(String dataUrl) {
try {
return base64.decode(dataUrl.split(',').last);
} catch (e) {
return null;
}
}
}

class _Base64DataUrlImageState extends State<Base64DataUrlImage> {
Uint8List? imageBytes;
Object? error;
StackTrace? stackTrace;

@override
void initState() {
super.initState();
try {
imageBytes = Base64DataUrlImage._dataUrlToBytes(widget.imageUrl);
if (imageBytes == null) {
throw Exception('Failed to decode image data.');
}
} catch (e, st) {
error = e;
stackTrace = st;
}
}

@override
Widget build(BuildContext context) {
if (error != null) {
return widget.errorBuilder?.call(context, error!, stackTrace) ??
Center(child: Text('<image>'));
}

return Image.memory(
imageBytes!,
key: widget.key,
scale: 1.0,
frameBuilder: null,
errorBuilder: widget.errorBuilder,
semanticLabel: widget.semanticLabel,
excludeFromSemantics: widget.excludeFromSemantics,
width: widget.width,
height: widget.height,
color: widget.color,
opacity: widget.opacity,
colorBlendMode: widget.colorBlendMode,
fit: widget.fit,
alignment: widget.alignment,
repeat: widget.repeat,
centerSlice: widget.centerSlice,
matchTextDirection: widget.matchTextDirection,
gaplessPlayback: widget.gaplessPlayback,
isAntiAlias: widget.isAntiAlias,
filterQuality: widget.filterQuality,
cacheWidth: widget.cacheWidth,
cacheHeight: widget.cacheHeight,
);
}
}

40 changes: 29 additions & 11 deletions lib/widget/inlines/img.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import 'dart:convert';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:markdown_widget/markdown_widget.dart';
import 'package:markdown_widget/widget/inlines/data_url_image.dart';

///Tag: [MarkdownTag.img]
class ImageNode extends SpanNode {
Expand All @@ -21,17 +24,32 @@ class ImageNode extends SpanNode {
final imageUrl = attributes['src'] ?? '';
final alt = attributes['alt'] ?? '';
final isNetImage = imageUrl.startsWith('http');
final imgWidget = isNetImage
? Image.network(imageUrl,
width: width,
height: height,
fit: BoxFit.cover, errorBuilder: (ctx, error, stacktrace) {
return buildErrorImage(imageUrl, alt, error);
})
: Image.asset(imageUrl, width: width, height: height, fit: BoxFit.cover,
errorBuilder: (ctx, error, stacktrace) {
return buildErrorImage(imageUrl, alt, error);
});
final isDataUrl = imageUrl.startsWith("data:");

final Widget imgWidget;
if (isNetImage) {
imgWidget = Image.network(imageUrl,
width: width,
height: height,
fit: BoxFit.cover, errorBuilder: (ctx, error, stacktrace) {
return buildErrorImage(imageUrl, alt, error);
});
} else if (isDataUrl) {
imgWidget = Base64DataUrlImage(imageUrl,
width: width,
height: height,
fit: BoxFit.cover, errorBuilder: (ctx, error, stacktrace) {
return buildErrorImage(imageUrl, alt, error);
});
} else {
imgWidget = Image.asset(imageUrl,
width: width,
height: height,
fit: BoxFit.cover, errorBuilder: (ctx, error, stacktrace) {
return buildErrorImage(imageUrl, alt, error);
});
}

final result = (parent != null && parent is LinkNode)
? imgWidget
: Builder(builder: (context) {
Expand Down
3 changes: 2 additions & 1 deletion lib/widget/markdown.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:markdown_widget/markdown_widget.dart';
import 'package:markdown_widget/widget/selection_transformer.dart';
import 'package:scroll_to_index/scroll_to_index.dart';
import 'package:visibility_detector/visibility_detector.dart';

Expand Down Expand Up @@ -126,7 +127,7 @@ class _MarkdownWidgetState extends State<MarkdownWidget> {
),
);
return widget.selectable
? SelectionArea(child: markdownWidget)
? SelectionArea(child: SelectionTransformer.separated(child: markdownWidget))
: markdownWidget;
}

Expand Down
3 changes: 2 additions & 1 deletion lib/widget/markdown_block.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:markdown_widget/widget/selection_transformer.dart';

import '../config/configs.dart';
import '../config/markdown_generator.dart';
Expand Down Expand Up @@ -35,6 +36,6 @@ class MarkdownBlock extends StatelessWidget {
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
);
return selectable ? SelectionArea(child: column) : column;
return selectable ? SelectionArea(child: SelectionTransformer.separated(child: column)) : column;
}
}
Loading