Skip to content

Migrate to Dart 3.3 and package:web (wasm-ready)#401

Merged
passsy merged 31 commits into
stablefrom
fix/flutter-stable-analyzer
May 27, 2026
Merged

Migrate to Dart 3.3 and package:web (wasm-ready)#401
passsy merged 31 commits into
stablefrom
fix/flutter-stable-analyzer

Conversation

@passsy

@passsy passsy commented May 20, 2026

Copy link
Copy Markdown
Member

Wiredash now requires Dart 3.3 / Flutter 3.19, drops every dart:html / dart:js / dart:js_util import, and compiles to WebAssembly.

Why

CI on stable broke after the latest Flutter docker image moved to Dart 3.11 — the analyzer rejects dart:js_util, parameter_assignments is now strict, and http_parser/library/break infos became errors. Plastering on more // ignore: lines wouldn't carry us forward, so this jumps to Dart 3.3 and the modern JS-interop stack instead.

Web migration

before after
dart:html types + document / window package:web (web.document, web.window, web.Event, web.MutationObserver, …)
dart:js_util.callMethod(document, 'hasFocus', []) web.document.hasFocus()
dart:js' js.context['flutterCanvasKit'] @JS('flutterCanvasKit') external JSAny? get _flutterCanvasKit
if (dart.library.html) conditional imports if (dart.library.js_interop) (wasm-compatible)

flutter build web --wasm now works end-to-end. A new examples/wasm sample exercises that build in nightly CI.

SDK floor

  • sdk: '>=3.3.0 <4.0.0', flutter: '>=3.19.0'
  • New examples/old_flutter_3_19_0 (created with flutter create on Flutter 3.19.0) drives the pr-min and nightly minVersion jobs against cirruslabs/flutter:3.19.0, matching the declared floor exactly.
  • examples/old_flutter_3_0 and its CI jobs are gone.

Analyzer fallout

Cleaned up under the bumped lint set:

  • parameter_assignments — kept the app parameter name in WiredashBackdrop._buildAppPositioningAnimation with a scoped // ignore:; rewrote WiredashThemeData.fromColor to use a local.
  • unnecessary_importhttp_parser stays in wiredash_api.dart (Flutter 3.19's package:http doesn't re-export MediaType yet) with an ignore + export 'package:http_parser/http_parser.dart' show MediaType so callers stay decoupled.
  • Removed unused library directives, unused break;s, redundant !s.
  • CorePalette stays with a focused // ignore: deprecated_member_useCorePalettes isn't available across all of our supported channels yet.

Fixes #402

passsy added 8 commits May 20, 2026 05:38
Bumps the wiredash environment to Dart >=3.3.0 / Flutter >=3.19.0,
unlocking modern web interop (and WebAssembly compilation).

- Replace dart:html, dart:js, and dart:js_util with package:web and
  dart:js_interop in the web lifecycle backport, device info, and the
  renderer detection helper.
- Switch conditional imports from `dart.library.html` to
  `dart.library.js_interop`, which is the wasm-compatible signal.
- Clean up analyzer fallout from the SDK bump: parameter assignments
  in the backdrop animation builder, unnecessary http_parser imports,
  unnecessary library names, unnecessary breaks, unnecessary
  non-null assertions, and the CorePalette deprecation gets an
  ignore until CorePalettes is available across supported channels.
- Drop the Flutter 3.0.0 example and CI jobs; the new min-version
  job targets Flutter 3.19.0.
Flutter 3.19's `flutter pub get` checks the Android embedding by
looking at `build.gradle` (Groovy) and treats projects that only have
`build.gradle.kts` as embedding v1 — which then fails plugins like
`device_info_plus`. The fix landed in 3.24, so bump the min-version
container there and reformat with the CI Dart version. The SDK
itself still works on Dart 3.3.
The previous bump to Flutter 3.24 in the min-version job didn't match
the declared `flutter: >=3.19.0` constraint. Going back to 3.19 in CI
required a sample project that 3.19 can actually resolve — the older
`old_flutter_3_0` sample used Flutter 3.0 era pubspec/Gradle layout,
and our other examples use Kotlin Gradle (build.gradle.kts) which
3.19's `flutter pub get` mis-detects as embedding v1.

- Add `examples/old_flutter_3_19_0`, generated by `flutter create`
  on Flutter 3.19.0 and wired up to wiredash via a local path
  dependency.
- Drop the obsolete `examples/old_flutter_3_0` leftovers.
- Point `pr-min` and the nightly `minVersion` job at
  `cirruslabs/flutter:3.19.0` and the new example.
- Filter the new sample out of `wiresdk recreate-examples` since it
  intentionally sticks to the old Flutter 3.19 layout.
- Restore the `http_parser` import in `wiredash_api.dart` with an
  `unnecessary_import` ignore: `MediaType` is re-exported by
  `package:http` only on Flutter >=3.22, so we still need the direct
  dependency for the 3.19 build, and re-export `MediaType` so
  downstream call sites keep compiling.
`./wiresdk deps` pulls every example package, but several rely on
Dart 3.4+ APIs or Kotlin Gradle layouts that Flutter 3.19 can't
resolve (notably `examples/wasm` which pins sdk ^3.11.5). Replace
the catch-all with explicit `flutter pub get` calls in just the
wiredash package and the 3.19 sample, then run `flutter test` and
`flutter build web` against those two directly. Also pin the 3.19
sample's `flutter` constraint to 3.19.x and trim boilerplate from
its pubspec.yaml. Reformat the two sample `lib/main.dart` files so
`./wiresdk format --verify` is happy.
./wiresdk test/flutter wrap the system Flutter SDK and add nicer
package iteration, so use them instead of bare flutter commands.
The ./wiresdk deps step is still skipped here because it would pull
every example, and several of them require Dart 3.4+ or newer
Flutter tooling.
Renaming to 'transformed' was an over-correction for the new
parameter_assignments warning. Keep the more readable 'app' name
and silence the warning with a scoped ignore.
@passsy passsy changed the title Migrate to Dart 3.3 and package:web for wasm support Migrate to Dart 3.3 and package:web (wasm-ready) May 26, 2026
passsy added 13 commits May 26, 2026 18:45
The non-null helper guarded against Flutter 2.11's nullable
WidgetsBinding.instance. Min Flutter is now 3.19, so use
WidgetsBinding.instance directly.
AppLifecycleState.hidden was added in Flutter 3.13 and our floor is
now 3.19, so call sites can read it directly and the helper goes
away. Also drop the leftover comment about not being able to use
AppLifecycleListener — same Flutter 3.13 cutoff.
MediaQuery.fromView landed in Flutter 3.7.0-32.0.pre. Min Flutter is
3.19, so the deprecated MediaQuery.fromWindow path can go.
…View

SingletonFlutterWindow / WindowPadding have been deprecated since
Flutter 3.7.0-32.0.pre / 3.8.0-14.0.pre and we're now at Flutter 3.19,
so swap FlutterInfoCollector and WiredashWindowPadding to FlutterView
and ViewPadding. Services pass WidgetsBinding.instance
.platformDispatcher.views.first.
Picasso reads physicalSize from the implicit FlutterView now, and
the device_info dartdoc links point to the correct api.flutter.dev
pages (FlutterView for view-scoped values, PlatformDispatcher for
locale/brightness/textScaleFactor).
Wiredash.trackEvent runs on background isolates (e.g. via compute()
+ BackgroundIsolateBinaryMessenger). The earlier migration to
FlutterView passed the implicit view as a non-null argument, but
on a worker isolate PlatformDispatcher.implicitView trips an
assertion ('The implicit view ID is 0, but the implicit view does
not exist') and views.first throws because views is empty.

- Make FlutterInfoCollector accept a nullable FlutterView and fall
  back to PlatformDispatcher defaults (locale, brightness,
  textScaleFactor) and zero-padding/size when no view exists.
- Resolve the view in services.dart via
  PlatformDispatcher.instance.views.firstOrNull-style guarding so
  isolates pass null instead of triggering the assertion.
- Apply the same guard to PicassoController._sketcherCanvasSize.

Reproduced locally with
  flutter test -d macos examples/theming/integration_test
which now passes again.
The earlier isolate-safety fix returned zeros (Size.zero, 1.0,
zero-paddings) when there was no FlutterView, which silently
synthesized misleading data. Mark all view-only fields nullable so
absence is visible:

- FlutterInfo: viewPadding, physicalSize, pixelRatio, viewInsets,
  gestureInsets
- AllMetaData: windowInsets, windowPadding, windowPixelRatio,
  windowSize, platformGestureInsets

Dispatcher-derived fields (platformLocale, platformSupportedLocales,
platformBrightness, textScaleFactor) stay non-null because
PlatformDispatcher.instance is available on any isolate.

JSON serialization now skips the nullable fields when they are
null, the same way other optional metadata is handled.
The macOS-latest runners frequently fail to foreground the
integration test app ('Failed to foreground app; open returned 1')
and the job then hangs until the default 6h timeout. Add a
15-minute job-level timeout and an 8-minute step-level timeout so
a known-bad run fails fast instead of clogging the runner pool.
macOS-latest runners can't foreground the test app reliably and the
job hangs. Linux + Xvfb gives the integration test a virtual
display, so flutter test -d linux integration_test can launch the
app without needing a real GUI session.
@passsy passsy force-pushed the fix/flutter-stable-analyzer branch from 2fb6888 to b47a042 Compare May 26, 2026 19:10
passsy added 6 commits May 26, 2026 23:22
Look up the view via PlatformDispatcher.views.firstOrNull at capture
time instead of carrying it through the constructor. Promote the
shared body to a top-level captureBaseFlutterInfo() and replace the
three view != null ternaries with a file-private ViewPadding
extension that pairs with ?. short-circuiting.
Replace deprecated spot APIs:
- spotSingleText → spotText
- spotTexts → spotText (not spot<Text>().whereText())
- spotSingle<X>() → spot<X>()
- spotSingle<X>(children:) → spot<X>(children:)
- SingleWidgetSelector/Snapshot → WidgetSelector/Snapshot
- hasProp(selector:) → hasWidgetProp(prop:)
- Custom _tap → act.tap
- waitUntil(isNot(screenshotDrawing)) was a pre-existing
  bug — should wait for drawing, not not-drawing
- 'first trackEvent() is fast' relaxed to <=100ms since
  act.tap now pumps a frame
passsy added 4 commits May 27, 2026 02:56
The v3 serializer now omits platformGestureInsets, windowInsets,
windowPadding, windowPixelRatio and windowSize when the metadata was
captured without a FlutterView (background isolate). The parser still
cast every one of them as present and non-null, so any persisted item
in that state failed to deserialize and got dropped on the next
retrieval. Read them as optional and skip windowSize when missing.

Add two tests:
- parse v3 JSON with all FlutterView-derived fields omitted
- round-trip a PendingFeedbackItem whose metadata has all view fields
  null, and assert the serializer drops the keys rather than writing
  nulls
^3.11.5 was both unusual (^ on an SDK constraint, inconsistent with
the rest of the repo) and missing a Flutter floor. Use the standard
>= / < pair and pin Flutter to >=3.22.0, when --wasm became stable.
Removing widget_binding_support.dart left an empty line where the
import used to sit. dart format tolerates it but the original files
had no blank between flutter and wiredash imports — restore that.
spot's loadAppFonts() reads wiredash's pubspec and registers Inter /
Wirecons under their bare family names. But wiredash's TextStyles and
IconDatas set package: 'wiredash', which Flutter resolves to
packages/wiredash/Inter / packages/wiredash/Wirecons at lookup time.
Without the prefixed registration the engine falls back to Ahem and
all Wiredash text + icons render as empty boxes in test screenshots.

Re-register both families under the package-prefixed names right
after loadAppFonts(). Remove once spot bridges this for packages
testing their own fonts (passsy/spot#141).
@passsy passsy merged commit 928abb2 into stable May 27, 2026
7 checks passed
@passsy passsy deleted the fix/flutter-stable-analyzer branch May 27, 2026 01:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Wasm support

1 participant