Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/devtools_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
Copy link
Contributor Author

@ericwindmill ericwindmill Jan 27, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should just merge this because its going to keep generating the file as long as it isn't there. There are no consequences to merging

3 changes: 2 additions & 1 deletion firebase.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
{ "source": "/setup", "destination": "/install", "type": 301 },
{ "source": "/technical-overview", "destination": "/resources/architectural-overview", "type": 301 },
{ "source": "/text-input", "destination": "/cookbook/forms/text-input", "type": 301 },
{ "source": "/tutorial", "destination": "/learn/tutorial", "type": 301 },
{ "source": "/tutorial", "destination": "/learn/pathway/tutorial", "type": 301 },
{ "source": "/tutorials", "destination": "/reference/learning-resources", "type": 301 },
{ "source": "/unbounded-constraints", "destination": "/ui/layout/constraints#unbounded", "type": 301 },
{ "source": "/ui-performance", "destination": "/perf/ui-performance", "type": 301 },
Expand Down Expand Up @@ -185,6 +185,7 @@
{ "source": "/platform-integration/windows/run-loop-migration", "destination": "/release/breaking-changes/windows-run-loop", "type": 301 },
{ "source": "/platform-integration/windows/version-migration", "destination": "/release/breaking-changes/windows-version-information", "type": 301 },
{ "source": "/platform-integration/windows/dark-mode-migration", "destination": "/release/breaking-changes/windows-dark-mode", "type": 301 },
{ "source": "/reference/learning-resources", "destination": "/learn/learning-resources", "type": 301 },
{ "source": "/reference/widgets/:catalogpage+", "destination": "/ui/widgets/:catalogpage+", "type": 301 },
{ "source": "/reference/widgets/widgetindex", "destination": "/reference/widgets", "type": 301 },
{ "source": "/release/release-notes/changelogs/changelog-1.17.0", "destination": "/release/release-notes/release-notes-1.17.0", "type": 301 },
Expand Down
2 changes: 1 addition & 1 deletion site/lib/_sass/components/_header.scss
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@
ul.nav-items {
display: none;
padding: 0;
margin: 0 0 0 auto;
margin: 0 auto 0 0;
gap: .75rem;
flex-direction: row;
list-style-type: none;
Expand Down
6 changes: 5 additions & 1 deletion site/lib/src/components/layout/header.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class DashHeader extends StatelessComponent {
ul(classes: 'nav-items', [
_NavItem(
href: '/',
label: 'Home',
label: 'User Guides',
isActive: activeEntry == ActiveNavEntry.home,
),
_NavItem(
Expand All @@ -65,6 +65,7 @@ class DashHeader extends StatelessComponent {
const _NavItem(
href: 'https://api.flutter.dev',
label: 'Reference',
openInNewTab: true,
),
]),

Expand Down Expand Up @@ -123,17 +124,20 @@ class _NavItem extends StatelessComponent {
required this.href,
required this.label,
this.isActive = false,
this.openInNewTab = false,
});

final String href;
final String label;
final bool isActive;
final bool openInNewTab;

@override
Component build(BuildContext _) => li([
a(
href: href,
classes: ['nav-link', 'text-button', if (isActive) 'active'].toClasses,
attributes: openInNewTab ? {'target': '_blank', 'rel': 'noopener'} : null,
[.text(label)],
),
]);
Expand Down
2 changes: 1 addition & 1 deletion site/lib/src/extensions/tutorial_prefetch_processor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ final class TutorialNavigationExtension implements PageExtension {

@override
Future<List<Node>> apply(Page page, List<Node> nodes) async {
if (!page.path.startsWith('learn/tutorial/')) {
if (!page.path.startsWith('learn/pathway/')) {
return nodes;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ final class TutorialStructureExtension implements PageExtension {

@override
Future<List<Node>> apply(Page page, List<Node> nodes) async {
if (!page.path.startsWith('learn/tutorial/') ||
if (!page.path.startsWith('learn/pathway/tutorial/') ||
page.path.endsWith('index.md')) {
return nodes;
}
Expand Down
41 changes: 40 additions & 1 deletion site/lib/src/layouts/dash_layout.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,37 @@ abstract class FlutterDocsLayout extends PageLayoutBase {

List<String> get defaultBodyClasses => [];

String get defaultSidenav => 'default';

/// Path prefix to sidenav name mapping.
/// More specific paths should come before less specific ones.
static const pathSidenavs = <String, String>{
'/learn': 'learn',
// Add future path-specific sidenavs here
};

/// Returns the sidenav key for a given page URL.
/// Priority order:
/// 1. Page frontmatter 'sidenav' field (if specified)
/// 2. Path-based sidenav (if URL matches a registered path prefix)
/// 3. Default sidenav
String getSidenavForPage(String pageUrl, String? pageSpecifiedSidenav) {
// Priority 1: Page-specific override from frontmatter
if (pageSpecifiedSidenav != null) {
return pageSpecifiedSidenav;
}

// Priority 2: Path prefix matching
for (final entry in pathSidenavs.entries) {
if (pageUrl.startsWith(entry.key)) {
return entry.value;
}
}

// Priority 3: Default fallback
return defaultSidenav;
}

@override
@mustCallSuper
Iterable<Component> buildHead(Page page) {
Expand Down Expand Up @@ -154,8 +185,16 @@ ga('send', 'pageview');
final pageData = page.data.page;
final bodyClass = pageData['bodyClass'] as String?;
final pageUrl = page.url.startsWith('/') ? page.url : '/${page.url}';

final pageSidenav = getSidenavForPage(
pageUrl,
pageData['sidenav'] as String?,
);
final sideNavEntries = switch (page.data['sidenav']) {
final List<Object?> sidenavData => navEntriesFromData(sidenavData),
final Map<String, Object?> sidenavs => switch (sidenavs[pageSidenav]) {
final List<Object?> sidenavData => navEntriesFromData(sidenavData),
_ => null,
},
_ => null,
};
final obsolete = pageData['obsolete'] == true;
Expand Down
2 changes: 1 addition & 1 deletion site/lib/src/layouts/tutorial_layout.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class TutorialLayout extends DocLayout {
bool get allowBreadcrumbs => false;

@override
List<String> get defaultBodyClasses => ['sidenav-closed'];
List<String> get defaultBodyClasses => [];

@override
Component buildBody(Page page, Component child) {
Expand Down
Loading
Loading