Skip to content

[flutter_tools] Intercept prohibited hot reloads in widget preview and trigger hot restart - #193447

Merged
auto-submit[bot] merged 5 commits into
flutter:masterfrom
bkonyi:issue-192697
Oct 7, 2026
Merged

auto-submit[bot] merged 5 commits into
flutter:masterfrom
bkonyi:issue-192697

Conversation

@bkonyi

@bkonyi bkonyi commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

When hot reload fails due to unsupported changes (such as class hierarchy modifications, global initializer changes, or file additions/deletions) in flutter widget-preview, automatically fall back to a hot restart instead of leaving the previewer in a failed reload state.

Changes

  • ResidentWebRunner: Attach updateFSReport to the failed OperationResult when _updateDevFS fails so callers can inspect UpdateFSReport.hotReloadRejected.
  • WidgetPreviewStartCommand: Introduce handleReload() (invoked inside _triggerReload for .hotReload requests so the fallback remains serialized with other reload and restart requests, and add a @visibleForTesting widgetPreviewApp setter), which checks if restart() returned an OperationResult with UpdateFSReport(hotReloadRejected: true). When rejected, log a status message (kHotReloadRejectedMessage) and invoke restart(fullRestart: true).

Fixes #192697

Pre-launch Checklist

…stead

When using `flutter widget-preview start`, the user cannot trigger a hot restart
manually because stdin is not connected to the preview runner process. If a hot
reload is rejected due to unsupported changes, previews become stuck.

This change attaches `UpdateFSReport` to `OperationResult` on reload rejection
in `ResidentWebRunner`, and updates `WidgetPreviewStartCommand` to intercept
rejected reloads and automatically perform a hot restart instead.

Fixes flutter#192697
…lback

Adds `checkForHotRestartTriggerEvent` to `WidgetPreviewDtdServices` and
`WidgetPreviewScaffoldDtdServices` so that when an automatic hot restart is
triggered due to a rejected hot reload, the reloaded `WidgetPreviewScaffold`
displays a `SnackBar` toast notifying the user.

Issue flutter#192697
@flutter-dashboard flutter-dashboard Bot added the CICD Run CI/CD label Sep 28, 2026
@github-actions github-actions Bot added the tool Affects the "flutter" command-line tool. See also t: labels. label Sep 28, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request implements an automatic fallback to hot restart when a hot reload is rejected due to unsupported changes in the widget previewer, displaying a SnackBar notification to the user. The review feedback suggests introducing state guards (_isReloading and _reloadPending) in WidgetPreviewStartCommand to serialize reload operations and prevent potential race conditions from rapid successive file changes.

bkonyi added 2 commits October 2, 2026 15:49
…anges

Simplifies the hot reload rejection fallback to only trigger a hot restart in `WidgetPreviewStartCommand.handleReload`, reverting the DTD `checkForHotRestartTriggerEvent` RPC and scaffold `SnackBar` changes.
# Conflicts:
#	packages/flutter_tools/lib/src/commands/widget_preview.dart
#	packages/flutter_tools/lib/src/isolated/resident_web_runner.dart
#	packages/flutter_tools/test/commands.shard/permeable/widget_preview/widget_preview_test.dart
@bkonyi
bkonyi requested a review from srawlins October 6, 2026 16:29
@bkonyi bkonyi added the autosubmit Merge PR when tree becomes green via auto submit App label Oct 6, 2026
@auto-submit
auto-submit Bot added this pull request to the merge queue Oct 7, 2026
@auto-submit auto-submit Bot removed the autosubmit Merge PR when tree becomes green via auto submit App label Oct 7, 2026
@auto-submit

auto-submit Bot commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

auto label is removed for flutter/flutter/193447, Failed to enqueue flutter/flutter/193447 with HTTP 400: UnknownException(TimeoutException after 0:00:05.000000: No stream event, stack:

).

Merged via the queue into flutter:master with commit 42d685b Oct 7, 2026
25 checks passed
@bkonyi

bkonyi commented Oct 7, 2026

Copy link
Copy Markdown
Contributor Author

Reason for revert: conflicting changes with #192831 are causing failures due to concurrent merge

@bkonyi bkonyi added the revert Autorevert PR (with "Reason for revert:" comment) label Oct 7, 2026
@bkonyi
bkonyi deleted the issue-192697 branch October 7, 2026 21:11
@flutteractionsbot

Copy link
Copy Markdown
Contributor

Successfully created revert PR: #193968

@flutteractionsbot flutteractionsbot removed the revert Autorevert PR (with "Reason for revert:" comment) label Oct 7, 2026
richyo-codes pushed a commit to richyo-codes/flutter that referenced this pull request Oct 8, 2026
…eview and trigger hot restart (flutter#193968)

Reverts: [[flutter_tools] Intercept prohibited hot reloads in widget
preview and trigger hot
restart](flutter#193447)

Initiated by: @bkonyi

Reason for reverting: conflicting changes with
flutter#192831 are causing failures due
to concurrent merge

Original PR Author: @bkonyi

Reviewed By: @srawlins

The original PR description is provided below:

When hot reload fails due to unsupported changes (such as class
hierarchy modifications, global initializer changes, or file
additions/deletions) in `flutter widget-preview`, automatically fall
back to a hot restart instead of leaving the previewer in a failed
reload state.

### Changes
- **`ResidentWebRunner`**: Attach `updateFSReport` to the failed
`OperationResult` when `_updateDevFS` fails so callers can inspect
`UpdateFSReport.hotReloadRejected`.
- **`WidgetPreviewStartCommand`**: Introduce `handleReload()` (invoked
inside `_triggerReload` for `.hotReload` requests so the fallback
remains serialized with other reload and restart requests, and add a
`@visibleForTesting` `widgetPreviewApp` setter), which checks if
`restart()` returned an `OperationResult` with
`UpdateFSReport(hotReloadRejected: true)`. When rejected, log a status
message (`kHotReloadRejectedMessage`) and invoke `restart(fullRestart:
true)`.

Fixes flutter#192697

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
suryaKommana2662 pushed a commit to suryaKommana2662/flutter that referenced this pull request Oct 29, 2026
…d trigger hot restart (flutter#193447)

When hot reload fails due to unsupported changes (such as class
hierarchy modifications, global initializer changes, or file
additions/deletions) in `flutter widget-preview`, automatically fall
back to a hot restart instead of leaving the previewer in a failed
reload state.

### Changes
- **`ResidentWebRunner`**: Attach `updateFSReport` to the failed
`OperationResult` when `_updateDevFS` fails so callers can inspect
`UpdateFSReport.hotReloadRejected`.
- **`WidgetPreviewStartCommand`**: Introduce `handleReload()` (invoked
inside `_triggerReload` for `.hotReload` requests so the fallback
remains serialized with other reload and restart requests, and add a
`@visibleForTesting` `widgetPreviewApp` setter), which checks if
`restart()` returned an `OperationResult` with
`UpdateFSReport(hotReloadRejected: true)`. When rejected, log a status
message (`kHotReloadRejectedMessage`) and invoke `restart(fullRestart:
true)`.

Fixes flutter#192697

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
suryaKommana2662 pushed a commit to suryaKommana2662/flutter that referenced this pull request Oct 29, 2026
…eview and trigger hot restart (flutter#193968)

Reverts: [[flutter_tools] Intercept prohibited hot reloads in widget
preview and trigger hot
restart](flutter#193447)

Initiated by: @bkonyi

Reason for reverting: conflicting changes with
flutter#192831 are causing failures due
to concurrent merge

Original PR Author: @bkonyi

Reviewed By: @srawlins

The original PR description is provided below:

When hot reload fails due to unsupported changes (such as class
hierarchy modifications, global initializer changes, or file
additions/deletions) in `flutter widget-preview`, automatically fall
back to a hot restart instead of leaving the previewer in a failed
reload state.

### Changes
- **`ResidentWebRunner`**: Attach `updateFSReport` to the failed
`OperationResult` when `_updateDevFS` fails so callers can inspect
`UpdateFSReport.hotReloadRejected`.
- **`WidgetPreviewStartCommand`**: Introduce `handleReload()` (invoked
inside `_triggerReload` for `.hotReload` requests so the fallback
remains serialized with other reload and restart requests, and add a
`@visibleForTesting` `widgetPreviewApp` setter), which checks if
`restart()` returned an `OperationResult` with
`UpdateFSReport(hotReloadRejected: true)`. When rejected, log a status
message (`kHotReloadRejectedMessage`) and invoke `restart(fullRestart:
true)`.

Fixes flutter#192697

## Pre-launch Checklist

- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.

<!-- Links -->
[Contributor Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview
[Tree Hygiene]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md
[test-exempt]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests
[Flutter Style Guide]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md
[Features we expect every widget to implement]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement
[CLA]: https://cla.developers.google.com/
[breaking change policy]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes
[Discord]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md
[Data Driven Fixes]:
https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
chingjun added a commit to chingjun/flutter that referenced this pull request Nov 12, 2026
…sts that run the real pub

The hermetic commands shard had left out nine files and the permeable
half entirely: their tests resolve a project of their own with the real
pub (the widget_preview tests' scaffold, `flutter create`'s projects,
`flutter pub get` on plugins), and pub wants a Flutter SDK -- not just the
Dart SDK and engine stamps the general shard's facsimile under bin/cache
already had. This lays the rest of pub's view of an SDK in the runfiles
tree, and a pub cache of what the projects reach, so no test downloads a
package:

* `pub_cache(name, roots, dev_roots, without)` (dart/pub.bzl): the hosted
  packages of a closure, laid as a pub cache
  (`hosted/pub.dev/<name>-<version>/` plus the `hosted-hashes` sha256 pub
  checks) from the pinned sources `@pub` already fetches. dart_test's new
  `pub_cache` attribute symlinks it under the runfiles `.pub-cache` and the
  launcher exports PUB_CACHE. pub still asks pub.dev which versions exist
  unless it runs `--offline` (the tests do not), so such a shard has the
  network; it downloads nothing it finds in the cache (one package --
  webview_flutter_web, outside the workspace's lock -- it does fetch).
* `bin/cache/flutter.version.json` (bin/internal: `flutter_version_json`):
  pub's Flutter SDK source reads `flutterVersion` from it and checks it
  against the packages' `flutter:` constraints (leak_tracker_flutter_testing
  wants >=3.18); the tool parses `frameworkVersion` as a git tag. Fixed
  words again: 999.0.0-0.0.pre, the version every constraint allows.
* The SDK packages under FLUTTER_ROOT/packages (flutter, flutter_test,
  flutter_localizations, flutter_driver, integration_test,
  fuchsia_remote_debug_protocol and a new BUILD for flutter_web_plugins),
  the workspace's pubspec.yaml and pubspec.lock (`flutter create` reads
  the SDK's lock), and the engine's sky_engine package under
  `bin/cache/pkg/sky_engine` (the analyzer resolves dart:ui from it):
  `assembled_directory(contents, prefix)` in local_engine.bzl is the
  `install`/`local_engine` assembly as a rule of its own, and
  `//engine/src/flutter/sky/packages/sky_engine:package` (a converter
  appendix) lays dart_pkg's directory and the copied libraries as GN's
  `gen/dart-pkg/sky_engine`.
* `<dart-sdk>/version` in the SDK overlay's `dart` filegroup: the analyzer
  (`AnalysisContextCollection` with `sdkPath`) reads it; six widget_preview
  tests needed only this.
* `@gradle_wrapper` (flutter_sdk_cache.bzl, from bin/internal's pin):
  `flutter create` copies `bin/cache/artifacts/gradle_wrapper` into an
  Android project.
* `dart_tests(large = [...])`, as flutter_tests has it: preview_detector_test,
  preview_detector_workspace_test and preview_code_generator_test take
  minutes on a loaded machine (the first timed out at 300 s).
* //bin:BUILD.bazel exports the Windows entrypoint scripts as text
  (script_test reads them for what they must not do); the Unix ones stay
  out on purpose -- a test that finds bin/flutter spawns it, and the script
  bootstraps the tool from the network. integration_test's `sources` gain
  its Android embedding: the package is a plugin, and the tool reads the
  main class when a project that depends on it resolves.

`//packages/flutter_tools:commands_shard_hermetic` is now the whole
directory (67 files, up from 58); `commands_shard_permeable` is new (8 of
11: create_test analyzes, resolves and tests the projects it creates with
bin/cache/flutter_tools.snapshot and icon_tree_shaker_test spawns
bin/flutter -- the built tool, which a later change can lay in the
facsimile from Bazel's own compile; widget_preview_test does not compile
at this checkout, flutter#192831 and flutter#193447 crossed and upstream reverted the
latter in flutter#193968). Both are in `//:tool_tests`. The README's tests
section describes the facsimile.

Measured:
  bazel test //packages/flutter_tools:commands_shard_hermetic \
             //packages/flutter_tools:commands_shard_permeable
    75 of 75 targets pass; 1,140 cases passed, 6 skipped
  dart test (FLUTTER_ROOT set, -j 4) over the same 75 files: 1,140 + 6
  bazel test //:tool_tests //:dart_tests: 406 of 406 targets pass
  gn_to_bazel --check clean; converter tests 231, bazel_tools tests 43.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CICD Run CI/CD tool Affects the "flutter" command-line tool. See also t: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[widget_previews] Intercept prohibited hot reloads and hot restart instead

3 participants