saminsohag

ui improved

... ... @@ -110,16 +110,21 @@ $hello$
shape: const RoundedRectangleBorder(
side: BorderSide(width: 1),
),
child: TexMarkdown(
_controller.text,
onLinkTab: (url, title) {
log(title, name: "title");
log(url, name: "url");
},
style: const TextStyle(
color: Colors.green,
),
),
child: LayoutBuilder(builder: (context, constraints) {
return SingleChildScrollView(
scrollDirection: Axis.horizontal,
child: TexMarkdown(
_controller.text,
onLinkTab: (url, title) {
log(title, name: "title");
log(url, name: "url");
},
style: const TextStyle(
// color: Colors.green,
),
),
);
}),
);
}),
],
... ...
import 'package:flutter/material.dart';
class CustomDivider extends LeafRenderObjectWidget {
const CustomDivider({super.key, this.height, this.color});
final Color? color;
final double? height;
@override
RenderObject createRenderObject(BuildContext context) {
return RenderDivider(
color ?? Theme.of(context).colorScheme.onSurfaceVariant,
MediaQuery.of(context).size.width,
height ?? 2);
}
@override
void updateRenderObject(
BuildContext context, covariant RenderDivider renderObject) {
renderObject.color =
color ?? Theme.of(context).colorScheme.onSurfaceVariant;
renderObject.height = height ?? 2;
renderObject.width = MediaQuery.of(context).size.width;
}
}
class RenderDivider extends RenderBox {
RenderDivider(Color color, double width, double height)
: _color = color,
_height = height,
_width = width;
Color _color;
double _height;
double _width;
set color(Color value) {
if (value == _color) {
return;
}
_color = value;
markNeedsPaint();
}
set height(double value) {
if (value == _height) {
return;
}
_height = value;
markNeedsLayout();
}
set width(double value) {
if (value == _width) {
return;
}
_width = value;
markNeedsLayout();
}
@override
Size computeDryLayout(BoxConstraints constraints) {
return BoxConstraints.tightFor(width: null, height: _height)
.enforce(constraints)
.smallest;
}
@override
void performLayout() {
size = getDryLayout(constraints);
}
@override
void paint(PaintingContext context, Offset offset) {
context.canvas.drawRect(offset & Size(Rect.largest.size.width, _height),
Paint()..color = _color);
}
}
... ...
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
enum CustomRbSlot {
rb,
child,
}
class CustomRb extends RenderObjectWidget
with SlottedMultiChildRenderObjectWidgetMixin<CustomRbSlot> {
const CustomRb(
{super.key, this.spacing = 5, required this.child, required this.value});
final Widget child;
final bool value;
final double spacing;
@override
Widget? childForSlot(CustomRbSlot slot) {
switch (slot) {
case CustomRbSlot.rb:
return Radio(value: value, groupValue: true, onChanged: (value) {});
case CustomRbSlot.child:
return child;
}
}
@override
SlottedContainerRenderObjectMixin<CustomRbSlot> createRenderObject(
BuildContext context) {
return RenderCustomRb(spacing);
}
@override
void updateRenderObject(
BuildContext context, covariant RenderCustomRb renderObject) {
renderObject.spacing = spacing;
}
@override
Iterable<CustomRbSlot> get slots => CustomRbSlot.values;
}
class RenderCustomRb extends RenderBox
with SlottedContainerRenderObjectMixin<CustomRbSlot> {
RenderCustomRb(this._spacing);
double _spacing;
set spacing(double value) {
if (_spacing == value) {
return;
}
_spacing = value;
markNeedsLayout();
}
RenderBox? get rb => childForSlot(CustomRbSlot.rb);
RenderBox? get body => childForSlot(CustomRbSlot.child);
Size _layoutBox(RenderBox box, BoxConstraints constraints) {
box.layout(constraints, parentUsesSize: true);
return box.size;
}
@override
void performLayout() {
if (rb == null || body == null) {
size = constraints.constrain(const Size(50, 10));
return;
}
rb;
Size rbSize = _layoutBox(rb!, const BoxConstraints(maxWidth: 50));
Size bodySize = _layoutBox(
body!,
BoxConstraints(
maxWidth: constraints.maxWidth - rbSize.width - _spacing));
body!.parentData = BoxParentData()
..offset = Offset(rbSize.width + _spacing, 0);
rb!.parentData = BoxParentData()
..offset = Offset(
0,
body!.computeDistanceToActualBaseline(TextBaseline.alphabetic)! -
rb!.size.height / 1.5);
size = constraints.constrain(Size(bodySize.width + rbSize.width + _spacing,
max(rbSize.height, bodySize.height)));
}
@override
void paint(PaintingContext context, Offset offset) {
context.paintChild(
body!, offset + (body!.parentData as BoxParentData).offset);
context.paintChild(rb!, offset + (rb!.parentData as BoxParentData).offset);
}
@override
bool hitTestSelf(Offset position) {
return true;
}
@override
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
for (final RenderBox child in children) {
final BoxParentData parentData = child.parentData! as BoxParentData;
final bool isHit = result.addWithPaintOffset(
offset: parentData.offset,
position: position,
hitTest: (BoxHitTestResult result, Offset transformed) {
assert(transformed == position - parentData.offset);
return child.hitTest(result, position: transformed);
},
);
if (isHit) {
return true;
}
}
return false;
}
}
enum CustomCbSlot {
cb,
child,
}
class CustomCb extends RenderObjectWidget
with SlottedMultiChildRenderObjectWidgetMixin<CustomCbSlot> {
const CustomCb(
{super.key, this.spacing = 5, required this.child, required this.value});
final Widget child;
final bool value;
final double spacing;
@override
Widget? childForSlot(CustomCbSlot slot) {
switch (slot) {
case CustomCbSlot.cb:
return Checkbox(value: value, onChanged: (value) {});
case CustomCbSlot.child:
return child;
}
}
@override
SlottedContainerRenderObjectMixin<CustomCbSlot> createRenderObject(
BuildContext context) {
return RenderCustomCb(spacing);
}
@override
void updateRenderObject(
BuildContext context, covariant RenderCustomCb renderObject) {
renderObject.spacing = spacing;
}
@override
Iterable<CustomCbSlot> get slots => CustomCbSlot.values;
}
class RenderCustomCb extends RenderBox
with SlottedContainerRenderObjectMixin<CustomCbSlot> {
RenderCustomCb(this._spacing);
double _spacing;
set spacing(double value) {
if (_spacing == value) {
return;
}
_spacing = value;
markNeedsLayout();
}
RenderBox? get rb => childForSlot(CustomCbSlot.cb);
RenderBox? get body => childForSlot(CustomCbSlot.child);
Size _layoutBox(RenderBox box, BoxConstraints constraints) {
box.layout(constraints, parentUsesSize: true);
return box.size;
}
@override
void performLayout() {
if (rb == null || body == null) {
size = constraints.constrain(const Size(50, 10));
return;
}
rb;
Size rbSize = _layoutBox(rb!, const BoxConstraints(maxWidth: 50));
Size bodySize = _layoutBox(
body!,
BoxConstraints(
maxWidth: constraints.maxWidth - rbSize.width - _spacing));
body!.parentData = BoxParentData()
..offset = Offset(rbSize.width + _spacing, 0);
rb!.parentData = BoxParentData()
..offset = Offset(
0,
body!.computeDistanceToActualBaseline(TextBaseline.alphabetic)! -
rb!.size.height / 1.5);
size = constraints.constrain(Size(bodySize.width + rbSize.width + _spacing,
max(rbSize.height, bodySize.height)));
}
@override
void paint(PaintingContext context, Offset offset) {
context.paintChild(
body!, offset + (body!.parentData as BoxParentData).offset);
context.paintChild(rb!, offset + (rb!.parentData as BoxParentData).offset);
}
@override
bool hitTestSelf(Offset position) {
return true;
}
@override
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
for (final RenderBox child in children) {
final BoxParentData parentData = child.parentData! as BoxParentData;
final bool isHit = result.addWithPaintOffset(
offset: parentData.offset,
position: position,
hitTest: (BoxHitTestResult result, Offset transformed) {
assert(transformed == position - parentData.offset);
return child.hitTest(result, position: transformed);
},
);
if (isHit) {
return true;
}
}
return false;
}
}
... ...
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class UnorderedListView extends SingleChildRenderObjectWidget {
const UnorderedListView(
{super.key,
this.spacing = 6,
this.padding = 10,
this.bulletColor,
this.bulletSize = 4,
required super.child});
final double bulletSize;
final double spacing;
final double padding;
final Color? bulletColor;
@override
RenderObject createRenderObject(BuildContext context) {
return UnorderedListRenderObject(
spacing,
padding,
bulletColor ?? Theme.of(context).colorScheme.onSurface,
bulletSize,
);
}
@override
void updateRenderObject(
BuildContext context, covariant UnorderedListRenderObject renderObject) {
renderObject.bulletColor =
bulletColor ?? Theme.of(context).colorScheme.onSurface;
renderObject.bulletSize = bulletSize;
renderObject.spacing = spacing;
renderObject.padding = padding;
}
}
class UnorderedListRenderObject extends RenderProxyBox {
UnorderedListRenderObject(
double spacing,
double padding,
Color bulletColor,
this._bulletSize, {
RenderBox? child,
}) : _bulletColor = bulletColor,
_spacing = spacing,
_padding = padding,
super(child);
double _spacing;
double _padding;
Offset _bulletOffset = Offset.zero;
set spacing(double value) {
if (_spacing == value) {
return;
}
_spacing = value;
markNeedsLayout();
}
set padding(double value) {
if (_padding == value) {
return;
}
_padding = value;
markNeedsLayout();
}
Color _bulletColor;
double _bulletSize;
set bulletSize(double value) {
if (_bulletSize == value) {
return;
}
_bulletSize = value;
markNeedsLayout();
}
set bulletColor(Color value) {
if (_bulletColor == value) {
return;
}
_bulletColor = value;
markNeedsLayout();
}
@override
double computeMinIntrinsicWidth(double height) {
child!.layout(
BoxConstraints(
maxWidth:
constraints.maxWidth - _spacing - 6 - _bulletSize - _padding,
),
parentUsesSize: true);
return child!.size.width;
}
@override
double computeMaxIntrinsicWidth(double height) {
child!.layout(
BoxConstraints(
maxWidth:
constraints.maxWidth - _spacing - 6 - _bulletSize - _padding,
),
parentUsesSize: true);
return child!.size.width;
}
@override
double computeMinIntrinsicHeight(double width) {
child!.layout(
BoxConstraints(
maxWidth:
constraints.maxWidth - _spacing - 6 - _bulletSize - _padding,
),
parentUsesSize: true);
return child!.size.height;
}
@override
double computeMaxIntrinsicHeight(double width) {
child!.layout(
BoxConstraints(
maxWidth:
constraints.maxWidth - _spacing - 6 - _bulletSize - _padding,
),
parentUsesSize: true);
return child!.size.height;
}
@override
void performLayout() {
super.performLayout();
if (child == null) {
return;
}
child!.layout(
BoxConstraints(
maxWidth:
constraints.maxWidth - _spacing - 6 - _bulletSize - _padding,
),
parentUsesSize: true);
child!.parentData = BoxParentData()
..offset = Offset(_spacing + _padding + 6 + _bulletSize, 0);
var value = child!.computeDistanceToActualBaseline(TextBaseline.alphabetic);
_bulletOffset = Offset(4 + _padding, value! - _bulletSize);
size = constraints.constrain(Size(
child!.size.width + _spacing + _padding + 6 + _bulletSize,
child!.size.height));
}
@override
void paint(PaintingContext context, Offset offset) {
if (child == null) {
return;
}
context.paintChild(
child!, offset + (child!.parentData as BoxParentData).offset);
context.canvas.drawCircle(
offset + _bulletOffset, _bulletSize, Paint()..color = _bulletColor);
}
}
class OrderedListView extends SingleChildRenderObjectWidget {
final String no;
final double spacing;
final double padding;
const OrderedListView(
{super.key,
this.spacing = 6,
this.padding = 10,
TextStyle? style,
required super.child,
required this.no})
: _style = style;
final TextStyle? _style;
TextStyle getStyle(BuildContext context) {
if (_style == null || _style!.inherit) {
return DefaultTextStyle.of(context).style.merge(_style);
}
return _style!;
}
@override
RenderObject createRenderObject(BuildContext context) {
return OrderedListRenderObject(
no,
spacing,
padding,
getStyle(context),
);
}
@override
void updateRenderObject(
BuildContext context, covariant OrderedListRenderObject renderObject) {
renderObject.no = no;
renderObject.spacing = spacing;
renderObject.padding = padding;
renderObject.style = getStyle(context);
}
}
class OrderedListRenderObject extends RenderProxyBox {
OrderedListRenderObject(
String no,
double spacing,
double padding,
TextStyle style, {
RenderBox? child,
}) : _no = no,
_style = style,
_spacing = spacing,
_padding = padding,
super(child);
double _spacing;
double _padding;
Offset _ptOffset = Offset.zero;
set spacing(double value) {
if (_spacing == value) {
return;
}
_spacing = value;
markNeedsLayout();
}
set padding(double value) {
if (_padding == value) {
return;
}
_padding = value;
markNeedsLayout();
}
TextStyle _style;
set style(TextStyle value) {
_style = value;
markNeedsLayout();
}
String _no;
set no(String value) {
if (_no == value) {
return;
}
_no = value;
markNeedsLayout();
}
@override
double computeMinIntrinsicHeight(double width) {
pt = TextPainter(
text: TextSpan(
text: _no,
style: _style,
),
textDirection: TextDirection.ltr);
pt.layout(maxWidth: constraints.maxWidth - 50 - _spacing - _padding);
return child!
.computeMinIntrinsicHeight(width - pt.width - _spacing - _padding);
}
@override
double computeMaxIntrinsicHeight(double width) {
pt = TextPainter(
text: TextSpan(
text: _no,
style: _style,
),
textDirection: TextDirection.ltr);
pt.layout(maxWidth: constraints.maxWidth - 50 - _spacing - _padding);
return child!
.computeMaxIntrinsicHeight(width - pt.width - _spacing - _padding);
}
late TextPainter pt;
@override
void performLayout() {
super.performLayout();
if (child == null) {
return;
}
pt = TextPainter(
text: TextSpan(
text: _no,
style: _style,
),
textDirection: TextDirection.ltr);
pt.layout(maxWidth: constraints.maxWidth - 50 - _spacing - _padding);
child!.layout(
BoxConstraints(
maxWidth: constraints.maxWidth - pt.width - _spacing - _padding,
),
parentUsesSize: true);
child!.parentData = BoxParentData()
..offset = Offset(_spacing + _padding + pt.width, 0);
var value = child!.computeDistanceToActualBaseline(TextBaseline.alphabetic);
_ptOffset = Offset(_padding,
value! - pt.computeDistanceToActualBaseline(TextBaseline.alphabetic));
size = constraints.constrain(Size(
child!.size.width + _spacing + _padding + pt.width,
child!.size.height));
}
@override
void paint(PaintingContext context, Offset offset) {
if (child == null) {
return;
}
context.paintChild(
child!,
offset + (child!.parentData as BoxParentData).offset,
);
pt.paint(context.canvas, offset + _ptOffset);
}
}
... ...
import 'package:flutter/material.dart';
import 'package:tex_markdown/custom_widgets/custom_divider.dart';
import 'package:tex_markdown/custom_widgets/custom_rb_cb.dart';
import 'package:tex_markdown/custom_widgets/unordered_ordered_list.dart';
import 'package:tex_text/tex_text.dart';
import 'md_widget.dart';
/// Markdown components
abstract class MarkdownComponent {
static final List<MarkdownComponent> components = [
// TableMd(),
HTag(),
BoldMd(),
ItalicMd(),
ATagMd(),
ImageMd(),
ATagMd(),
UnOrderedList(),
OrderedList(),
RadioButtonMd(),
... ... @@ -38,7 +42,7 @@ abstract class MarkdownComponent {
}
/// Generate widget for markdown widget
static Widget generate(
static List<InlineSpan> generate(
BuildContext context,
String text,
TextStyle? style,
... ... @@ -52,7 +56,7 @@ abstract class MarkdownComponent {
if (each is InlineMd) {
spans.add(each.span(
context,
element,
element.trim(),
style,
onLinkTab,
));
... ... @@ -65,7 +69,7 @@ abstract class MarkdownComponent {
} else {
if (each is BlockMd) {
spans.add(
each.span(context, element, style, onLinkTab),
each.span(context, element.trim(), style, onLinkTab),
);
}
}
... ... @@ -74,12 +78,13 @@ abstract class MarkdownComponent {
}
},
);
return Text.rich(
TextSpan(
children: List.from(spans),
),
textAlign: TextAlign.left,
);
// return Text.rich(
// TextSpan(
// children: List.from(spans),
// ),
// // textAlign: TextAlign.left,
// );
return spans;
}
InlineSpan span(
... ... @@ -119,11 +124,27 @@ abstract class BlockMd extends MarkdownComponent {
TextStyle? style,
final void Function(String url, String title)? onLinkTab,
) {
return WidgetSpan(
child: Align(
alignment: Alignment.centerLeft,
child: build(context, text, style, onLinkTab),
),
return TextSpan(
children: [
const TextSpan(
text: "\n",
style: TextStyle(
fontSize: 0,
),
),
WidgetSpan(
child: build(context, text, style, onLinkTab),
alignment: PlaceholderAlignment.middle,
),
const TextSpan(
text: "\n",
style: TextStyle(fontSize: 0),
),
],
// child: Align(
// alignment: Alignment.centerLeft,
// child: build(context, text, style, onLinkTab),
// ),
);
}
... ... @@ -148,50 +169,53 @@ class HTag extends BlockMd {
final void Function(String url, String title)? onLinkTab,
) {
var match = exp.firstMatch(text.trim());
return Column(
return Text.rich(TextSpan(
children: [
Row(
children: [
Expanded(
child: TexText("${match?[2]}",
style: [
Theme.of(context)
.textTheme
.headlineLarge
?.copyWith(color: style?.color),
Theme.of(context)
.textTheme
.headlineMedium
?.copyWith(color: style?.color),
Theme.of(context)
.textTheme
.headlineSmall
?.copyWith(color: style?.color),
Theme.of(context)
.textTheme
.titleLarge
?.copyWith(color: style?.color),
Theme.of(context)
.textTheme
.titleMedium
?.copyWith(color: style?.color),
Theme.of(context)
.textTheme
.titleSmall
?.copyWith(color: style?.color),
][match![1]!.length - 1]),
),
],
WidgetSpan(
child: TexText("${match?[2]}",
style: [
Theme.of(context)
.textTheme
.headlineLarge
?.copyWith(color: style?.color),
Theme.of(context)
.textTheme
.headlineMedium
?.copyWith(color: style?.color),
Theme.of(context)
.textTheme
.headlineSmall
?.copyWith(color: style?.color),
Theme.of(context)
.textTheme
.titleLarge
?.copyWith(color: style?.color),
Theme.of(context)
.textTheme
.titleMedium
?.copyWith(color: style?.color),
Theme.of(context)
.textTheme
.titleSmall
?.copyWith(color: style?.color),
][match![1]!.length - 1]),
),
if (match[1]!.length == 1)
Divider(
height: 6,
thickness: 2,
color:
style?.color ?? Theme.of(context).colorScheme.onSurfaceVariant,
if (match[1]!.length == 1) ...[
const TextSpan(
text: "\n",
style: TextStyle(fontSize: 0, height: 0),
),
WidgetSpan(
alignment: PlaceholderAlignment.top,
child: CustomDivider(
height: 2,
color: style?.color ??
Theme.of(context).colorScheme.onSurfaceVariant,
),
),
],
],
);
));
}
@override
... ... @@ -212,9 +236,8 @@ class HrLine extends BlockMd {
TextStyle? style,
final void Function(String url, String title)? onLinkTab,
) {
return Divider(
height: 6,
thickness: 2,
return CustomDivider(
height: 2,
color: style?.color ?? Theme.of(context).colorScheme.onSurfaceVariant,
);
}
... ... @@ -237,27 +260,13 @@ class CheckBoxMd extends BlockMd {
final void Function(String url, String title)? onLinkTab,
) {
var match = exp.firstMatch(text.trim());
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Checkbox(
// value: true,
value: ("${match?[1]}" == "x"),
onChanged: (value) {},
fillColor: ButtonStyleButton.allOrNull(style?.color),
),
),
Expanded(
child: MdWidget(
"${match?[2]}",
onLinkTab: onLinkTab,
style: style,
),
),
],
return CustomCb(
value: ("${match?[1]}" == "x"),
child: MdWidget(
"${match?[2]}",
onLinkTab: onLinkTab,
style: style,
),
);
}
... ... @@ -283,27 +292,13 @@ class RadioButtonMd extends BlockMd {
final void Function(String url, String title)? onLinkTab,
) {
var match = exp.firstMatch(text.trim());
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Radio(
value: true,
groupValue: ("${match?[1]}" == "x"),
onChanged: (value) {},
fillColor: ButtonStyleButton.allOrNull(style?.color),
),
),
Expanded(
child: MdWidget(
"${match?[2]}",
onLinkTab: onLinkTab,
style: style,
),
),
],
return CustomRb(
value: ("${match?[1]}" == "x"),
child: MdWidget(
"${match?[2]}",
onLinkTab: onLinkTab,
style: style,
),
);
}
... ... @@ -329,26 +324,13 @@ class UnOrderedList extends BlockMd {
final void Function(String url, String title)? onLinkTab,
) {
var match = exp.firstMatch(text.trim());
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: Icon(
Icons.circle,
color: style?.color,
size: 8,
),
),
Expanded(
child: MdWidget(
"${match?[2]}",
onLinkTab: onLinkTab,
style: style,
),
),
],
return UnorderedListView(
bulletColor: style?.color,
child: MdWidget(
"${match?[2]}",
onLinkTab: onLinkTab,
style: style,
),
);
}
... ... @@ -377,26 +359,14 @@ class OrderedList extends BlockMd {
final void Function(String url, String title)? onLinkTab,
) {
var match = exp.firstMatch(text.trim());
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 11),
child: Text(
"${match?[1]}",
style: (style ?? const TextStyle())
.copyWith(fontWeight: FontWeight.bold),
),
),
Expanded(
child: MdWidget(
"${match?[2]}",
onLinkTab: onLinkTab,
style: style,
),
),
],
return OrderedListView(
no: "${match?[1]}",
style: (style ?? const TextStyle()).copyWith(fontWeight: FontWeight.bold),
child: MdWidget(
"${match?[2]}",
onLinkTab: onLinkTab,
style: style,
),
);
}
... ... @@ -576,6 +546,89 @@ class ImageMd extends InlineMd {
}
}
/// Table component
class TableMd extends BlockMd {
@override
Widget build(BuildContext context, String text, TextStyle? style,
void Function(String url, String title)? onLinkTab) {
final List<Map<int, String>> value = text
.split('\n')
.map<Map<int, String>>(
(e) => e
.split('|')
.where((element) => element.isNotEmpty)
.toList()
.asMap(),
)
.toList();
int maxCol = 0;
for (final each in value) {
if (maxCol < each.keys.length) {
maxCol = each.keys.length;
}
}
if (maxCol == 0) {
return Text("", style: style);
}
return Table(
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
border: TableBorder.all(
width: 1,
color: Theme.of(context).colorScheme.onSurface,
),
children: value
.map<TableRow>(
(e) => TableRow(
children: List.generate(
maxCol,
(index) => Center(
child: MdWidget(
(e[index] ?? "").trim(),
onLinkTab: onLinkTab,
style: style,
),
),
),
),
)
.toList(),
);
}
@override
RegExp get exp => RegExp(
r"(((\|[^\n\|]+\|)((([^\n\|]+\|)+)?))(\n(((\|[^\n\|]+\|)(([^\n\|]+\|)+)?)))+)?",
);
@override
String toHtml(String text) {
final String value = text.trim().splitMapJoin(
RegExp(r'^\||\|\n\||\|$'),
onMatch: (p0) => "\n",
onNonMatch: (p0) {
if (p0.trim().isEmpty) {
return "";
}
// return p0;
return '<tr>${p0.trim().splitMapJoin(
'|',
onMatch: (p0) {
return "";
},
onNonMatch: (p0) {
return '<td>$p0</td>';
},
)}</tr>';
},
);
return '''
<table border="1" cellspacing="0">
$value
</table>
''';
}
}
/// Text component
class TextMd extends InlineMd {
@override
... ...
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:tex_markdown/markdown_component.dart';
... ... @@ -46,53 +48,110 @@ $value
@override
Widget build(BuildContext context) {
final RegExp table = RegExp(
r"^(((\|[^\n\|]+\|)((([^\n\|]+\|)+)?))(\n(((\|[^\n\|]+\|)(([^\n\|]+\|)+)?)))+)?$",
);
if (table.hasMatch(exp)) {
final List<Map<int, String>> value = exp
.split('\n')
.map<Map<int, String>>(
(e) => e
.split('|')
.where((element) => element.isNotEmpty)
.toList()
.asMap(),
)
.toList();
int maxCol = 0;
for (final each in value) {
if (maxCol < each.keys.length) {
maxCol = each.keys.length;
}
}
if (maxCol == 0) {
return Text("", style: style);
}
return Table(
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
border: TableBorder.all(
width: 1,
color: Theme.of(context).colorScheme.onSurface,
),
children: value
.map<TableRow>(
(e) => TableRow(
children: List.generate(
maxCol,
(index) => Center(
child: MdWidget(
(e[index] ?? "").trim(),
onLinkTab: onLinkTab,
style: style,
),
List<InlineSpan> list = [];
exp.trim().splitMapJoin(
RegExp(r"\n\n+"),
onMatch: (p0) {
list.add(
const TextSpan(text: "\n"),
);
return "";
},
onNonMatch: (eachLn) {
final RegExp table = RegExp(
r"^(((\|[^\n\|]+\|)((([^\n\|]+\|)+)?))(\n(((\|[^\n\|]+\|)(([^\n\|]+\|)+)?)))+)?$",
);
if (table.hasMatch(eachLn)) {
final List<Map<int, String>> value = eachLn
.split('\n')
.map<Map<int, String>>(
(e) => e
.split('|')
.where((element) => element.isNotEmpty)
.toList()
.asMap(),
)
.toList();
int maxCol = 0;
for (final each in value) {
if (maxCol < each.keys.length) {
maxCol = each.keys.length;
}
}
// if (maxCol == 0) {
// return Text("", style: style);
// }
list.addAll(
[
const TextSpan(
text: "\n",
style: TextStyle(height: 0),
),
WidgetSpan(
child: Table(
defaultColumnWidth: CustomTableColumnWidth(),
defaultVerticalAlignment: TableCellVerticalAlignment.middle,
// defaultColumnWidth: const FixedColumnWidth(double.infinity),
border: TableBorder.all(
width: 1,
color: Theme.of(context).colorScheme.onSurface,
),
children: value
.map<TableRow>(
(e) => TableRow(
children: List.generate(
maxCol,
(index) => Center(
child: MdWidget(
(e[index] ?? "").trim(),
onLinkTab: onLinkTab,
style: style,
),
),
),
),
)
.toList(),
),
),
)
.toList(),
);
const TextSpan(
text: "\n",
style: TextStyle(height: 0),
),
],
);
} else {
list.addAll(
MarkdownComponent.generate(context, eachLn, style, onLinkTab),
);
}
return "";
},
);
return Text.rich(
TextSpan(
children: list,
),
);
}
}
class CustomTableColumnWidth extends TableColumnWidth {
@override
double maxIntrinsicWidth(Iterable<RenderBox> cells, double containerWidth) {
double width = 50;
for (var each in cells) {
each.layout(const BoxConstraints(), parentUsesSize: true);
width = max(width, each.size.width);
}
if (containerWidth == double.infinity) {
return width;
}
return MarkdownComponent.generate(context, exp, style, onLinkTab);
return min(containerWidth, width);
}
@override
double minIntrinsicWidth(Iterable<RenderBox> cells, double containerWidth) {
return 50;
}
}
... ...
... ... @@ -27,25 +27,6 @@ class TexMarkdown extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: data
.trim()
.split(
RegExp(r"\n\n+"),
)
.map<Widget>(
(e) => Padding(
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 4),
child: MdWidget(
e,
style: style,
followLinkColor: followLinkColor,
onLinkTab: onLinkTab,
),
),
)
.toList(),
);
return ClipRRect(child: MdWidget(data.trim()));
}
}
... ...