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
6 changes: 6 additions & 0 deletions site/lib/jaspr_options.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import 'package:docs_flutter_dev_site/src/components/cookie_notice.dart'
import 'package:docs_flutter_dev_site/src/components/copy_button.dart'
as prefix7;
import 'package:docs_flutter_dev_site/src/components/feedback.dart' as prefix8;
import 'package:docs_flutter_dev_site/src/components/os_selector.dart'
as prefix9;

/// Default [JasprOptions] for use with your jaspr project.
///
Expand Down Expand Up @@ -79,6 +81,10 @@ JasprOptions get defaultJasprOptions => JasprOptions(
prefix5.ThemeSwitcher: ClientTarget<prefix5.ThemeSwitcher>(
'src/components/header/theme_switcher',
),

prefix9.OsSelector: ClientTarget<prefix9.OsSelector>(
'src/components/os_selector',
),
},
styles: () => [],
);
Expand Down
7 changes: 7 additions & 0 deletions site/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'package:path/path.dart' as path;

import 'jaspr_options.dart'; // Generated. Do not remove or edit.
import 'src/components/card.dart';
import 'src/components/os_selector.dart';
import 'src/components/pages/learning_resource_index.dart';
import 'src/components/tabs.dart';
import 'src/data/learning_resources.dart';
Expand Down Expand Up @@ -67,6 +68,12 @@ final RegExp _passThroughPattern = RegExp(r'.*\.(txt|json|pdf)$');
/// Custom "components" that can be used from Markdown files.
List<CustomComponent> get _embeddableComponents => [
const DashTabs(),
CustomComponent(
pattern: RegExp('OSSelector', caseSensitive: false),
builder: (name, attributes, child) {
return const OsSelector();
},
),
CustomComponent(
pattern: RegExp('Card', caseSensitive: false),
builder: (name, attributes, child) {
Expand Down
94 changes: 94 additions & 0 deletions site/lib/src/components/os_selector.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2025 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:jaspr/jaspr.dart';
import 'package:universal_web/web.dart' as web;

import '../util.dart';

@client
class OsSelector extends StatefulComponent {
const OsSelector({super.key});

@override
State<OsSelector> createState() => _OsSelectorState();
}

class _OsSelectorState extends State<OsSelector> {
// This value is currently not synced across potential multiple instances
// of the OS selector on the page. In practice, this currently does not
// happen, but would need to be addressed if changed.
OperatingSystem selectedOs = OperatingSystem.windows;

@override
void initState() {
super.initState();

if (kIsWeb) {
final currentOs = getOS() ?? OperatingSystem.windows;
setOS(currentOs);
}
}

void setOS(OperatingSystem os) {
setState(() {
selectedOs = os;
});

final selectedOsTextSpans = web.document.querySelectorAll(
'.selected-os-text',
);
for (var i = 0; i < selectedOsTextSpans.length; i++) {
final span = selectedOsTextSpans.item(i) as web.Element;
span.textContent = os.label;
}

final bodyClasses = web.document.body!.classList;
for (final os in OperatingSystem.values) {
bodyClasses.remove('show-${os.name}');
}
bodyClasses.add('show-${os.name}');
}

@override
Component build(BuildContext context) {
return div(classes: 'card-grid narrow os-selector', [
for (final os in OperatingSystem.values)
button(
id: 'install-${os.name}',
classes: [
'card outlined-card install-card',
if (selectedOs == os) 'selected-card',
].toClasses,
attributes: {
'data-os': os.name,
'aria-label': 'Update docs to cover ${os.label}',
},
events: {
'click': (event) {
setOS(os);
},
},
[
div(classes: 'card-leading', [
img(
src: '/assets/images/docs/brand-svg/${os.name}.svg',
alt: '${os.label} logo',
attributes: {
'width': '72',
'height': '72',
'aria-hidden': 'true',
},
),
]),
div(classes: 'card-header text-center', [
span(classes: 'card-title', [
text(os.label),
]),
]),
],
),
]);
}
}
40 changes: 40 additions & 0 deletions site/lib/src/util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import 'package:jaspr/jaspr.dart';
import 'package:path/path.dart' as path;
import 'package:universal_web/web.dart' as web;

/// Whether this build of the site will be deployed to production.
const productionBuild = bool.fromEnvironment('PRODUCTION');
Expand Down Expand Up @@ -109,3 +110,42 @@ extension ListToClasses on List<String> {
/// that can be added to an HTML element.
String get toClasses => join(' ');
}

enum OperatingSystem {
windows('Windows'),
macos('macOS'),
linux('Linux'),
chromeos('ChromeOS');

const OperatingSystem(this.label);
final String label;
}

/// Get the user's current operating system, or
/// `null` if not of one "macos", "windows", "linux", or "chromeos".
OperatingSystem? getOS() {
final userAgent = web.window.navigator.userAgent;
if (userAgent.contains('Mac')) {
// macOS or iPhone
return OperatingSystem.macos;
}

if (userAgent.contains('Win')) {
// Windows
return OperatingSystem.windows;
}

if ((userAgent.contains('Linux') || userAgent.contains('X11')) &&
!userAgent.contains('Android')) {
// Linux, but not Android
return OperatingSystem.linux;
}

if (userAgent.contains('CrOS')) {
// ChromeOS
return OperatingSystem.chromeos;
}

// Anything else
return null;
}
53 changes: 0 additions & 53 deletions site/web/assets/js/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,61 +180,8 @@ function setupPlatformKeys() {
});
}

function _osNameFromId(osId) {
switch (osId) {
case 'macos':
return 'macOS';
case 'linux':
return 'Linux';
case 'chromeos':
return 'ChromeOS';
default:
return 'Windows';
}
}

function _adjustSelectedOs(osId) {
if (!osId) return;

const osName = _osNameFromId(osId);
const selectedOsTextSpans = document.querySelectorAll('.selected-os-text');
selectedOsTextSpans.forEach(function (span) {
span.textContent = osName;
});

const osSelectors = document.querySelectorAll('.os-selector button');
osSelectors.forEach(function (osSelector) {
if (osSelector.getAttribute('data-os') === osId) {
osSelector.classList.add('selected-card');
} else {
osSelector.classList.remove('selected-card');
}
});

document.body.classList.remove('show-macos', 'show-linux', 'show-windows', 'show-chromeos');
document.body.classList.add(`show-${osId}`);
}

function setupOsSelectors() {
const currentOsId = getOS() ?? 'windows';
_adjustSelectedOs(currentOsId);

const osSelectors = document.querySelectorAll('.os-selector');

for (const osSelector of osSelectors) {
const osButtons = osSelector.querySelectorAll('button');
for (const osButton of osButtons) {
const osId = osButton.getAttribute('data-os');
osButton.addEventListener('click', (e) => {
_adjustSelectedOs(osId);
});
}
}
}

function setupSite() {
setupOsSelectors();

setupToc();
setupPlatformKeys();
setupCollapsibleElements();
Expand Down
2 changes: 1 addition & 1 deletion src/content/get-started/quick.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ installing and trying out Flutter on a **Windows**{:.selected-os-text} device.
If you'd like to follow the instructions for a different OS,
please select one of the following.

{% osSelector %}
<OSSelector />

## Download prerequisite software {: #download-prerequisites}

Expand Down
2 changes: 1 addition & 1 deletion src/content/install/manual.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ installing Flutter on a **Windows**{:.selected-os-text} device.
If you'd like to follow the instructions for a different OS,
please select one of the following.

{% osSelector %}
<OSSelector />

## Download prerequisite software {: #download-prerequisites}

Expand Down
2 changes: 1 addition & 1 deletion src/content/install/uninstall.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ uninstall Flutter on a **Windows**{:.selected-os-text} device.
If you'd like to follow the instructions for a different OS,
please select one of the following.

{% osSelector %}
<OSSelector />

## Uninstall the Flutter SDK {: #uninstall }

Expand Down
2 changes: 1 addition & 1 deletion src/content/install/with-vs-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ installing Flutter on a **Windows**{:.selected-os-text} device.
If you'd like to follow the instructions for a different OS,
please select one of the following.

{% osSelector %}
<OSSelector />

## Download prerequisite software {: #download-prerequisites}

Expand Down
2 changes: 1 addition & 1 deletion src/content/platform-integration/android/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ setting up Android development on a **Windows**{:.selected-os-text} device.
If you'd like to follow the instructions for a different OS,
please select one of the following.

{% osSelector %}
<OSSelector />

## Set up Android tooling {: #set-up-tooling}

Expand Down