Toggle navigation
Toggle navigation
This project
Loading...
Sign in
flutter_package
/
gpt_markdown
Go to a project
Toggle navigation
Projects
Groups
Snippets
Help
Toggle navigation pinning
Project
Activity
Repository
Pipelines
Graphs
Issues
0
Merge Requests
0
Wiki
Network
Create a new issue
Builds
Commits
Authored by
saminsohag
2023-03-25 19:38:48 +0600
Browse Files
Options
Browse Files
Download
Email Patches
Plain Diff
Commit
43eeb3c450bad2bcca0a0d247f079d2ebbc26d6c
43eeb3c4
1 parent
742e0131
bug fixed
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
655 additions
and
278 deletions
tex_markdown/.vscode/settings.json
tex_markdown/CHANGELOG.md
tex_markdown/example/lib/main.dart
tex_markdown/example/pubspec.lock
tex_markdown/lib/markdown_component.dart
tex_markdown/lib/md_widget.dart
tex_markdown/lib/tex_markdown.dart
tex_markdown/pubspec.yaml
tex_markdown/.vscode/settings.json
0 → 100644
View file @
43eeb3c
{
"cSpell.words"
:
[
"cellspacing"
,
"Sohag"
,
"stylesheet"
]
}
\ No newline at end of file
...
...
tex_markdown/CHANGELOG.md
View file @
43eeb3c
## 0.0.9
*
To html added.
## 0.0.8
*
Bug fixes.
## 0.0.7
*
Bug fixes.
## 0.0.6
*
Bug fixes.
...
...
tex_markdown/example/lib/main.dart
View file @
43eeb3c
import
'dart:developer'
;
import
'package:flutter/material.dart'
;
import
'package:flutter/services.dart'
;
import
'package:tex_markdown/tex_markdown.dart'
;
void
main
(
)
{
...
...
@@ -105,8 +106,8 @@ class _MyHomePageState extends State<MyHomePage> {
log
(
url
,
name:
"url"
);
},
style:
const
TextStyle
(
// color: Colors.red,
),
color:
Colors
.
green
,
),
);
}),
],
...
...
@@ -114,14 +115,37 @@ class _MyHomePageState extends State<MyHomePage> {
),
ConstrainedBox
(
constraints:
const
BoxConstraints
(
maxHeight:
200
),
child:
Padding
(
padding:
const
EdgeInsets
.
all
(
8.0
),
child:
TextField
(
decoration:
const
InputDecoration
(
border:
OutlineInputBorder
(),
label:
Text
(
"Type here:"
)),
maxLines:
null
,
controller:
_controller
,
),
child:
Stack
(
children:
[
Padding
(
padding:
const
EdgeInsets
.
all
(
8.0
),
child:
TextField
(
decoration:
const
InputDecoration
(
border:
OutlineInputBorder
(),
label:
Text
(
"Type here:"
)),
maxLines:
null
,
controller:
_controller
,
),
),
IconButton
(
onPressed:
()
{
String
html
=
'''<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"><title>markdown</title><link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.css" integrity="sha384-zB1R0rpPzHqg7Kpt0Aljp8JPLqbXI3bhnPWROx27a9N0Ll6ZP/+DiW/UqRcLbRjq" crossorigin="anonymous">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/katex.min.js" integrity="sha384-y23I5Q6l+B6vatafAwxRu/0oK/79VlbSz7Q9aiSZUvyWYIYsd+qj+o24G5ZU2zJz" crossorigin="anonymous"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/contrib/auto-render.min.js" integrity="sha384-kWPLUVMOks5AQFrykwIup5lo0m3iMkkHrD0uJ4H5cjeGihAutqP0yW0J6dpFiVkI" crossorigin="anonymous" onload="renderMathInElement(document.body);"></script>
</head>
<body>
${TexMarkdown.toHtml(_controller.text)}
</body>
</html>
'''
;
Clipboard
.
setData
(
ClipboardData
(
text:
html
));
},
icon:
const
Icon
(
Icons
.
html
),
),
],
),
),
],
...
...
tex_markdown/example/pubspec.lock
View file @
43eeb3c
...
...
@@ -238,15 +238,15 @@ packages:
path: ".."
relative: true
source: path
version: "0.0.
6
"
version: "0.0.
9
"
tex_text:
dependency: transitive
description:
name: tex_text
sha256: "5
62415e16b9c46816d8c2ed128fc46b299bb27bca9fac65db0c07535be0d0c60
"
sha256: "5
8c556fb09dff7034d6f29b77b3be6b90e7f1257817daa0000c6f1609094490a
"
url: "https://pub.dev"
source: hosted
version: "0.
0.8
"
version: "0.
1.2
"
tuple:
dependency: transitive
description:
...
...
tex_markdown/lib/markdown_component.dart
0 → 100644
View file @
43eeb3c
import
'package:flutter/material.dart'
;
import
'package:tex_text/tex_text.dart'
;
import
'md_widget.dart'
;
/// Markdown components
abstract
class
MarkdownComponent
{
static
final
List
<
MarkdownComponent
>
components
=
[
HTag
(),
BoldMd
(),
ItalicMd
(),
ATagMd
(),
ImageMd
(),
UnOrderedList
(),
OrderedList
(),
RadioButtonMd
(),
CheckBoxMd
(),
HrLine
(),
TextMd
(),
];
static
String
toHtml
(
String
text
)
{
String
html
=
""
;
text
.
split
(
RegExp
(
r"\n+"
)).
forEach
((
element
)
{
for
(
var
each
in
components
)
{
if
(
each
.
exp
.
hasMatch
(
element
.
trim
()))
{
if
(
each
is
InlineMd
)
{
html
+=
"
${each.toHtml(element)}
\n
"
;
break
;
}
else
if
(
each
is
BlockMd
)
{
html
+=
"
${each.toHtml(element)}
\n
"
;
break
;
}
}
}
});
return
html
;
}
static
Widget
generate
(
BuildContext
context
,
String
text
,
TextStyle
?
style
,
final
void
Function
(
String
url
,
String
title
)?
onLinkTab
,
)
{
List
<
Widget
>
children
=
[];
List
<
InlineSpan
>
spans
=
[];
text
.
split
(
RegExp
(
r"\n+"
)).
forEach
(
(
element
)
{
for
(
var
each
in
components
)
{
if
(
each
.
exp
.
hasMatch
(
element
.
trim
()))
{
if
(
each
is
InlineMd
)
{
if
(
spans
.
isNotEmpty
)
{
spans
.
add
(
TextSpan
(
text:
" "
,
style:
style
),
);
}
spans
.
add
(
each
.
inlineSpan
(
context
,
element
,
style
,
onLinkTab
,
));
}
else
{
if
(
spans
.
isNotEmpty
)
{
children
.
add
(
Text
.
rich
(
TextSpan
(
children:
List
.
from
(
spans
)),
textAlign:
TextAlign
.
left
,
),
);
spans
.
clear
();
}
if
(
each
is
BlockMd
)
{
children
.
add
(
each
.
build
(
context
,
element
,
style
,
onLinkTab
),
);
}
}
return
;
}
}
},
);
if
(
spans
.
isNotEmpty
)
{
children
.
add
(
Text
.
rich
(
TextSpan
(
children:
List
.
from
(
spans
)),
textAlign:
TextAlign
.
left
,
),
);
}
return
Column
(
crossAxisAlignment:
CrossAxisAlignment
.
start
,
children:
children
,
);
}
RegExp
get
exp
;
bool
get
inline
;
}
abstract
class
InlineMd
extends
MarkdownComponent
{
@override
bool
get
inline
=>
true
;
InlineSpan
inlineSpan
(
BuildContext
context
,
String
text
,
TextStyle
?
style
,
final
void
Function
(
String
url
,
String
title
)?
onLinkTab
,
);
String
toHtml
(
String
text
);
}
abstract
class
BlockMd
extends
MarkdownComponent
{
@override
bool
get
inline
=>
false
;
Widget
build
(
BuildContext
context
,
String
text
,
TextStyle
?
style
,
final
void
Function
(
String
url
,
String
title
)?
onLinkTab
,
);
String
toHtml
(
String
text
);
}
class
HTag
extends
BlockMd
{
@override
final
RegExp
exp
=
RegExp
(
r"^(#{1,6})\s([^\n]+)$"
);
@override
Widget
build
(
BuildContext
context
,
String
text
,
TextStyle
?
style
,
final
void
Function
(
String
url
,
String
title
)?
onLinkTab
,
)
{
var
match
=
exp
.
firstMatch
(
text
.
trim
());
return
Column
(
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
],
),
),
],
),
if
(
match
[
1
]!.
length
==
1
)
const
Divider
(
height:
6
),
],
);
}
@override
String
toHtml
(
String
text
)
{
var
match
=
exp
.
firstMatch
(
text
.
trim
());
return
"<h
${match![1]!.length}
>
${TexText.toHtmlData(match[2].toString().trim())}
<h
${match[1]!.length}
>"
;
}
}
class
HrLine
extends
BlockMd
{
@override
final
RegExp
exp
=
RegExp
(
r"^(--)[-]+$"
);
@override
Widget
build
(
BuildContext
context
,
String
text
,
TextStyle
?
style
,
final
void
Function
(
String
url
,
String
title
)?
onLinkTab
,
)
{
return
const
Divider
(
height:
5
,
);
}
@override
String
toHtml
(
String
text
)
{
return
"<hr/>"
;
}
}
class
CheckBoxMd
extends
BlockMd
{
get
onLinkTab
=>
null
;
@override
Widget
build
(
BuildContext
context
,
String
text
,
TextStyle
?
style
,
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
,
),
),
],
);
}
@override
final
RegExp
exp
=
RegExp
(
r"^\[(\x?)\]\s(\S.*)$"
);
@override
String
toHtml
(
String
text
)
{
var
match
=
exp
.
firstMatch
(
text
.
trim
());
return
'<p><input type="checkbox"
${("${match?[1]}
" == "x") ? "checked" : ""}>
${MdWidget.toHtml((match?[2]).toString()).trim()}
</p>'
;
}
}
class
RadioButtonMd
extends
BlockMd
{
get
onLinkTab
=>
null
;
@override
Widget
build
(
BuildContext
context
,
String
text
,
TextStyle
?
style
,
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
,
),
),
],
);
}
@override
final
RegExp
exp
=
RegExp
(
r"^\((\x?)\)\s(\S.*)$"
);
@override
String
toHtml
(
String
text
)
{
var
match
=
exp
.
firstMatch
(
text
.
trim
());
return
'<p><input type="radio"
${("${match?[1]}
" == "x") ? "checked" : ""}>
${MdWidget.toHtml((match?[2]).toString().trim())}
</p>'
;
}
}
class
UnOrderedList
extends
BlockMd
{
get
onLinkTab
=>
null
;
@override
Widget
build
(
BuildContext
context
,
String
text
,
TextStyle
?
style
,
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
,
),
),
],
);
}
@override
final
RegExp
exp
=
RegExp
(
r"^(\-|\*)\s([^\n]+)$"
);
@override
String
toHtml
(
String
text
)
{
var
match
=
exp
.
firstMatch
(
text
.
trim
());
return
"<ul><li>
${MdWidget.toHtml((match?[2]).toString())}
</li></ul>"
;
}
}
class
OrderedList
extends
BlockMd
{
@override
final
RegExp
exp
=
RegExp
(
r"^([0-9]+\.)\s([^\n]+)$"
);
get
onLinkTab
=>
null
;
@override
Widget
build
(
BuildContext
context
,
String
text
,
TextStyle
?
style
,
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
,
),
),
],
);
}
@override
String
toHtml
(
String
text
)
{
var
match
=
exp
.
firstMatch
(
text
.
trim
());
return
'<ol start="
${match?[1]}
"><li>
${MdWidget.toHtml((match?[2]).toString())}
</li></ol>'
;
}
}
class
BoldMd
extends
InlineMd
{
@override
final
RegExp
exp
=
RegExp
(
r"^\*{2}(([\S^\*].*)?[\S^\*])\*{2}$"
);
@override
InlineSpan
inlineSpan
(
BuildContext
context
,
String
text
,
TextStyle
?
style
,
final
void
Function
(
String
url
,
String
title
)?
onLinkTab
,
)
{
var
match
=
exp
.
firstMatch
(
text
.
trim
());
return
WidgetSpan
(
// text: "${match?[1]}",
child:
TexText
(
"
${match?[1]}
"
,
style:
style
?.
copyWith
(
fontWeight:
FontWeight
.
bold
)
??
const
TextStyle
(
fontWeight:
FontWeight
.
bold
),
),
style:
style
?.
copyWith
(
fontWeight:
FontWeight
.
bold
)
??
const
TextStyle
(
fontWeight:
FontWeight
.
bold
),
);
}
@override
String
toHtml
(
String
text
)
{
var
match
=
exp
.
firstMatch
(
text
.
trim
());
return
'<b>
${TexText.toHtmlData((match?[1]).toString())}
</b>'
;
}
}
class
ItalicMd
extends
InlineMd
{
@override
final
RegExp
exp
=
RegExp
(
r"^\*{1}(([\S^\*].*)?[\S^\*])\*{1}$"
);
@override
InlineSpan
inlineSpan
(
BuildContext
context
,
String
text
,
TextStyle
?
style
,
final
void
Function
(
String
url
,
String
title
)?
onLinkTab
,
)
{
var
match
=
exp
.
firstMatch
(
text
.
trim
());
return
WidgetSpan
(
child:
TexText
(
"
${match?[1]}
"
,
style:
(
style
??
const
TextStyle
()).
copyWith
(
fontStyle:
FontStyle
.
italic
),
),
style:
(
style
??
const
TextStyle
()).
copyWith
(
fontStyle:
FontStyle
.
italic
),
);
}
@override
String
toHtml
(
String
text
)
{
var
match
=
exp
.
firstMatch
(
text
.
trim
());
return
'<i>
${TexText.toHtmlData((match?[1]).toString())}
</i>'
;
}
}
class
ATagMd
extends
InlineMd
{
@override
final
RegExp
exp
=
RegExp
(
r"^\[([^\s\*].*[^\s]?)?\]\(([^\s\*]+)?\)$"
);
@override
InlineSpan
inlineSpan
(
BuildContext
context
,
String
text
,
TextStyle
?
style
,
final
void
Function
(
String
url
,
String
title
)?
onLinkTab
,
)
{
var
match
=
exp
.
firstMatch
(
text
.
trim
());
if
(
match
?[
1
]
==
null
&&
match
?[
2
]
==
null
)
{
return
const
TextSpan
();
}
return
WidgetSpan
(
child:
GestureDetector
(
onTap:
()
{
if
(
onLinkTab
==
null
)
{
return
;
}
onLinkTab
(
"
${match?[2]}
"
,
"
${match?[1]}
"
);
},
child:
TexText
(
"
${match?[1]}
"
,
style:
(
style
??
const
TextStyle
()).
copyWith
(
color:
Colors
.
blueAccent
,
decorationColor:
Colors
.
blue
,
decoration:
TextDecoration
.
underline
,
),
),
),
);
}
@override
String
toHtml
(
String
text
)
{
var
match
=
exp
.
firstMatch
(
text
.
trim
());
return
'<a href="
${match?[2]}
">
${TexText.toHtmlData((match?[1]).toString())}
</a>'
;
}
}
class
ImageMd
extends
InlineMd
{
@override
final
RegExp
exp
=
RegExp
(
r"^\!\[([^\s].*[^\s]?)?\]\(([^\s]+)\)$"
);
@override
InlineSpan
inlineSpan
(
BuildContext
context
,
String
text
,
TextStyle
?
style
,
final
void
Function
(
String
url
,
String
title
)?
onLinkTab
,
)
{
var
match
=
exp
.
firstMatch
(
text
.
trim
());
double
?
height
;
double
?
width
;
if
(
match
?[
1
]
!=
null
)
{
var
size
=
RegExp
(
r"^([0-9]+)?x?([0-9]+)?"
)
.
firstMatch
(
match
![
1
].
toString
().
trim
());
width
=
double
.
tryParse
(
size
?[
1
]?.
toString
()
??
'a'
);
height
=
double
.
tryParse
(
size
?[
2
]?.
toString
()
??
'a'
);
}
return
WidgetSpan
(
// alignment: PlaceholderAlignment.middle,
child:
SizedBox
(
width:
width
,
height:
height
,
child:
Image
.
network
(
"
${match?[2]}
"
,
fit:
BoxFit
.
fill
,
errorBuilder:
(
context
,
error
,
stackTrace
)
{
return
Placeholder
(
color:
Theme
.
of
(
context
).
colorScheme
.
error
,
child:
Text
(
"
${match?[2]}
\n
$error
"
,
style:
style
,
),
);
},
),
),
);
}
@override
String
toHtml
(
String
text
)
{
var
match
=
exp
.
firstMatch
(
text
.
trim
());
return
'<img src="
${match?[2]}
">'
;
}
}
class
TextMd
extends
InlineMd
{
@override
final
RegExp
exp
=
RegExp
(
".*"
);
@override
InlineSpan
inlineSpan
(
BuildContext
context
,
String
text
,
TextStyle
?
style
,
void
Function
(
String
url
,
String
title
)?
onLinkTab
)
{
return
WidgetSpan
(
child:
TexText
(
text
,
style:
style
,
));
}
@override
String
toHtml
(
String
text
)
{
return
TexText
.
toHtmlData
(
text
);
}
}
...
...
tex_markdown/lib/md_widget.dart
View file @
43eeb3c
import
'package:flutter/material.dart'
;
import
'package:tex_
text/tex_tex
t.dart'
;
import
'package:tex_
markdown/markdown_componen
t.dart'
;
/// It creates a markdown widget closed to each other.
class
MdWidget
extends
StatelessWidget
{
...
...
@@ -9,18 +9,43 @@ class MdWidget extends StatelessWidget {
final
TextStyle
?
style
;
final
void
Function
(
String
url
,
String
title
)?
onLinkTab
;
final
bool
followLinkColor
;
/// To convert markdown text to html text.
static
String
toHtml
(
String
text
)
{
final
RegExp
table
=
RegExp
(
r"^(((\|[^\n\|]+\|)((([^\n\|]+\|)+)?))(\n(((\|[^\n\|]+\|)(([^\n\|]+\|)+)?)))+)?$"
,
);
if
(
table
.
hasMatch
(
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>
'''
;
}
return
MarkdownComponent
.
toHtml
(
text
);
}
@override
Widget
build
(
BuildContext
context
)
{
final
RegExp
h
=
RegExp
(
r"^(#{1,6})\s([^\n]+)$"
);
final
RegExp
b
=
RegExp
(
r"^\*{2}(([\S^\*].*)?[\S^\*])\*{2}$"
);
final
RegExp
i
=
RegExp
(
r"^\*{1}(([\S^\*].*)?[\S^\*])\*{1}$"
);
final
RegExp
a
=
RegExp
(
r"^\[([^\s\*].*[^\s]?)?\]\(([^\s\*]+)?\)$"
);
final
RegExp
img
=
RegExp
(
r"^\!\[([^\s].*[^\s]?)?\]\(([^\s]+)\)$"
);
final
RegExp
ul
=
RegExp
(
r"^(\-|\*)\s([^\n]+)$"
);
final
RegExp
ol
=
RegExp
(
r"^([0-9]+\.)\s([^\n]+)$"
);
final
RegExp
rb
=
RegExp
(
r"^\((\x?)\)\s(\S.*)$"
);
final
RegExp
cb
=
RegExp
(
r"^\[(\x?)\]\s(\S.*)$"
);
final
RegExp
hr
=
RegExp
(
r"^(--)[-]+$"
);
final
RegExp
table
=
RegExp
(
r"^(((\|[^\n\|]+\|)((([^\n\|]+\|)+)?))(\n(((\|[^\n\|]+\|)(([^\n\|]+\|)+)?)))+)?$"
,
);
...
...
@@ -42,7 +67,6 @@ class MdWidget extends StatelessWidget {
}
}
if
(
maxCol
==
0
)
{
// return const SizedBox();
return
Text
(
""
,
style:
style
);
}
return
Table
(
...
...
@@ -69,256 +93,6 @@ class MdWidget extends StatelessWidget {
.
toList
(),
);
}
if
(
h
.
hasMatch
(
exp
))
{
var
match
=
h
.
firstMatch
(
exp
.
trim
());
return
Column
(
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
],
),
),
],
),
if
(
match
[
1
]!.
length
==
1
)
const
Divider
(
height:
6
),
],
);
}
if
(
hr
.
hasMatch
(
exp
))
{
return
const
Divider
(
height:
5
,
);
}
if
(
cb
.
hasMatch
(
exp
))
{
var
match
=
cb
.
firstMatch
(
exp
.
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
,
),
),
],
);
}
if
(
rb
.
hasMatch
(
exp
))
{
var
match
=
rb
.
firstMatch
(
exp
.
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
,
),
),
],
);
}
if
(
ul
.
hasMatch
(
exp
))
{
var
match
=
ul
.
firstMatch
(
exp
.
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
,
),
),
],
);
}
if
(
ol
.
hasMatch
(
exp
))
{
var
match
=
ol
.
firstMatch
(
exp
.
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
,
),
),
],
);
}
if
(
b
.
hasMatch
(
exp
))
{
var
match
=
b
.
firstMatch
(
exp
.
trim
());
return
TexText
(
"
${match?[1]}
"
,
style:
style
?.
copyWith
(
fontWeight:
FontWeight
.
bold
)
??
const
TextStyle
(
fontWeight:
FontWeight
.
bold
),
);
}
if
(
i
.
hasMatch
(
exp
))
{
var
match
=
i
.
firstMatch
(
exp
.
trim
());
return
TexText
(
"
${match?[1]}
"
,
style:
(
style
??
const
TextStyle
()).
copyWith
(
fontStyle:
FontStyle
.
italic
),
);
}
if
(
a
.
hasMatch
(
exp
))
{
var
match
=
a
.
firstMatch
(
exp
.
trim
());
if
(
match
?[
1
]
==
null
&&
match
?[
2
]
==
null
)
{
return
const
SizedBox
();
}
return
GestureDetector
(
onTap:
()
{
if
(
onLinkTab
==
null
)
{
return
;
}
onLinkTab
!(
"
${match?[2]}
"
,
"
${match?[1]}
"
);
},
child:
MdWidget
(
"
${match?[1]}
"
,
onLinkTab:
onLinkTab
,
style:
((
followLinkColor
&&
style
!=
null
)
?
style
:
const
TextStyle
(
color:
Colors
.
blueAccent
))
?.
copyWith
(
decoration:
TextDecoration
.
underline
),
),
);
}
if
(
img
.
hasMatch
(
exp
))
{
var
match
=
img
.
firstMatch
(
exp
.
trim
());
double
?
height
;
double
?
width
;
if
(
match
?[
1
]
!=
null
)
{
var
size
=
RegExp
(
r"^([0-9]+)?x?([0-9]+)?"
)
.
firstMatch
(
match
![
1
].
toString
().
trim
());
width
=
double
.
tryParse
(
size
?[
1
]?.
toString
()
??
'a'
);
height
=
double
.
tryParse
(
size
?[
2
]?.
toString
()
??
'a'
);
}
return
SizedBox
(
width:
width
,
height:
height
,
child:
Image
.
network
(
"
${match?[2]}
"
,
fit:
BoxFit
.
fill
,
errorBuilder:
(
context
,
error
,
stackTrace
)
{
return
Placeholder
(
color:
Theme
.
of
(
context
).
colorScheme
.
error
,
child:
Text
(
"
${match?[2]}
\n
$error
"
,
style:
style
,
),
);
},
),
);
}
List
<
String
>
expL
=
exp
.
split
(
'
\n
'
);
// .map(
// (e) => e.trim(),
// )
// .toList();
bool
hasMatch
=
false
;
for
(
final
each
in
expL
)
{
if
(
a
.
hasMatch
(
each
)
||
b
.
hasMatch
(
each
)
||
i
.
hasMatch
(
each
)
||
h
.
hasMatch
(
each
)
||
hr
.
hasMatch
(
each
)
||
ol
.
hasMatch
(
each
)
||
rb
.
hasMatch
(
each
)
||
cb
.
hasMatch
(
each
)
||
img
.
hasMatch
(
each
)
||
ul
.
hasMatch
(
each
))
{
hasMatch
=
true
;
}
}
if
(
hasMatch
)
{
return
Wrap
(
crossAxisAlignment:
WrapCrossAlignment
.
center
,
spacing:
10
,
children:
exp
.
split
(
"
\n
"
)
.
map
<
Widget
>((
e
)
=>
MdWidget
(
e
,
onLinkTab:
onLinkTab
,
style:
style
,
))
.
toList
(),
);
}
return
TexText
(
exp
,
style:
style
,
);
return
MarkdownComponent
.
generate
(
context
,
exp
,
style
,
onLinkTab
);
}
}
...
...
tex_markdown/lib/tex_markdown.dart
View file @
43eeb3c
...
...
@@ -17,6 +17,13 @@ class TexMarkdown extends StatelessWidget {
final
TextStyle
?
style
;
final
void
Function
(
String
url
,
String
title
)?
onLinkTab
;
final
bool
followLinkColor
;
static
String
toHtml
(
String
text
)
{
String
html
=
""
;
text
.
trim
().
split
(
RegExp
(
r"\n\n+"
)).
forEach
((
element
)
{
html
+=
MdWidget
.
toHtml
(
element
);
});
return
html
;
}
@override
Widget
build
(
BuildContext
context
)
{
...
...
tex_markdown/pubspec.yaml
View file @
43eeb3c
name
:
tex_markdown
description
:
This package is used to create flutter widget that can render markdown and latex formulas. It is very simple to use and uses native flutter components.
version
:
0.0.
6
version
:
0.0.
9
homepage
:
https://github.com/saminsohag/flutter_packages/tree/main/tex_markdown
environment
:
...
...
@@ -10,7 +10,7 @@ environment:
dependencies
:
flutter
:
sdk
:
flutter
tex_text
:
^0.
0.8
tex_text
:
^0.
1.2
dev_dependencies
:
flutter_test
:
...
...
Please
register
or
login
to post a comment