From 49cb4a05f2c99e2d50fb2e8a7648394503be8939 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Fri, 3 Oct 2025 10:36:50 -0700 Subject: [PATCH 1/2] Add the initial wasm by default flag --- .../devtools_app/lib/src/shared/feature_flags.dart | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/packages/devtools_app/lib/src/shared/feature_flags.dart b/packages/devtools_app/lib/src/shared/feature_flags.dart index d885a3e460e..607160431df 100644 --- a/packages/devtools_app/lib/src/shared/feature_flags.dart +++ b/packages/devtools_app/lib/src/shared/feature_flags.dart @@ -110,6 +110,17 @@ extension FeatureFlags on Never { enabled: true, ); + /// Flag to enable refactors in the Flutter Property Editor sidebar. + /// + /// https://github.com/flutter/devtools/issues/9214 + static const wasmByDefault = FlutterChannelFeatureFlag( + name: 'wasmByDefault', + flutterChannel: FlutterChannel.beta, + enabledForDartApps: false, + enabledForFlutterAppsFallback: false, + ); + + /// A set of all the boolean feature flags for debugging purposes. /// /// When adding a new boolean flag, you are responsible for adding it to this @@ -130,7 +141,7 @@ extension FeatureFlags on Never { /// When adding a new Flutter channel flag, you are responsible for adding it /// to this map as well. static final _flutterChannelFlags = { - // TODO(https://github.com/flutter/devtools/issues/9438): Add wasm flag. + wasmByDefault, }; /// A helper to print the status of all the feature flags. From 223ea29f46894c914f0cf5ad345ff336f8a3d8c0 Mon Sep 17 00:00:00 2001 From: Elliott Brooks <21270878+elliette@users.noreply.github.com> Date: Fri, 3 Oct 2025 14:17:07 -0700 Subject: [PATCH 2/2] Hook up WASM experiment flag to show WASM banner --- .../lib/src/shared/feature_flags.dart | 1 - .../src/shared/managers/banner_messages.dart | 37 ++++++++++++++++++ .../src/shared/preferences/preferences.dart | 39 +++++++++++++++++++ .../lib/src/utils/url/_url_stub.dart | 5 +++ .../lib/src/utils/url/_url_web.dart | 4 ++ 5 files changed, 85 insertions(+), 1 deletion(-) diff --git a/packages/devtools_app/lib/src/shared/feature_flags.dart b/packages/devtools_app/lib/src/shared/feature_flags.dart index 607160431df..b9a331cbe75 100644 --- a/packages/devtools_app/lib/src/shared/feature_flags.dart +++ b/packages/devtools_app/lib/src/shared/feature_flags.dart @@ -120,7 +120,6 @@ extension FeatureFlags on Never { enabledForFlutterAppsFallback: false, ); - /// A set of all the boolean feature flags for debugging purposes. /// /// When adding a new boolean flag, you are responsible for adding it to this diff --git a/packages/devtools_app/lib/src/shared/managers/banner_messages.dart b/packages/devtools_app/lib/src/shared/managers/banner_messages.dart index c3fc64a5a86..45e0b778293 100644 --- a/packages/devtools_app/lib/src/shared/managers/banner_messages.dart +++ b/packages/devtools_app/lib/src/shared/managers/banner_messages.dart @@ -545,6 +545,39 @@ class WelcomeToNewInspectorMessage extends BannerInfo { ); } +class WasmWelcomeMessage extends BannerInfo { + WasmWelcomeMessage() + : super( + key: const Key('WasmWelcomeMessage'), + screenId: universalScreenId, + dismissOnConnectionChanges: true, + buildTextSpans: (context) => [ + const TextSpan( + text: + '🚀 A faster and more performant DevTools is now available on WebAssembly! Click ', + ), + const TextSpan( + text: 'Enable', + style: TextStyle(fontWeight: FontWeight.bold), + ), + const TextSpan(text: ' to try it out now.'), + const TextSpan( + text: ' Please note that this will trigger a reload of DevTools.', + style: TextStyle(fontStyle: FontStyle.italic), + ), + ], + buildActions: (context) => [ + DevToolsButton( + label: 'Enable', + onPressed: () async { + await preferences.enableWasmInStorage(); + webReload(); + }, + ), + ], + ); +} + void maybePushDebugModePerformanceMessage(String screenId) { if (offlineDataController.showingOfflineData.value) return; if (serviceConnection.serviceManager.connectedApp?.isDebugFlutterAppNow ?? @@ -577,6 +610,10 @@ void pushWelcomeToNewInspectorMessage(String screenId) { bannerMessages.addMessage(WelcomeToNewInspectorMessage(screenId: screenId)); } +void pushWasmWelcomeMessage() { + bannerMessages.addMessage(WasmWelcomeMessage()); +} + extension BannerMessageThemeExtension on ThemeData { TextStyle get warningMessageLinkStyle => regularTextStyle.copyWith( decoration: TextDecoration.underline, diff --git a/packages/devtools_app/lib/src/shared/preferences/preferences.dart b/packages/devtools_app/lib/src/shared/preferences/preferences.dart index c54e260efbb..32422f19c3f 100644 --- a/packages/devtools_app/lib/src/shared/preferences/preferences.dart +++ b/packages/devtools_app/lib/src/shared/preferences/preferences.dart @@ -19,6 +19,7 @@ import '../constants.dart'; import '../diagnostics/inspector_service.dart'; import '../feature_flags.dart'; import '../globals.dart'; +import '../managers/banner_messages.dart'; import '../primitives/query_parameters.dart'; import '../server/server.dart'; import '../utils/utils.dart'; @@ -65,6 +66,8 @@ enum _GeneralPreferences { verboseLogging } /// A controller for global application preferences. class PreferencesController extends DisposableController with AutoDisposeControllerMixin { + static const _welcomeShownStorageId = 'wasmWelcomeShown'; + /// Whether the user preference for DevTools theme is set to dark mode. /// /// To check whether DevTools is using a light or dark theme, other parts of @@ -134,6 +137,13 @@ class PreferencesController extends DisposableController setGlobal(PreferencesController, this); } + /// Enables the wasm experiment in storage. + /// + /// This is used to persist the preference across reloads. + Future enableWasmInStorage() async { + await storage.setValue(_ExperimentPreferences.wasm.storageKey, 'true'); + } + Future _initDarkMode() async { final darkModeValue = await storage.getValue( _UiPreferences.darkMode.storageKey, @@ -181,6 +191,18 @@ class PreferencesController extends DisposableController ); } + // Maybe show the WASM welcome message on app connection if this is the + // first time the user is loading DevTools after the WASM experiment was + // enabled. + addAutoDisposeListener( + serviceConnection.serviceManager.connectedState, + () async { + if (serviceConnection.serviceManager.connectedState.value.connected) { + await _maybeShowWasmWelcomeMessage(); + } + }, + ); + addAutoDisposeListener(wasmEnabled, () async { final enabled = wasmEnabled.value; _log.fine('preference update (wasmEnabled = $enabled)'); @@ -247,6 +269,23 @@ class PreferencesController extends DisposableController toggleWasmEnabled(shouldEnableWasm); } + Future _maybeShowWasmWelcomeMessage() async { + // If we have already shown the welcome message, don't show it again. + final welcomeAlreadyShown = await storage.getValue(_welcomeShownStorageId); + if (welcomeAlreadyShown == 'true') return; + + // Show the welcome message if the WASM experiment is enabled but the user + // is not using the WASM build. + final connectedApp = serviceConnection.serviceManager.connectedApp; + if (connectedApp != null && + FeatureFlags.wasmByDefault.isEnabled(connectedApp) && + !kIsWasm) { + // Mark the welcome message as shown. + await storage.setValue(_welcomeShownStorageId, 'true'); + pushWasmWelcomeMessage(); + } + } + Future _initVerboseLogging() async { final verboseLoggingEnabledValue = await boolValueFromStorage( _GeneralPreferences.verboseLogging.name, diff --git a/packages/devtools_app_shared/lib/src/utils/url/_url_stub.dart b/packages/devtools_app_shared/lib/src/utils/url/_url_stub.dart index 8f9649e823b..0990d64b3ad 100644 --- a/packages/devtools_app_shared/lib/src/utils/url/_url_stub.dart +++ b/packages/devtools_app_shared/lib/src/utils/url/_url_stub.dart @@ -17,6 +17,11 @@ String? getWebUrl() => null; // Unused parameter lint doesn't make sense for stub files. void webRedirect(String url) {} +/// Performs a web reload using window.location.reload(). +/// +/// No-op for non-web platforms. +void webReload() {} + /// Updates the query parameter with [key] to the new [value], and optionally /// reloads the page when [reload] is true. /// diff --git a/packages/devtools_app_shared/lib/src/utils/url/_url_web.dart b/packages/devtools_app_shared/lib/src/utils/url/_url_web.dart index 7ca399dab72..67e84eeff1f 100644 --- a/packages/devtools_app_shared/lib/src/utils/url/_url_web.dart +++ b/packages/devtools_app_shared/lib/src/utils/url/_url_web.dart @@ -16,6 +16,10 @@ void webRedirect(String url) { window.location.replace(url); } +void webReload() { + window.location.reload(); +} + void updateQueryParameter(String key, String? value, {bool reload = false}) { final newQueryParams = Map.of(loadQueryParams()); if (value == null) {