Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
F
Front-End
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
abdullh.alsoleman
Front-End
Commits
800e2558
Commit
800e2558
authored
Mar 10, 2016
by
Viktor Lidholt
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Initial support for links in markdown
parent
3d8176ed
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
124 additions
and
37 deletions
+124
-37
demo.dart
packages/flutter_markdown/example/demo.dart
+3
-0
markdown.dart
packages/flutter_markdown/lib/src/markdown.dart
+12
-6
markdown_raw.dart
packages/flutter_markdown/lib/src/markdown_raw.dart
+94
-20
flutter_markdown_test.dart
packages/flutter_markdown/test/flutter_markdown_test.dart
+15
-11
No files found.
packages/flutter_markdown/example/demo.dart
View file @
800e2558
...
...
@@ -15,6 +15,9 @@ Style text as _italic_, __bold__, or `inline code`.
- To better clarify
- Your points
## Links
You can use [hyperlinks](hyperlink) in markdown
## Code blocks
Formatted Dart code looks really pretty too. This is an example of how to create your own Markdown widget:
...
...
packages/flutter_markdown/lib/src/markdown.dart
View file @
800e2558
...
...
@@ -25,22 +25,26 @@ class Markdown extends MarkdownRaw {
Markdown
({
String
data
,
SyntaxHighlighter
syntaxHighlighter
,
MarkdownStyle
markdownStyle
MarkdownStyle
markdownStyle
,
MarkdownLinkCallback
onTapLink
})
:
super
(
data:
data
,
syntaxHighlighter:
syntaxHighlighter
,
markdownStyle:
markdownStyle
markdownStyle:
markdownStyle
,
onTapLink:
onTapLink
);
MarkdownBody
createMarkdownBody
({
String
data
,
MarkdownStyle
markdownStyle
,
SyntaxHighlighter
syntaxHighlighter
SyntaxHighlighter
syntaxHighlighter
,
MarkdownLinkCallback
onTapLink
})
{
return
new
MarkdownBody
(
data:
data
,
markdownStyle:
markdownStyle
,
syntaxHighlighter:
syntaxHighlighter
syntaxHighlighter:
syntaxHighlighter
,
onTapLink:
onTapLink
);
}
}
...
...
@@ -71,11 +75,13 @@ class MarkdownBody extends MarkdownBodyRaw {
MarkdownBody
({
String
data
,
SyntaxHighlighter
syntaxHighlighter
,
MarkdownStyle
markdownStyle
MarkdownStyle
markdownStyle
,
MarkdownLinkCallback
onTapLink
})
:
super
(
data:
data
,
syntaxHighlighter:
syntaxHighlighter
,
markdownStyle:
markdownStyle
markdownStyle:
markdownStyle
,
onTapLink:
onTapLink
);
MarkdownStyle
createDefaultStyle
(
BuildContext
context
)
{
...
...
packages/flutter_markdown/lib/src/markdown_raw.dart
View file @
800e2558
...
...
@@ -4,8 +4,11 @@
import
'package:markdown/markdown.dart'
as
md
;
import
'package:flutter/widgets.dart'
;
import
'package:flutter/gestures.dart'
;
import
'markdown_style_raw.dart'
;
typedef
void
MarkdownLinkCallback
(
String
href
);
/// A [Widget] that renders markdown formatted text. It supports all standard
/// markdowns from the original markdown specification found here:
...
...
@@ -26,7 +29,8 @@ class MarkdownRaw extends StatelessComponent {
this
.
data
,
this
.
markdownStyle
,
this
.
syntaxHighlighter
,
this
.
padding
:
const
EdgeDims
.
all
(
16.0
)
this
.
padding
:
const
EdgeDims
.
all
(
16.0
),
this
.
onTapLink
});
/// Markdown styled text
...
...
@@ -41,6 +45,9 @@ class MarkdownRaw extends StatelessComponent {
/// Padding used
final
EdgeDims
padding
;
/// Callback when a link is tapped
final
MarkdownLinkCallback
onTapLink
;
Widget
build
(
BuildContext
context
)
{
return
new
ScrollableViewport
(
child:
new
Padding
(
...
...
@@ -48,7 +55,8 @@ class MarkdownRaw extends StatelessComponent {
child:
createMarkdownBody
(
data:
data
,
markdownStyle:
markdownStyle
,
syntaxHighlighter:
syntaxHighlighter
syntaxHighlighter:
syntaxHighlighter
,
onTapLink:
onTapLink
)
)
);
...
...
@@ -57,12 +65,14 @@ class MarkdownRaw extends StatelessComponent {
MarkdownBodyRaw
createMarkdownBody
({
String
data
,
MarkdownStyleRaw
markdownStyle
,
SyntaxHighlighter
syntaxHighlighter
SyntaxHighlighter
syntaxHighlighter
,
MarkdownLinkCallback
onTapLink
})
{
return
new
MarkdownBodyRaw
(
data:
data
,
markdownStyle:
markdownStyle
,
syntaxHighlighter:
syntaxHighlighter
syntaxHighlighter:
syntaxHighlighter
,
onTapLink:
onTapLink
);
}
}
...
...
@@ -94,7 +104,8 @@ class MarkdownBodyRaw extends StatefulComponent {
MarkdownBodyRaw
({
this
.
data
,
this
.
markdownStyle
,
this
.
syntaxHighlighter
this
.
syntaxHighlighter
,
this
.
onTapLink
});
/// Markdown styled text
...
...
@@ -106,6 +117,9 @@ class MarkdownBodyRaw extends StatefulComponent {
/// The syntax highlighter used to color text in code blocks
final
SyntaxHighlighter
syntaxHighlighter
;
/// Callback when a link is tapped
final
MarkdownLinkCallback
onTapLink
;
_MarkdownBodyRawState
createState
()
=>
new
_MarkdownBodyRawState
();
MarkdownStyleRaw
createDefaultStyle
(
BuildContext
context
)
=>
null
;
...
...
@@ -119,10 +133,23 @@ class _MarkdownBodyRawState extends State<MarkdownBodyRaw> {
MarkdownStyleRaw
markdownStyle
=
config
.
markdownStyle
??
config
.
createDefaultStyle
(
context
);
SyntaxHighlighter
syntaxHighlighter
=
config
.
syntaxHighlighter
??
new
_DefaultSyntaxHighlighter
(
markdownStyle
.
code
);
_cachedBlocks
=
_blocksFromMarkup
(
config
.
data
,
markdownStyle
,
syntaxHighlighter
);
_linkHandler
=
new
_LinkHandler
(
config
.
onTapLink
);
// TODO: This can be optimized by doing the split and removing \r at the same time
List
<
String
>
lines
=
config
.
data
.
replaceAll
(
'
\r\n
'
,
'
\n
'
).
split
(
'
\n
'
);
md
.
Document
document
=
new
md
.
Document
();
_Renderer
renderer
=
new
_Renderer
();
_cachedBlocks
=
renderer
.
render
(
document
.
parseLines
(
lines
),
markdownStyle
,
syntaxHighlighter
,
_linkHandler
);
}
void
dispose
()
{
_linkHandler
.
dispose
();
super
.
dispose
();
}
List
<
_Block
>
_cachedBlocks
;
_LinkHandler
_linkHandler
;
Widget
build
(
BuildContext
context
)
{
List
<
Widget
>
blocks
=
<
Widget
>[];
...
...
@@ -137,23 +164,15 @@ class _MarkdownBodyRawState extends State<MarkdownBodyRaw> {
}
}
List
<
_Block
>
_blocksFromMarkup
(
String
data
,
MarkdownStyleRaw
markdownStyle
,
SyntaxHighlighter
syntaxHighlighter
)
{
// TODO: This can be optimized by doing the split and removing \r at the same time
List
<
String
>
lines
=
data
.
replaceAll
(
'
\r\n
'
,
'
\n
'
).
split
(
'
\n
'
);
md
.
Document
document
=
new
md
.
Document
();
_Renderer
renderer
=
new
_Renderer
();
return
renderer
.
render
(
document
.
parseLines
(
lines
),
markdownStyle
,
syntaxHighlighter
);
}
class
_Renderer
implements
md
.
NodeVisitor
{
List
<
_Block
>
render
(
List
<
md
.
Node
>
nodes
,
MarkdownStyleRaw
markdownStyle
,
SyntaxHighlighter
syntaxHighlighter
)
{
List
<
_Block
>
render
(
List
<
md
.
Node
>
nodes
,
MarkdownStyleRaw
markdownStyle
,
SyntaxHighlighter
syntaxHighlighter
,
_LinkHandler
linkHandler
)
{
assert
(
markdownStyle
!=
null
);
_blocks
=
<
_Block
>[];
_listIndents
=
<
String
>[];
_markdownStyle
=
markdownStyle
;
_syntaxHighlighter
=
syntaxHighlighter
;
_linkHandler
=
linkHandler
;
for
(
final
md
.
Node
node
in
nodes
)
{
node
.
accept
(
this
);
...
...
@@ -166,6 +185,7 @@ class _Renderer implements md.NodeVisitor {
List
<
String
>
_listIndents
;
MarkdownStyleRaw
_markdownStyle
;
SyntaxHighlighter
_syntaxHighlighter
;
_LinkHandler
_linkHandler
;
void
visitText
(
md
.
Text
text
)
{
_MarkdownNodeList
topList
=
_currentBlock
.
stack
.
last
;
...
...
@@ -191,8 +211,13 @@ class _Renderer implements md.NodeVisitor {
_Block
newBlock
=
new
_Block
(
element
.
tag
,
element
.
attributes
,
_markdownStyle
,
new
List
<
String
>.
from
(
_listIndents
),
blockList
.
length
);
blockList
.
add
(
newBlock
);
}
else
{
_LinkInfo
linkInfo
=
null
;
if
(
element
.
tag
==
'a'
)
{
linkInfo
=
_linkHandler
.
createLinkInfo
(
element
.
attributes
[
'href'
]);
}
TextStyle
style
=
_markdownStyle
.
styles
[
element
.
tag
]
??
new
TextStyle
();
List
<
_MarkdownNode
>
styleElement
=
<
_MarkdownNode
>[
new
_MarkdownNodeTextStyle
(
style
)];
List
<
_MarkdownNode
>
styleElement
=
<
_MarkdownNode
>[
new
_MarkdownNodeTextStyle
(
style
,
linkInfo
)];
_currentBlock
.
stack
.
add
(
new
_MarkdownNodeList
(
styleElement
));
}
return
true
;
...
...
@@ -260,8 +285,9 @@ class _MarkdownNodeList extends _MarkdownNode {
}
class
_MarkdownNodeTextStyle
extends
_MarkdownNode
{
_MarkdownNodeTextStyle
(
this
.
style
);
_MarkdownNodeTextStyle
(
this
.
style
,
[
this
.
linkInfo
=
null
]
);
TextStyle
style
;
_LinkInfo
linkInfo
;
}
class
_MarkdownNodeString
extends
_MarkdownNode
{
...
...
@@ -325,7 +351,8 @@ class _Block {
children:
subWidgets
);
}
else
{
contents
=
new
RichText
(
text:
_stackToTextSpan
(
new
_MarkdownNodeList
(
stack
)));
TextSpan
span
=
_stackToTextSpan
(
new
_MarkdownNodeList
(
stack
));
contents
=
new
RichText
(
text:
span
);
if
(
listIndents
.
length
>
0
)
{
Widget
bullet
;
...
...
@@ -384,13 +411,23 @@ class _Block {
if
(
stack
is
_MarkdownNodeList
)
{
List
<
_MarkdownNode
>
list
=
stack
.
list
;
_MarkdownNodeTextStyle
styleNode
=
list
[
0
];
_LinkInfo
linkInfo
=
styleNode
.
linkInfo
;
TextStyle
style
=
styleNode
.
style
;
List
<
TextSpan
>
children
=
<
TextSpan
>[];
for
(
int
i
=
1
;
i
<
list
.
length
;
i
++)
{
children
.
add
(
_stackToTextSpan
(
list
[
i
]));
}
return
new
TextSpan
(
style:
style
,
children:
children
);
String
text
=
null
;
if
(
children
.
length
==
1
&&
_isPlainText
(
children
[
0
]))
{
text
=
children
[
0
].
text
;
children
=
null
;
}
TapGestureRecognizer
recognizer
=
linkInfo
?.
recognizer
;
return
new
TextSpan
(
style:
style
,
children:
children
,
recognizer:
recognizer
,
text:
text
);
}
if
(
stack
is
_MarkdownNodeString
)
{
...
...
@@ -400,6 +437,10 @@ class _Block {
return
null
;
}
bool
_isPlainText
(
TextSpan
span
)
{
return
(
span
.
text
!=
null
&&
span
.
style
==
null
&&
span
.
recognizer
==
null
&&
span
.
children
==
null
);
}
Widget
_buildImage
(
BuildContext
context
,
String
src
)
{
List
<
String
>
parts
=
src
.
split
(
'#'
);
if
(
parts
.
length
==
0
)
return
new
Container
();
...
...
@@ -419,6 +460,39 @@ class _Block {
}
}
class
_LinkInfo
{
_LinkInfo
(
this
.
href
,
this
.
recognizer
);
final
String
href
;
final
TapGestureRecognizer
recognizer
;
}
class
_LinkHandler
{
_LinkHandler
(
this
.
onTapLink
);
List
<
_LinkInfo
>
links
=
<
_LinkInfo
>[];
MarkdownLinkCallback
onTapLink
;
_LinkInfo
createLinkInfo
(
String
href
)
{
TapGestureRecognizer
recognizer
=
new
TapGestureRecognizer
();
recognizer
.
onTap
=
()
{
if
(
onTapLink
!=
null
)
onTapLink
(
href
);
};
_LinkInfo
linkInfo
=
new
_LinkInfo
(
href
,
recognizer
);
links
.
add
(
linkInfo
);
return
linkInfo
;
}
void
dispose
()
{
for
(
_LinkInfo
linkInfo
in
links
)
{
linkInfo
.
recognizer
.
dispose
();
}
}
}
abstract
class
SyntaxHighlighter
{
TextSpan
format
(
String
source
);
}
...
...
packages/flutter_markdown/test/flutter_markdown_test.dart
View file @
800e2558
import
'package:flutter_markdown/flutter_markdown.dart'
;
import
'package:flutter_test/flutter_test.dart'
;
import
'package:flutter/gestures.dart'
;
import
'package:flutter/widgets.dart'
;
import
'package:test/test.dart'
;
import
'package:flutter/material.dart'
;
...
...
@@ -9,13 +10,9 @@ void main() {
testWidgets
((
WidgetTester
tester
)
{
tester
.
pumpWidget
(
new
MarkdownBody
(
data:
"Hello"
));
Element
textElement
=
tester
.
findElement
((
Element
element
)
=>
element
.
widget
is
RichText
);
RichText
textWidget
=
textElement
.
widget
;
TextSpan
textSpan
=
textWidget
.
text
;
List
<
Element
>
elements
=
_listElements
(
tester
);
_expectWidgetTypes
(
elements
,
<
Type
>[
MarkdownBody
,
Column
,
Container
,
Padding
,
RichText
]);
expect
(
textSpan
.
children
[
0
].
text
,
equals
(
"Hello"
)
);
_expectTextStrings
(
elements
,
<
String
>[
"Hello"
]
);
});
});
...
...
@@ -23,13 +20,9 @@ void main() {
testWidgets
((
WidgetTester
tester
)
{
tester
.
pumpWidget
(
new
MarkdownBody
(
data:
"# Header"
));
Element
textElement
=
tester
.
findElement
((
Element
element
)
=>
element
.
widget
is
RichText
);
RichText
textWidget
=
textElement
.
widget
;
TextSpan
textSpan
=
textWidget
.
text
;
List
<
Element
>
elements
=
_listElements
(
tester
);
_expectWidgetTypes
(
elements
,
<
Type
>[
MarkdownBody
,
Column
,
Container
,
Padding
,
RichText
]);
expect
(
textSpan
.
children
[
0
].
text
,
equals
(
"Header"
)
);
_expectTextStrings
(
elements
,
<
String
>[
"Header"
]
);
});
});
...
...
@@ -79,7 +72,6 @@ void main() {
tester
.
pumpWidget
(
new
Markdown
(
data:
""
));
List
<
Element
>
elements
=
_listElements
(
tester
);
for
(
Element
element
in
elements
)
print
(
"e:
$element
"
);
_expectWidgetTypes
(
elements
,
<
Type
>[
Markdown
,
ScrollableViewport
,
...
...
@@ -90,6 +82,18 @@ void main() {
]);
});
});
test
(
"Links"
,
()
{
testWidgets
((
WidgetTester
tester
)
{
tester
.
pumpWidget
(
new
Markdown
(
data:
"[Link Text](href)"
));
Element
textElement
=
tester
.
findElement
((
Element
element
)
=>
element
.
widget
is
RichText
);
RichText
textWidget
=
textElement
.
widget
;
TextSpan
span
=
textWidget
.
text
;
expect
(
span
.
children
[
0
].
recognizer
.
runtimeType
,
equals
(
TapGestureRecognizer
));
});
});
}
List
<
Element
>
_listElements
(
WidgetTester
tester
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment