Skip to content

Commit

Permalink
[CHORE] add Seo.html (#98)
Browse files Browse the repository at this point in the history
  • Loading branch information
krokyze authored Jan 26, 2025
1 parent 5848e7c commit 1e058d4
Show file tree
Hide file tree
Showing 15 changed files with 120 additions and 20 deletions.
2 changes: 1 addition & 1 deletion .fvmrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"flutter": "3.27.2",
"flutter": "3.27.3",
"flavors": {}
}
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,6 @@ app.*.symbols

# FVM Version Cache
.fvm/

# Todo
todo
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.0.10

* Added Seo.html() for raw html.

## 0.0.9

* Add support for Wasm.
Expand Down
12 changes: 10 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Flutter package for enabling SEO (meta, body tag) support on Web. The package li
To use this plugin, add `seo` as a [dependency in your pubspec.yaml file](https://flutter.io/platform-plugins/).
```yaml
dependencies:
seo: ^0.0.9
seo: ^0.0.10
```
Use `usePathUrlStrategy()` to ensure that Google recognizes each URL as a distinct page. Failure to do so may result in Google perceiving all URLs as the same page. For additional details, refer to [this video](https://www.youtube.com/watch?v=vow-m6R-YHo).
Expand Down Expand Up @@ -50,7 +50,7 @@ class App extends StatelessWidget {
 
There's two available SeoTree implementations:
* **WidgetTree (recommended)** - based on traversing widget tree, while it's bit slower than SemanticsTree it's production ready and doesn't have any blocking Flutter SDK issues.
* **SemanticsTree (`experimental`)** - based on traversing semantic data node tree. Does traverse the tree faster but doesn't support `Seo.head(...)`, `Seo.text(style: ...)`, `Seo.link(rel: ...)`
* **SemanticsTree (`experimental`)** - based on traversing semantic data node tree. Does traverse the tree faster but doesn't support `Seo.head(...)`, `Seo.text(style: ...)`, `Seo.link(rel: ...)`, `Seo.html(html: ...)`

 
## Sample Usage
Expand Down Expand Up @@ -89,6 +89,14 @@ Seo.link(
); // converts to: <a href="http://www.example.com" rel="nofollow"><p>Some example</p></a>
```

#### Html
```dart
Seo.html(
html: '<div>Some raw html</div>',
child: ...,
); // converts to: <div>Some raw html</div>
```

#### Head
```dart
Seo.head(
Expand Down
6 changes: 6 additions & 0 deletions lib/html/seo_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@ class Seo extends StatelessWidget {
required this.child,
}) : tag = LinkTag(anchor: anchor, href: href, rel: rel);

Seo.html({
super.key,
required String html,
required this.child,
}) : tag = HtmlTag(html: html);

Seo.head({
super.key,
required List<head_tag.HeadTag> tags,
Expand Down
10 changes: 5 additions & 5 deletions lib/html/tree/semantics_tree.dart
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class SemanticsTree extends SeoTree {
container: true,
child: child,
);
} else if (tag is HeadTags) {
} else if (tag is HtmlTag || tag is HeadTags) {
// Semantics doesn't support a way to pass head tag info to SemanticsNode
return child;
}
Expand Down Expand Up @@ -148,7 +148,7 @@ class _Node with SeoTreeNode {

if (_link) {
return html.copyWith(
body: link(
body: linkTag(
anchor: parent.label,
href: parent.value,
rel: null,
Expand All @@ -157,7 +157,7 @@ class _Node with SeoTreeNode {
);
} else if (_image) {
return html.copyWith(
body: image(
body: imageTag(
src: parent.value,
alt: parent.label,
height: parent.rect.height,
Expand All @@ -167,15 +167,15 @@ class _Node with SeoTreeNode {
);
} else if (_text) {
return html.copyWith(
body: text(
body: textTag(
text: parent.label,
style: TextTagStyle.p,
content: html.body,
),
);
} else {
return html.copyWith(
body: div(
body: divTag(
content: html.body,
),
);
Expand Down
19 changes: 14 additions & 5 deletions lib/html/tree/widget_tree.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ class _Node with SeoTreeNode {
return 'image: ${tag.alt} | url: ${tag.src}';
} else if (tag is LinkTag) {
return 'link: ${tag.anchor} | url: ${tag.href} | rel: ${tag.rel}';
} else if (tag is HtmlTag) {
return 'html: ${tag.html}';
} else if (tag is HeadTags) {
return 'head: ${tag.tags.length}';
} else {
Expand All @@ -93,15 +95,15 @@ class _Node with SeoTreeNode {

if (tag is TextTag) {
return html.copyWith(
body: text(
body: textTag(
text: tag.text,
style: tag.style,
content: html.body,
),
);
} else if (tag is ImageTag) {
return html.copyWith(
body: image(
body: imageTag(
src: tag.src,
alt: tag.alt,
height: parent?.size?.height,
Expand All @@ -111,20 +113,27 @@ class _Node with SeoTreeNode {
);
} else if (tag is LinkTag) {
return html.copyWith(
body: link(
body: linkTag(
anchor: tag.anchor,
href: tag.href,
rel: tag.rel,
content: html.body,
),
);
} else if (tag is HtmlTag) {
return html.copyWith(
body: htmlTag(
html: tag.html,
content: html.body,
),
);
} else if (tag is HeadTags) {
return html.copyWith(
head: html.head + tag.tags.map((tag) => head(tag: tag)).join('\n'),
head: html.head + tag.tags.map((tag) => headTag(tag: tag)).join('\n'),
);
} else {
return html.copyWith(
body: div(
body: divTag(
content: html.body,
),
);
Expand Down
7 changes: 7 additions & 0 deletions lib/io/seo_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,13 @@ class Seo extends StatelessWidget {
super.key,
required String anchor,
required String href,
String? rel,
required this.child,
});

const Seo.html({
super.key,
required String html,
required this.child,
});

Expand Down
8 changes: 8 additions & 0 deletions lib/src/seo_tag.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ class LinkTag extends SeoTag {
});
}

class HtmlTag extends SeoTag {
final String html;

const HtmlTag({
required this.html,
});
}

class HeadTags extends SeoTag {
final List<HeadTag> tags;

Expand Down
17 changes: 12 additions & 5 deletions lib/src/seo_tree.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ mixin SeoTreeNode {

SeoHtml toHtml();

String text({
String textTag({
required String text,
required TextTagStyle style,
required String content,
Expand All @@ -37,7 +37,7 @@ mixin SeoTreeNode {
return '<$tag style="color:black;">$text</$tag>$content';
}

String image({
String imageTag({
required String src,
required String alt,
required double? height,
Expand All @@ -47,7 +47,7 @@ mixin SeoTreeNode {
return '<noscript><img src="$src" alt="$alt" height="$height" width="$width"/></noscript>$content';
}

String link({
String linkTag({
required String anchor,
required String href,
required String? rel,
Expand All @@ -65,7 +65,14 @@ mixin SeoTreeNode {
return '<div><a $attributes><p>$anchor</p></a>$content</div>';
}

String head({
String htmlTag({
required String html,
required String content,
}) {
return '$html$content';
}

String headTag({
required head_tag.HeadTag tag,
}) {
if (tag is head_tag.MetaTag) {
Expand Down Expand Up @@ -100,7 +107,7 @@ mixin SeoTreeNode {
throw UnimplementedError('unsupported tag: $tag');
}

String div({
String divTag({
required String content,
}) {
return '<div>$content</div>';
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: seo
description: Flutter package for enabling SEO (meta, body tag) support on Web.
version: 0.0.9
version: 0.0.10
homepage: https://github.com/krokyze/flutter_seo

environment:
Expand Down
3 changes: 3 additions & 0 deletions test/const.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,6 @@ const rel = 'alternate';
const type = 'application/rss+xml';
const media = 'only screen and (max-device-width: 480px)';
const hreflang = 'en';

// html
const html = '<div>Lorem Ipsum</div>';
14 changes: 14 additions & 0 deletions test/semantics_tree_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:seo/src/seo_tag.dart';
import 'base.dart';
import 'const.dart';
import 'widgets/test_seo_controller.dart';
import 'widgets/test_seo_html.dart';
import 'widgets/test_seo_image.dart';
import 'widgets/test_seo_link.dart';
import 'widgets/test_seo_page.dart';
Expand Down Expand Up @@ -70,6 +71,19 @@ void main() {
);
});

testWidgets('Seo.html isn\'t processed', (tester) async {
await tester.pumpWidget(TestSeoController(
tree: (_) => SemanticsTree(),
child: const TestSeoHtml(),
));
await tester.pumpAndSettle(debounceTime);

expect(
bodyHtml,
'<div></div>',
);
});

testWidgets('multiple Seo\'s are processed correctly', (tester) async {
await tester.pumpWidget(TestSeoController(
tree: (_) => SemanticsTree(),
Expand Down
17 changes: 16 additions & 1 deletion test/widget_tree_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'base.dart';
import 'const.dart';
import 'widgets/test_seo_controller.dart';
import 'widgets/test_seo_head.dart';
import 'widgets/test_seo_html.dart';
import 'widgets/test_seo_image.dart';
import 'widgets/test_seo_link.dart';
import 'widgets/test_seo_page.dart';
Expand Down Expand Up @@ -71,6 +72,19 @@ void main() {
);
});

testWidgets('Seo.html is processed correctly', (tester) async {
await tester.pumpWidget(TestSeoController(
tree: (context) => WidgetTree(context: context),
child: const TestSeoHtml(),
));
await tester.pumpAndSettle(debounceTime);

expect(
bodyHtml,
'<div>$html</div>',
);
});

testWidgets('multiple Seo\'s are processed correctly', (tester) async {
await tester.pumpWidget(TestSeoController(
tree: (context) => WidgetTree(context: context),
Expand All @@ -80,6 +94,7 @@ void main() {
children: [
TestSeoImage(),
TestSeoText(),
TestSeoHtml(),
],
),
),
Expand All @@ -101,7 +116,7 @@ void main() {

expect(
bodyHtml,
'<div><div><a href="$href"><p>$anchor</p></a><noscript><img src="$src" alt="$alt" height="$height" width="$width"/></noscript><p style="color:black;">$text</p></div></div>',
'<div><div><a href="$href"><p>$anchor</p></a><noscript><img src="$src" alt="$alt" height="$height" width="$width"/></noscript><p style="color:black;">$text</p>$html</div></div>',
);
});

Expand Down
16 changes: 16 additions & 0 deletions test/widgets/test_seo_html.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import 'package:flutter/material.dart';
import 'package:seo/seo.dart';

import '../const.dart';

class TestSeoHtml extends StatelessWidget {
const TestSeoHtml({super.key});

@override
Widget build(BuildContext context) {
return Seo.html(
html: html,
child: const SizedBox.square(dimension: 1),
);
}
}

0 comments on commit 1e058d4

Please sign in to comment.