From 2b0e5671f55d9c64a1d88a1e7d7a9ccc3bc4a164 Mon Sep 17 00:00:00 2001 From: Parker Lougheed Date: Tue, 21 Apr 2026 17:54:36 +0200 Subject: [PATCH 1/2] Prefetch and/or prerender pages when likely navigated to --- site/lib/src/extensions/registry.dart | 2 +- ...rocessor.dart => tutorial_navigation.dart} | 21 +----- site/lib/src/layouts/dash_layout.dart | 75 +++++++++++++++++++ site/lib/src/layouts/doc_layout.dart | 30 ++++++++ 4 files changed, 109 insertions(+), 19 deletions(-) rename site/lib/src/extensions/{tutorial_prefetch_processor.dart => tutorial_navigation.dart} (80%) diff --git a/site/lib/src/extensions/registry.dart b/site/lib/src/extensions/registry.dart index fb51180b84b..a2690a6a188 100644 --- a/site/lib/src/extensions/registry.dart +++ b/site/lib/src/extensions/registry.dart @@ -10,7 +10,7 @@ import 'glossary_link_processor.dart'; import 'header_extractor.dart'; import 'header_processor.dart'; import 'table_processor.dart'; -import 'tutorial_prefetch_processor.dart'; +import 'tutorial_navigation.dart'; import 'tutorial_structure_processor.dart'; /// A list of all node-processing, page extensions to applied to diff --git a/site/lib/src/extensions/tutorial_prefetch_processor.dart b/site/lib/src/extensions/tutorial_navigation.dart similarity index 80% rename from site/lib/src/extensions/tutorial_prefetch_processor.dart rename to site/lib/src/extensions/tutorial_navigation.dart index 27ba7b19748..ed5aed6d3aa 100644 --- a/site/lib/src/extensions/tutorial_prefetch_processor.dart +++ b/site/lib/src/extensions/tutorial_navigation.dart @@ -2,14 +2,12 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'package:jaspr/dom.dart'; -import 'package:jaspr/jaspr.dart'; import 'package:jaspr_content/jaspr_content.dart'; import '../models/tutorial_model.dart'; -/// A page extension for Jaspr Content that adds page navigation and a -/// prefetch link for the next unit to the current tutorial page. +/// A page extension for Jaspr Content that adds +/// page navigation to the current tutorial page. final class TutorialNavigationExtension implements PageExtension { const TutorialNavigationExtension(); @@ -67,19 +65,6 @@ final class TutorialNavigationExtension implements PageExtension { }, ); - if (nextChapter == null) { - return nodes; - } - - return [ - ComponentNode( - Document.head( - children: [ - link(rel: 'prefetch', href: nextChapter.url), - ], - ), - ), - ...nodes, - ]; + return nodes; } } diff --git a/site/lib/src/layouts/dash_layout.dart b/site/lib/src/layouts/dash_layout.dart index b06835835b8..b9b20e54387 100644 --- a/site/lib/src/layouts/dash_layout.dart +++ b/site/lib/src/layouts/dash_layout.dart @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:convert'; + import 'package:jaspr/dom.dart'; import 'package:jaspr/jaspr.dart'; import 'package:jaspr_content/jaspr_content.dart'; @@ -25,6 +27,14 @@ abstract class FlutterDocsLayout extends PageLayoutBase { String get defaultSidenav => 'default'; + /// Returns page-specific URLs to eagerly speculate on, in addition to + /// the document-level rules that match all internal links. + /// + /// Override in subclasses to provide page-specific URLs for + /// eager prerendering and prefetching. + ({Set prerender, Set prefetch}) speculationUrls(Page page) => + const (prerender: {}, prefetch: {}); + @override @mustCallSuper Iterable buildHead(Page page) { @@ -148,6 +158,9 @@ ga('create', 'UA-67589403-1', 'auto'); ga('send', 'pageview'); '''), + // Add speculation rules and prefetch fallback links for + // URLs provided by subclass overrides of speculationUrls. + ..._buildSpeculationRulesHead(page), ]; } @@ -263,4 +276,66 @@ if (sidenav) { ], ); } + + /// Builds the speculation rules `'), + // Fall back to prefetch link tags for browsers without + // Speculation Rules API support. + for (final url in {...prerender, ...prefetch}) + link(rel: 'prefetch', href: url), + ]; + } } diff --git a/site/lib/src/layouts/doc_layout.dart b/site/lib/src/layouts/doc_layout.dart index cee1cb2ae74..35974839360 100644 --- a/site/lib/src/layouts/doc_layout.dart +++ b/site/lib/src/layouts/doc_layout.dart @@ -24,6 +24,27 @@ class DocLayout extends FlutterDocsLayout { bool get allowBreadcrumbs => true; + @override + ({Set prerender, Set prefetch}) speculationUrls(Page page) { + // On the homepage, prefetch pages commonly navigated to, + // such as entries from the top navigation menu. + if (page.path == 'index.md') { + return ( + prerender: const {}, + prefetch: const { + '/learn/pathway', + '/ai/create-with-ai', + }, + ); + } + + final pageData = page.data.page; + return ( + prerender: {?_pathFromPageInfo(pageData['next'])}, + prefetch: {?_pathFromPageInfo(pageData['prev'])}, + ); + } + @override Component buildBody(Page page, Component child) { final pageData = page.data.page; @@ -89,3 +110,12 @@ class DocLayout extends FlutterDocsLayout { ); } } + +/// Extracts and returns the `path` value from a page info map, +/// or `null` if [data] is not a map or has no `path` entry. +String? _pathFromPageInfo(Object? data) { + if (data case {'path': final String path}) { + return path; + } + return null; +} From 952f1fbf14d0ec5e866f48d79755793d743be0d1 Mon Sep 17 00:00:00 2001 From: Parker Lougheed Date: Tue, 21 Apr 2026 18:11:21 +0200 Subject: [PATCH 2/2] Apply updated version of Gemini suggested change --- site/lib/src/layouts/dash_layout.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/lib/src/layouts/dash_layout.dart b/site/lib/src/layouts/dash_layout.dart index b9b20e54387..56ee4244ad5 100644 --- a/site/lib/src/layouts/dash_layout.dart +++ b/site/lib/src/layouts/dash_layout.dart @@ -328,7 +328,7 @@ if (sidenav) { 'eagerness': 'eager', }, ], - }); + }).replaceAll('$rules'),