audit static method
Audit the current page and print issues to console
Implementation
static List<SeoAuditResult> audit() {
if (!Webify.isInitialized) {
return [
const SeoAuditResult(
severity: AuditSeverity.error,
rule: 'initialization',
message: 'Webify is not initialized. Call Webify.initialize() first.',
),
];
}
final results = <SeoAuditResult>[];
final webify = Webify.instance;
final meta = webify.currentMeta;
// Check title
if (webify.currentTitle == null || webify.currentTitle!.isEmpty) {
results.add(
const SeoAuditResult(
severity: AuditSeverity.error,
rule: 'title-missing',
message: 'Page title is missing. Every page must have a <title>.',
fix: 'Add title parameter to SeoHead or SeoPage widget.',
),
);
} else if (webify.currentTitle!.length > 60) {
results.add(
SeoAuditResult(
severity: AuditSeverity.warning,
rule: 'title-too-long',
message:
'Title is ${webify.currentTitle!.length} chars (recommended: ≤60).',
fix: 'Shorten your title for better display in search results.',
),
);
}
// Check description
final description = meta['description'];
if (description == null || description.isEmpty) {
results.add(
const SeoAuditResult(
severity: AuditSeverity.error,
rule: 'description-missing',
message: 'Meta description is missing.',
fix: 'Add description parameter to SeoHead or SeoPage widget.',
),
);
} else if (description.length > 160) {
results.add(
SeoAuditResult(
severity: AuditSeverity.warning,
rule: 'description-too-long',
message:
'Description is ${description.length} chars (recommended: ≤160).',
fix: 'Shorten your description. Google truncates after ~160 chars.',
),
);
} else if (description.length < 50) {
results.add(
const SeoAuditResult(
severity: AuditSeverity.info,
rule: 'description-too-short',
message: 'Description is very short (recommended: 50-160 chars).',
fix:
'Write a more detailed description for better click-through rates.',
),
);
}
// Check OG tags
if (!meta.containsKey('og:title')) {
results.add(
const SeoAuditResult(
severity: AuditSeverity.warning,
rule: 'og-title-missing',
message: 'og:title is missing. Social shares won\'t show a title.',
fix: 'SeoHead automatically sets og:title from the title parameter.',
),
);
}
if (!meta.containsKey('og:image')) {
results.add(
const SeoAuditResult(
severity: AuditSeverity.warning,
rule: 'og-image-missing',
message:
'og:image is missing. Social shares won\'t show a preview image.',
fix: 'Add ogImage parameter or ogTags with image to SeoHead.',
),
);
}
// Check Twitter card
if (!meta.containsKey('twitter:card')) {
results.add(
const SeoAuditResult(
severity: AuditSeverity.info,
rule: 'twitter-card-missing',
message: 'Twitter card meta is missing.',
fix: 'Add twitterCard parameter to SeoHead for Twitter/X previews.',
),
);
}
// Check canonical
// Note: We can't easily read canonical from our tracking,
// but we can check if it was set through meta
if (!meta.containsKey('og:url')) {
results.add(
const SeoAuditResult(
severity: AuditSeverity.info,
rule: 'canonical-missing',
message: 'Canonical URL may not be set.',
fix: 'Add canonicalUrl parameter to SeoHead or SeoPage.',
),
);
}
// Renderer warning
if (webify.platform.isWeb) {
final renderer = webify.platform.rendererType;
if (renderer == RendererType.canvasKit) {
results.add(
const SeoAuditResult(
severity: AuditSeverity.info,
rule: 'canvaskit-renderer',
message:
'CanvasKit renderer detected. All visual content is invisible '
'to crawlers. Meta tags and JSON-LD from webify are your '
'only SEO signals.',
),
);
}
}
// Print results in debug mode
if (Webify.instance.config.debugMode) {
_printResults(results);
}
return results;
}