Skip to content

flutter-tvos v1.1.0 — the porter release#11

Merged
DenisovAV merged 45 commits into
fluttertv:mainfrom
MAUstaoglu:feat/plugin-port-tooling
May 25, 2026
Merged

flutter-tvos v1.1.0 — the porter release#11
DenisovAV merged 45 commits into
fluttertv:mainfrom
MAUstaoglu:feat/plugin-port-tooling

Conversation

@MAUstaoglu
Copy link
Copy Markdown
Member

@MAUstaoglu MAUstaoglu commented May 24, 2026

Minor release — the "porter release". v3.41.9-tvos.1.0.1 had no
lib/plugin_porting/ or flutter-tvos plugin port command at all;
this PR ships the entire 7-phase porter for scaffolding federated
*_tvos plugins from existing iOS / macOS plugins, plus first-class
flutter-tvos create --platforms=tvos, a build-time nudge for
plugins missing tvOS support, and a handful of build-pipeline fixes.

No Flutter SDK or engine-artefact change — pinned versions match
v1.0.1 (Flutter 3.41.9 / 00b0c91f06, tvOS engine artefacts
v1.0.0-flutter3.41.9). Release tag will be v3.41.9-tvos.1.1.0
per the existing v<flutter-version>-tvos.<x.y.z> scheme.

Highlights

Added

  • flutter-tvos plugin port — new command. Scaffolds a
    federated <plugin>_tvos sibling from any existing iOS or macOS
    Flutter plugin. Source loaders: positional local path,
    --from-pub <name>, or --from-git <url> --ref <ref>. The 11
    packages at fluttertv/plugins
    / pub.dev under fluttertv.dev
    were all produced this way. The transformer:

    • widens Swift #if os(iOS) / #elseif os(macOS) and ObjC
      #if TARGET_OS_IOS so tvOS follows the iOS branch;
    • widens @available iOS X, * / #available /
      API_AVAILABLE(ios()) to also list tvOS X;
    • matches a compatibility DB of tvOS-incompatible APIs (WebKit,
      SafariServices, LocalAuthentication, CoreLocation,
      CaptiveNetwork, NEHotspot, StoreKit code-redemption,
      UIPasteboard, AVAudioSession Bluetooth / speaker options,
      CoreTelephony, GoogleSignIn SDK, …) and either strips the
      import + stubs the enclosing method-channel handler, or wraps
      type-level uses behind #if !os(tvOS) (graceful partial port);
    • collapses modern multi-target SwiftPM packages into one
      CocoaPods module;
    • emits a buildable Swift skeleton for FFI / native-assets
      plugins the tvOS toolchain can't build for as-is;
    • prunes cross-platform Dart from the generated lib/ (drops
      _plus-style packages' Linux / Windows / Web / macOS /
      Android implementations + scrubs their imports).

    Every transformation is recorded in a PORTING_REPORT.md
    written alongside the package.

  • flutter-tvos create --platforms=tvos / --tvos-only
    genuine tvOS-only project scaffold (no more
    create-iOS-then-strip).

  • Build-time warning for plugins missing tvOS support. Each
    plugin in the app's dep graph that has a FlutterTV-published
    <name>_tvos sibling the user hasn't added is surfaced as a
    one-line warning during flutter-tvos build/run. Keyed on the
    user-facing aggregator name so _darwin / _android /
    _linux siblings don't produce duplicate or noisy warnings.

  • New doc/port-plugin.md — proper user guide for plugin port (quick start, full flag reference, what the transformer
    does and doesn't do, after-porting workflow, troubleshooting).

Fixed

  • Generated.xcconfig now reads FLUTTER_BUILD_NAME /
    FLUTTER_BUILD_NUMBER from the app's pubspec.yaml version:
    (1.2.3+41.2.3 / 4). Previously hardcoded to 1.0.0 /
    1 unless overridden via --build-name / --build-number,
    which broke package_info_plus and any code reading
    CFBundleShortVersionString / CFBundleVersion.
  • FLTAssetsPath is now baked into the generated Info.plist so
    plugin asset lookup resolves on a real Apple TV (not just the
    simulator).
  • Device builds now pass -allowProvisioningUpdates to xcodebuild
    so the first build on a fresh team / device can refresh
    provisioning profiles itself.
  • tvOS build skips the Dart native-assets target so apps that
    transitively pull in an FFI / native-assets plugin (e.g.
    path_provider_foundation, package_info_plus) no longer fail
    with Target native_assets required define SdkRoot on first
    build (issue Target native_assets required define SdkRoot but it was not provided #3).

Documentation

  • README's plugin-porting section is now a 4-line pointer to
    doc/port-plugin.md; the platform-key paragraph points at the
    published fluttertv/plugins
    repo and the fluttertv.dev
    verified publisher in place of "FlutterTV-curated index being
    assembled and will be published soon".
  • doc/ index gains "Porting an existing plugin" under "Plugin
    development".

Verification

  • dart analyze lib/ — 0 errors, 0 warnings
  • dart test test/general/ — 191 / 191 passing
  • Built and verified end-to-end: scaffolded the 11 packages at
    fluttertv/plugins via
    --from-pub, published them to pub.dev under
    fluttertv.dev,
    and exercised every one in a sample app on the Apple TV
    simulator.

See CHANGELOG.md for the full per-line entry.


Closes #3.

MAUstaoglu added 30 commits May 3, 2026 20:45
…S / macOS plugins (phase 1)

Adds `flutter-tvos plugin port <source-dir>` — a one-shot scaffolding
command that takes any existing iOS or macOS Flutter plugin and produces
a sibling `*_tvos` package conforming to the FlutterTV plugin standards.
The source is never modified; the porter writes a brand-new federated
sibling that depends on the same platform interface.

This commit is phase 1 of the plan documented in `docs/PLUGIN_PORTING.md`.
Phases 2–7 layer on top of this skeleton:
  * phase 2 — copy native source into `tvos/Classes/` verbatim
  * phase 3 — compatibility database + Swift transformer that comments
              out and stubs iOS-only API call sites, generates
              PORTING_REPORT.md
  * phase 4 — Objective-C transformer
  * phase 5 — `--include-example` to add `tvos/` to the source plugin's
              example app
  * phase 6 — `--from-pub` and `--from-git`
  * phase 7 — docs, tab completion, real-plugin validation

What works today
- `flutter-tvos plugin port <path>` reads the source's pubspec, locates
  the plugin class via `flutter.plugin.platforms.<ios|macos>`,
  detects Swift vs Objective-C from `<platform>/Classes/`, finds the
  federated platform interface package, and writes a complete package
  skeleton:
    pubspec.yaml          (with flutter.plugin.platforms.tvos wired up)
    README.md
    CHANGELOG.md
    LICENSE               (copied from source if present)
    analysis_options.yaml
    .gitignore
    lib/<plugin>_tvos.dart
    test/<plugin>_tvos_test.dart
    tvos/Classes/<PluginClass>.swift              (stub)
    tvos/Classes/<PluginClass>-Bridging-Header.h
    tvos/<plugin>_tvos.podspec
- Flags: `--output`, `--base-platform={ios,macos}`, `--license-holder`,
  `--force` (overwrite), `--dry-run` (preview without writing).
- Subcommand structure: `flutter-tvos plugin` is the umbrella; `port`
  is its first subcommand. Future `lint` / `publish` subcommands slot
  in alongside.
- Sensible refusals: pure-Dart plugins (no `*_tvos` needed — they
  federate via the platform interface), plugins already targeting tvOS,
  plugins with neither iOS nor macOS implementations, plugins missing
  a `pluginClass` declaration.

Conventions enforced by the templates
- Output package name strips `_ios`, `_macos`, `_foundation`, `_darwin`
  so `path_provider_foundation` → `path_provider_tvos` (not
  `path_provider_foundation_tvos`).
- Generated podspec deliberately omits `s.dependency 'Flutter'` — the
  Flutter pod doesn't declare tvOS support, so depending on it breaks
  `pod install` for tvOS consumers. Flutter.framework is resolved via
  FRAMEWORK_SEARCH_PATHS, populated by the host app's Podfile (same
  mechanism `templates/app/swift/tvos.tmpl/Podfile` uses).
- Generated Dart entry declares `base class <X> extends <Interface>Platform`
  — Dart 3 requires the `base` modifier when extending a `base class`
  from a platform interface. This is the single most common breakage
  when hand-porting plugins; the scaffold gets it right by default.

Tests (all 93 passing — was 81, +12 new)
- SourceAnalyzer covers pubspec parsing, name derivation across the
  four platform suffixes, Swift vs ObjC detection, ios→macos fallback
  with prefer=ios, and all four refusal paths.
- Scaffolder covers the full file set (pubspec contains the right
  pluginClass / dartPluginClass, podspec has no `s.dependency 'Flutter'`,
  Dart entry imports the platform interface, etc.), `--dry-run`,
  `--force` overwrite semantics, and LICENSE copying.

Smoke-tested by running the command on a hand-built `url_launcher_ios`
fixture: produces a valid scaffold, `dart analyze lib test` clean
across the package after generation, `dart test test/` runs the
package's seed test successfully.

Documentation
- `docs/PLUGIN_PORTING.md` is the canonical design and phase plan;
  treat it as the spec, fix the doc if the code disagrees.
- `flutter-tvos/CLAUDE.md` gets a `## Plugin Porting` section pointing
  at the doc and explaining the file layout.

Files
- new: lib/commands/plugin.dart                     (umbrella)
- new: lib/commands/plugin_port.dart                (port subcommand)
- new: lib/plugin_porting/source_analyzer.dart      (pubspec/source detection)
- new: lib/plugin_porting/scaffolder.dart           (file orchestration)
- new: lib/plugin_porting/templates.dart            (rendered file bodies)
- new: test/general/plugin_port_test.dart           (analyzer + scaffolder tests)
- new: docs/PLUGIN_PORTING.md                       (design + plan)
- modified: lib/executable.dart                     (registers TvosPluginCommand)
… plugin

Phase 1 always wrote a stub Swift class with a single
`result(FlutterMethodNotImplemented)` body, leaving the user to paste
their iOS implementation in by hand. Phase 2 walks the source plugin's
`<platform>/Classes/` directory and copies every Swift / Objective-C
source file (`.swift` / `.h` / `.m` / `.mm`) verbatim into the output's
`tvos/Classes/`, preserving subdirectory structure.

Net effect: porting `url_launcher_ios → url_launcher_tvos` produces a
package that already contains the real iOS implementation. The user's
remaining work shrinks from "rewrite everything from scratch" to
"strip the imports/calls that are not available on tvOS." Phase 3 will
do most of that stripping automatically via the compatibility database.

Behaviour
- Native files under `<platform>/Classes/` (recursively) are copied to
  `tvos/Classes/`. Subdirectory structure is preserved — a helper at
  `ios/Classes/Helpers/UrlValidator.swift` lands at
  `tvos/Classes/Helpers/UrlValidator.swift`.
- `<platform>/Resources/` is copied to `tvos/Resources/` if it exists.
  Filtering is by extension for sources (`.swift`, `.h`, `.m`, `.mm`)
  but unfiltered for resources, since plugins can ship anything there
  (xib, asset catalogs, .strings, plist, PNGs).
- When the source has no native files at all (rare — e.g. plugins
  where Pigeon generates everything at build time, or the source's
  `Classes/` is empty), the scaffolder falls back to the Phase-1
  stub Swift class plus its bridging header. So `flutter-tvos plugin
  port` always produces a buildable package, regardless of whether
  the source has copyable native code.
- The bridging-header stub is only written in fallback mode — when
  the real Swift source is copied, the user's existing source already
  has whatever ObjC interop it needs. No more confusing
  `URLLauncherPlugin-Bridging-Header.h` showing up next to a real
  Swift source.

Tests added (now 16 in plugin_port_test.dart, was 12; 97/97 across the
package, was 93)
- `copies Objective-C sources verbatim` — both .h and .m round-trip
  unchanged; no Swift stub appears alongside.
- `preserves subdirectory structure under Classes/` — nested helper
  files land at the right depth in the output.
- `copies <platform>/Resources/ when present` — including nested
  asset catalogs (`Assets.xcassets/Contents.json`) and `.strings`
  files.
- `falls back to Swift stub when source has no native files` —
  guarantees the buildable-package promise even when the source's
  Classes/ is empty.
- The existing "writes a complete federated package skeleton" test
  was updated to assert the source content is copied verbatim, not
  the Phase-1 stub.

Smoke-tested by running the new command on a hand-built `url_launcher_ios`
fixture with a realistic `URLLauncherPlugin.swift` and a
`Resources/Localizable.strings`. Both files landed in the correct
locations in the generated `url_launcher_tvos` package; no bridging
header was emitted.

Files
- modified: lib/plugin_porting/scaffolder.dart
  (new helpers `_collectNativeCopies`, `_collectResourceCopies`,
  `_collectByExtension`, `_resolveResourcesDir`; new `_NativeCopy`
  type; the Phase-1 stub plan entries gated behind the empty-source
  fallback)
- modified: test/general/plugin_port_test.dart
  (4 new tests + realistic Swift/ObjC fixture content)
Phase 3 work-in-progress so plugin-port branch is shippable. Still TODO:
- report_emitter.dart (PORTING_REPORT.md generator)
- wire SwiftPorter through Scaffolder
- per-pattern + transformer + e2e tests

Saved as WIP so main branch can be shared without phase-3 churn.
Bring plugin-port up to date with main (1.0.4): web-compatible FFI,
RCU init refactors, doc/version bumps. No conflicts; plugin_porting
sources and docs/PLUGIN_PORTING.md are unaffected.
Completes the Phase 3 deliverable from docs/PLUGIN_PORTING.md:

- SwiftPorter now strips iOS-only `import`s independently of the API
  usage regex (the WIP code only stripped an import if the API regex
  also matched the import line, so `import WebKit` was never removed).
- New report_emitter.dart renders PORTING_REPORT.md (§7): summary
  table, per-method ported/partial/stubbed breakdown, removed imports,
  manual-review list, checklist.
- Scaffolder runs .swift sources through SwiftPorter (instead of a
  verbatim copy), collects findings, and writes the report. .h/.m/.mm
  stay verbatim for the Phase 4 ObjC transformer.
- plugin_port gains --no-report and prints a strip/stub/review summary
  plus the "manual review required" banner.
- SourceAnalyzer exposes the source version; SwiftPortingResult
  exposes detectedMethods for the report's "ported as-is" count.
- Tests: per-pattern compatibility-db coverage, SwiftPorter unit
  tests, and an end-to-end url_launcher_ios port asserting WebKit is
  stripped and the web handlers are stubbed and reported.

Header-comment regeneration (§6.1.6) remains deferred; noted in the
doc status snapshot.
- Extract shared FindingAction/PortingFinding/PortingResult into
  porting_result.dart so Swift and ObjC porters share one contract
  (SwiftPortingResult -> PortingResult).
- New ObjcPorter: strips iOS-only `#import <Framework/...>` and
  `@import Framework;` lines (banned frameworks derived from the same
  compatibility-database stripImports), and stubs unsupported method
  handlers found by brace-tracking `isEqualToString:@"x"` dispatch
  chains. Closure is detected at the character level so `} else if
  (...) {` chains are split correctly.
- Scaffolder routes .swift through SwiftPorter and .h/.m/.mm through
  ObjcPorter; both feed the porting report.
- Tests: ObjcPorter unit tests + an end-to-end ObjC url_launcher port
  asserting WebKit imports stripped, the web handler stubbed, and the
  report naming it. Full plugin-port suite (51 tests) green;
  `dart analyze lib/` exits 0.
New ExampleExtender wires the SOURCE plugin's example/ app for tvOS
without ever touching the generated *_tvos package:

- validates example/ (lib/main.dart + Flutter pubspec); skips with a
  reason instead of failing the port when absent/odd,
- merges `dependency_overrides` (source *_ios and new *_tvos as local
  path deps) into the existing block or a new one, idempotently and
  without clobbering a user-provided override,
- appends a one-line "run on tvOS" note to example/README.md.

Generating example/tvos/ stays delegated to the existing
`flutter-tvos create` template subsystem (on-disk templates + signing,
not reproducible in-memory); the exact create/build commands are
surfaced to the user via the new --include-example flag.

Tests: 6 ExampleExtender unit tests (merge, idempotency, existing
block, no-clobber, skip, dry-run). Full plugin-port suite 57 green;
`dart analyze lib/` exits 0.
- SourceSpec: pure, validated planner for the three source forms
  (positional path / --from-pub / --from-git [--ref]); enforces mutual
  exclusion and that --ref requires --from-git; derives the checkout
  name and builds shallow `git clone` argv.
- SourceFetcher: resolves a spec to a directory. localPath returns
  immediately; git shallow-clones into a temp dir; pub resolves the
  package via a throwaway probe project + package_config.json.
- plugin_port: new --from-pub/--from-git/--ref options; fetched
  sources materialise under a system temp dir that is always cleaned
  up (try/finally), default output moves to the cwd, and
  --include-example is refused for fetched sources (its edits would
  not persist).
- Tests: SourceSpec validation matrix + SourceFetcher git/pub/local
  paths driven by FakeProcessManager. Full plugin-port suite 70
  green; `dart analyze lib/` exits 0.
User-facing documentation for `flutter-tvos plugin port`: local /
--from-pub / --from-git usage, what the transformer does (Swift + ObjC
import stripping and handler stubbing), the PORTING_REPORT.md review
step, and the useful flags. No code changes; full plugin-port suite
(70) still green, `dart analyze lib/` exits 0.

Deferred (low value / not reproducible here): interactive Continue?
prompt (--force/--dry-run already gate writes), shell tab completion,
and the internal "port 3 real plugins" validation.
Dogfooding the porter against real pub.dev plugins (url_launcher_ios,
shared_preferences_foundation, path_provider_foundation) exposed three
modern-convention gaps; all fixed here:

- Source resolution now searches `<platform>/Classes`,
  `<platform>/<pkg>/Sources/<pkg>` (Swift Package Manager),
  `darwin/<pkg>/Sources/<pkg>` (sharedDarwinSource), any other
  `Sources/<dir>`, then the platform root — `darwin/` first when
  `sharedDarwinSource: true`. Previously only `<platform>/Classes`
  then `<platform>/` were tried, so SPM/darwin plugins silently fell
  back to an empty stub.
- `pluginClass` is no longer mandatory in the pubspec: when omitted it
  is inferred from the sources (the `class X: … FlutterPlugin` /
  `@interface X : … <FlutterPlugin>` declaration), falling back to a
  derived name.
- Plugins with no native code AND no pluginClass (pure-Dart or
  dart:ffi/package:objective_c, e.g. modern path_provider_foundation)
  now exit with an advisory message ("no *_tvos package is needed")
  instead of a hard error — these already work on tvOS via Dart.
- Scaffolder filters `Package.swift`/`Package.resolved` so SPM
  manifests never land in the podspec's `Classes/**` glob.

Tests: 5 new SourceAnalyzer cases (SPM, sharedDarwinSource, inferred
pluginClass, advisory FFI exit, Package.swift exclusion). Full
plugin-port suite 75 green; `dart analyze lib/` exits 0.
Two real-world correctness fixes found while prepping the plugin repo:

- _stripPlatformSuffix now also strips the federated Apple-impl
  suffixes `_avfoundation`, `_storekit`, `_apple` (longest-first so
  `_avfoundation` isn't shortened by `_foundation`). Without this,
  `video_player_avfoundation` → `video_player_avfoundation_tvos` etc.
- The generated pubspec now copies the source's actual
  `*_platform_interface` version constraint instead of a hardcoded
  `^1.0.0` (which made `pub get` fail for every plugin whose interface
  is past 1.x); falls back to `any` when the source didn't pin one.

Tests: suffix matrix (6 cases) + constraint carry/fallback. Full
plugin-port suite 77 green; analyze clean.
- `flutter-tvos create --tvos-only` removes the non-tvOS platform
  folders (android/ios/macos/linux/windows/web) after scaffolding, so
  a tvOS plugin's example app targets only Apple TV. Compose it with
  the standard `flutter create --platforms=ios .` for a minimal
  scaffold (upstream Flutter has no `tvos` platform, so `tvos/` is
  still added by this command and the residual `ios/` is stripped).
- Generated pubspec now quotes the carried platform-interface
  constraint when it contains YAML-significant chars (`>`, `<`,
  space) — an unquoted `>=2.4.0 <3.0.0` was a YAML block scalar and
  broke `pub get` (hit by sqflite_darwin).

Tests: range-constraint quoting. Full plugin-port suite 78 green;
analyze clean.
It's our CLI, so `tvos` should be a real platform. Upstream Flutter's
`--platforms` enforces an `allowed:` whitelist at parse time (no
`tvos`), and we don't patch Flutter — so the rewrite happens at our own
argv entrypoint, the one seam we own:

- `--platforms=tvos`        → `--platforms=ios --tvos-only`
  (Flutter scaffolds minimal ios, TvosCreateCommand adds tvos/, the
  residual ios/ is stripped → a clean tvOS-only project)
- `--platforms=tvos,ios,…`  → `--platforms=ios,…` (tvos dropped; tvos/
  still added; siblings kept, nothing stripped)
- no tvos / no create       → argv returned byte-identical

Logic is a pure helper (`expandTvosPlatformArgs`) in its own library so
it unit-tests with no I/O. 8 cases (forms, merge, passthrough, order).
Full analyze clean; suites green.
--platforms=tvos no longer scaffolds an iOS app and deletes it. The
new TvosAppScaffold writes the shared Dart app (pubspec, lib/main.dart,
test, analysis_options, .gitignore, README) and TvosCreateCommand
renders tvos/ on top — upstream `flutter create` is never invoked for
the tvOS-only case, so the project is tvOS-only by construction.

- `--tvos-only` is now an internal, hidden flag (the argv shim's
  signal); users only ever type `--platforms=tvos`.
- `_renderTvosRunner` extracted and shared by the standard and
  tvOS-only paths; the strip-platforms code is gone.

Smoke-tested: `create --platforms=tvos .` yields exactly
lib/ test/ tvos/ + pubspec — no ios/android/macos/etc. analyze lib
exits 0; suites green.
The generated `lib/<pkg>_tvos.dart` was a hand-written guess: it
invented the platform-interface class name and left abstract members
unimplemented, so it never compiled (every regenerated package failed
`dart analyze`).

The source plugin's own `lib/` is already a correct federated
implementation that extends the platform interface and talks to the
SAME method channel the native side registers — and the porter keeps
that channel name unchanged. So copy it verbatim, only:
  * renaming the conventional entry `lib/<src>.dart` → `lib/<out>.dart`
    (so Flutter's federated registrant resolves `dartPluginClass`), and
  * rewriting `package:<src>/…` self-imports to the output package.
Falls back to the templated stub when the source ships no Dart `lib/`.

Tests: copy+rename+rewrite, and the no-lib stub fallback. Full
plugin-port suite green; `dart analyze lib/` exits 0.
A federated `*_tvos` package is never used directly; the app imports
the app-facing plugin. The old generated example (placeholder app
depending only on `<pkg>_tvos: path: ../`) demonstrated nothing.

Rework, mirroring flutter-tizen/plugins:
- ExamplePorter reuses the app-facing plugin's real example app
  (its lib/, assets, deps), drops non-tvOS platform folders, and
  rewrites the pubspec to the federated dual-dependency form:
    dependencies:
      <base>: ^<resolved version>
      <base>_tvos: { path: ../ }
  Existing managed-key entries are replaced idempotently; other deps
  and the assets section are preserved.
- renderTvosRunner extracted from TvosCreateCommand into
  commands/tvos_runner.dart so the porter can add tvos/ to the copied
  example without re-running create.
- plugin_port --include-example now fetches the app-facing plugin via
  pub, runs ExamplePorter, and renders tvos/ (temp dir always cleaned).
- Superseded ExampleExtender + its test removed.

Tests: ExamplePorter copy/strip/dual-dep + idempotency + skip. Full
plugin-port suite 85 green; `dart analyze lib/` exits 0.
The generated test stub imported the package but never referenced it,
so every clean federated port still failed `dart analyze` with one
`unused_import` warning. Now it references the federated `dartPluginClass`
(verifying the package compiles and the class is reachable); when there
is no Dart plugin class it emits a dependency-free smoke test instead of
an unused import. Generated federated packages now analyze with zero
issues.
Real tvOS-simulator build of shared_preferences_tvos failed at
xcodebuild:

  messages.g.swift:14: error: Unsupported platform.

Pigeon (and many plugins) gate the Flutter module with
`#if os(iOS) import Flutter #elseif os(macOS) import FlutterMacOS
#else #error("Unsupported platform.") #endif`. tvOS fell into the
`#else`. The tvOS embedder ships the same `Flutter` module as iOS, so
the Swift porter now rewrites that specific guard's `#if os(iOS)` to
`#if os(iOS) || os(tvOS)`. Narrow: only an `#if os(iOS)` whose first
directive is `import Flutter` is touched; behaviour `#if os(iOS)`
blocks are left alone.

Tests: guard widened; non-guard block untouched. Suite green; analyze
clean.
Real tvOS-simulator build advanced past the import guard and then
failed on the messenger branch: upstream splits
`#if os(iOS) registrar.messenger() #else registrar.messenger`
(macOS). tvOS fell into the macOS branch, but the tvOS embedder
mirrors the iOS Flutter API, so `messenger` must be `messenger()`.

Generalised the earlier narrow import-guard rule: in every `#if` /
`#elseif` directive, widen each `os(iOS)` to `(os(iOS) || os(tvOS))`
(parenthesised so `&&`/`!` precedence is preserved). tvOS now takes
the iOS code paths everywhere; iOS-only APIs inside are still stubbed
by the compatibility-database passes.

Tests updated: import guard + behaviour/messenger branches widened,
compound `&&` precedence kept, non-iOS directives untouched. Suite
green; analyze clean.
Some plugins use APIs that simply do not exist on tvOS at type /
top-level scope (e.g. url_launcher: SFSafariViewController /
SFSafariViewControllerDelegate; WebKit). The porter can stub a
method-channel handler body, but it cannot invent a type the class
declaration depends on, so those packages fail only at Xcode time.

Now detected from the existing signal: a `taggedWithTodo` finding =
an `unsupported` API used outside a stubbable handler. When present:

- plugin port prints a loud warning ("NOT buildable on tvOS as-is —
  uses <APIs> at type level …"), in both real and --dry-run output;
- PORTING_REPORT.md gets a top banner ("⚠️ This plugin is not
  buildable on tvOS as-is") and the summary row becomes
  "tvOS build outlook | ❌ will NOT compile" vs "✅ expected to
  compile" (replacing the always-0 "Compile errors expected" row).

So a capability that's impossible on tvOS is reported clearly before
build, instead of surfacing as a cryptic xcodebuild error. Tests:
type-level unsupported → NOT-buildable banner; handler-only → expected
to compile. Suite green; analyze clean.
Empirically tested path_provider on the tvOS simulator: a tvOS-only
app depending on path_provider directly FAILS to build —

  Target native_assets required define SdkRoot but it was not provided
  The build failed.

So the prior advisory ("pure Dart-FFI … already works on tvOS, no
*_tvos package needed") was an unverified claim and is wrong for the
flutter-tvos toolchain: modern path_provider_foundation is a
dart:ffi / native-assets plugin (package:objective_c) and the tvOS
build pipeline does not feed SdkRoot to the native-assets hook.

Split the no-native-sources advisory into two honest cases:
- ffi/objective_c/build-hook present → "NOT supported by the
  flutter-tvos build today (native-assets SdkRoot); a *_tvos package
  won't help; needs engine native-assets support or a manual port."
- genuinely pure-Dart → federates via the platform interface; no
  *_tvos package needed.

Tests updated to assert both branches. Suite green; analyze clean.
…rovider seed)

dart:ffi / native-assets plugins (path_provider_foundation, …) can't be
built for tvOS by the toolchain and we don't patch Flutter. Instead of a
dead-end advisory, the analyzer now flags these (ffiNativeAssets) and the
scaffolder emits a NATIVE federated *_tvos package — a Swift
method-channel plugin + a Dart class extending the plugin's platform
interface — the same model as flutter-tizen and as the verified
shared_preferences_tvos.

- NativeSkeleton: pure (path→content) generator; reuses the shared
  templates for pubspec/podspec/etc.
- Seed registry: path_provider is fully implemented (NSTemporaryDirectory
  / NSSearchPathForDirectoriesInDomains for temp/docs/support/library/
  cache; external/downloads throw UnsupportedError). Unseeded FFI plugins
  get a buildable skeleton (handlers return FlutterMethodNotImplemented,
  Dart inherits the interface's throwing defaults) + a PORTING_REPORT
  checklist.
- This is the general, in-policy CLI mechanism for current and future
  FFI plugins: seeded → works; unseeded → buildable skeleton to finish.

Tests: analyzer ffiNativeAssets flag + names; seeded path_provider
package contents (Dart/Swift/pubspec/report); unseeded skeleton. Full
plugin-port suite green; dart analyze lib/ exits 0.
Apps that depend (often transitively) on an FFI/native-assets plugin
— e.g. path_provider drags in the endorsed path_provider_foundation —
failed the tvOS build with "Target native_assets required define
SdkRoot". flutter_tools' code-asset path is iOS/macOS-only and we do
not patch it.

Two in-policy changes, entirely within flutter-tvos:
- TvosCopyFlutterBundle no longer pulls DartBuildForNative() /
  InstallCodeAssets() into the tvOS build graph. tvOS cannot build
  Dart code-assets, and federated tvOS plugins are plain Swift built
  via CocoaPods — their Dart side is already routed by the existing
  tvOS plugin-registrant override, so the native-assets step is
  genuinely unnecessary on tvOS, not skipped as a hack.
- Since upstream CopyFlutterBundle still bundles native_assets.json as
  NativeAssetsManifest.json, TvosCopyFlutterBundle.build() now writes
  the canonical empty manifest ({"format-version":[1,0,0],
  "native-assets":{}}) first so the bundle copy succeeds.

Verified on the tvOS simulator: an app using `path_provider` (with the
generated native federated path_provider_tvos) now builds AND runs,
returning real temp/documents/support directories. `dart analyze
lib/` exits 0; full plugin-port suite green.
…packages

A generated native federated *_tvos (FFI path, e.g. path_provider_tvos)
had no example/, so it couldn't be tried. NativeSkeleton now also emits
a tvOS-only example: example/pubspec.yaml depending on `<base>: any` +
`<base>_tvos: { path: ../ }`, example/lib/main.dart (path_provider's is
tailored to show temp/documents/support; unseeded gets a minimal app),
plus analysis_options/.gitignore/README. plugin_port renders the
example's tvOS runner for the FFI case (no fragile upstream-monorepo
example copy). Run with: cd <pkg>/example && flutter-tvos run.

Tests assert the example pubspec deps + main.dart. Suite green;
`dart analyze lib/` exits 0.
The native-skeleton generator had a hand-written path_provider
implementation + a seed registry baked into the CLI. That couples a
generic build tool to one plugin's domain logic, rots against
platform-interface drift, and is the wrong layer/repo.

NativeSkeleton now ALWAYS emits a generic, buildable native federated
skeleton: federated pubspec/Dart (extends the platform interface,
inherits its throwing defaults), a Swift method-channel stub returning
FlutterMethodNotImplemented, a runnable tvOS-only example, and a
PORTING_REPORT checklist that points implementers at the plugins repo.
No `_seeds`, no per-plugin Swift/Dart, no special-casing anywhere in
lib/. Tests use a synthetic fixture (no real plugin names) so the CLI
repo carries zero plugin specificity.

Finished, working `<plugin>_tvos` implementations (path_provider etc.)
are authored and maintained in plugins/packages/, like
flutter-tizen/plugins — never in the porter.

dart analyze lib/ exits 0; plugin-port suite green.
Per the standing rule that the CLI repo carries zero plugin
specificity (code AND tests), every real Flutter plugin name used as a
test fixture is replaced with a synthetic one (gadget, widgetbox,
prefsbox, dbbox, vidbox, audbox, …). Federated suffixes
(_ios/_macos/_foundation/_darwin/_avfoundation/_storekit/_apple),
*_platform_interface naming, and derived class names are preserved so
the suffix-stripping / naming / FFI tests still exercise the same
conventions — only the names changed, not the logic or assertions.

grep for real plugin names across the plugin-port tests now returns
nothing. Full suite: 89 tests green; `dart analyze test/` clean.
Physical-device (`appletvos`) builds failed at signing:
"Automatic signing is disabled and unable to generate a profile …
pass -allowProvisioningUpdates to xcodebuild." flutter-tvos already
plumbs DEVELOPMENT_TEAM/CODE_SIGN_STYLE=Automatic but, unlike upstream
`flutter build ios`, never told xcodebuild it may create/update the
provisioning profile.

Mirror upstream Flutter iOS: add `-allowProvisioningUpdates` to the
device xcodebuild invocation (NativeTvosBundle). Simulator builds are
unaffected (not code-signed). Still requires a signed-in Apple
Developer team — this only lets automatic signing mint the profile
when one exists. analyze lib/ exits 0.
Modern flutter/packages plugins split native code across several
SwiftPM targets under one Sources/ dir (a Swift API target plus
Objective-C <pkg>_objc / <pkg>_ios / <pkg>_macos targets). The
analyzer only resolved the first target, silently dropping the
rest, so such plugins failed to compile on tvOS.

Now the porter detects the multi-target layout, copies every
sibling target preserving structure (so cross-target relative
imports keep resolving), drops the macOS-only target (tvOS uses
the iOS sibling), widens ObjC TARGET_OS_IOS guards the same way
Swift os(iOS) is widened, and emits a podspec that collapses the
targets into one CocoaPods module exactly as the upstream
package's own podspec does.

Verified end-to-end: video_player_avfoundation now ports, builds,
and renders actual video frames on the tvOS simulator.
Federated Apple plugins resolve flutter_assets via
Bundle.main.path(forResource:ofType:) with a
Bundle.main.bundleURL-relative fallback gated #if os(macOS).
On tvOS the primary lookup fails the same way it does on macOS
(nested flutter_assets/ paths don't resolve), so every
Controller.asset(...) threw "Asset ... not found" — e.g. the
official video_player example.

SwiftPorter now widens that guard to also run on tvOS, scoped to
the asset-fallback idiom (keyed on Bundle.main.bundleURL in the
block) so `#if os(macOS) import FlutterMacOS` branches stay
macOS-only — tvOS still takes the widened os(iOS) Flutter branch.

Verified: regenerated video_player_tvos; the official upstream
example loads and renders its bundled asset video on tvOS.
The prebuilt tvOS device engine's +[FlutterDartProject
lookupKeyForAsset:] defaults to the iOS AOT path
Frameworks/App.framework/flutter_assets/<asset>, but flutter-tvos
copies flutter_assets to the app bundle root. Every plugin that
resolves a bundled asset via lookupKeyForAsset: (e.g.
VideoPlayerController.asset) therefore received a path that does
not exist on a physical Apple TV (it worked on the simulator only
because that engine returns a bundle-root key).

Set the engine's documented override FLTAssetsPath=flutter_assets
in the generated app Info.plist so the lookup key matches where
the build actually places assets. Fixes asset loading generically
for all asset-using plugins.

Verified on a physical Apple TV: VideoPlayerController.asset now
returns INIT_OK/RENDER_OK with only CLI-generated artifacts.
MAUstaoglu added 11 commits May 17, 2026 20:12
Upstream plugin examples are wired for the source monorepo: many
declare `resolution: workspace` (a pub-workspace member) and a
`dependency_overrides:` block that re-points packages at sibling
`path:`s of that monorepo. Copied verbatim into the generated
package those make the detached example fail `pub get`
("found no workspace root" / "path which doesn't exist"), so the
example could never build or run its integration tests.

ExamplePorter now strips the workspace directive and the entire
dependency_overrides block. The porter already pins the real
`<base>` from pub.dev and wires the local `<base>_tvos`, so the
overrides are both unnecessary and wrong here; the platform
interface now resolves from pub.dev normally.

Verified: wakelock_plus_tvos example integration tests go from
"Failed to update packages" to PASS; audioplayers / sqflite
examples now resolve and build (their remaining failures are
unrelated tvOS-API issues, not pub wiring).
dart analyze on the porter branch was clean (0 errors, 0 warnings).
Of 114 info lints, 104 are the codebase's deliberate explicit-local-
type house style (consistent with vendored flutter_tools — left as
is). This clears the 10 genuine nits:

- drop redundant porting_result import (re-exported via swift_porter)
- fix 4 broken dartdoc [refs] -> code spans
- rewrap WebKit note so the URL is one literal (no adjacent-string
  whitespace lint, URL intact)
- $packageName interpolation, drop redundant dryRun: false arg,
  single-quote two test strings

Analyzer now reports only the 104 house-style infos; 92 porter
tests pass.
Previously a type-level tvOS-incompatible API made the command
refuse and write nothing. Many such plugins are mostly portable
(e.g. flutter_secure_storage: Keychain works on tvOS, only the
optional LAContext biometric path doesn't), so rejecting the whole
package was over-strict.

Now the porter never refuses. A type/top-level use the porter
can't stub behind a method channel has its enclosing declaration
(property, member, or whole type) wrapped in `#if !os(tvOS)` /
`#if !TARGET_OS_TV` (new `disabledOnTvos` finding). The package
still compiles on tvOS with that feature disabled; everything else
is ported normally. PORTING_REPORT.md gains a "Disabled on tvOS"
section + honest build outlook ("partial — N region(s) disabled")
so the developer knows exactly what to hand-port.

Removed the fail-fast gate and --allow-unbuildable flag. Exclusion
is best-effort and brace-shallow; a symbol referenced widely may
still need manual cleanup — flagged, not silently broken (per the
agreed contract). 92 porter tests pass; analyzer clean.
…ngle-target podspec

From the graceful-partial sweep reports:

- Compatibility DB: add AVAudioSessionOptions
  (.defaultToSpeaker / .allowBluetooth(A2DP)) and CoreTelephony
  (CTTelephonyNetworkInfo / CTCarrier / …) as unsupported, so the
  porter disables those regions on tvOS instead of build-failing
  (audioplayers, flutter_tts, permission_handler).

- Modular-SwiftPM podspec now also applies to a SINGLE-target
  package that uses the SwiftPM `include/<module>/` public-headers
  convention (new PluginSource.spmModularHeaders), not just
  multi-target. Without it the `#import "include/<mod>/X.h"` paths
  break once CocoaPods flattens the framework headers. Fixes
  sqflite_darwin → it now builds GREEN on tvOS. Packages without an
  `include/` dir keep the legacy flat globs (no regression).

94 porter tests pass; analyzer clean. flutter_tts/permission_handler/
audioplayers now detect+disable+document the new APIs but still hit
the accepted scoped-exclusion cascade (emit + flag in summary, per
the agreed contract) — clean compile needs the hand-edits the
PORTING_REPORT points to.
Upstream `_plus`-style packages (connectivity_plus, device_info_plus,
wakelock_plus, package_info_plus, flutter_tts, …) bundle Dart
implementations for Linux / Windows / Web / macOS / Android alongside
the iOS one. The porter was copying all of them verbatim into the
generated `*_tvos` package's `lib/`. None of those files are reachable
at runtime on tvOS (the federated registrar loads only the tvOS plugin
class) and their transitive imports (`package:web`,
`flutter_web_plugins`, `win32`, `package:nm`, …) are not in the
generated pubspec — shipping them inflates the package and breaks
`pana` / `dart pub publish` analysis.

Scaffolder now:

- Drops `.dart` files whose path/name marks them as a non-Apple
  platform implementation (`_(linux|windows|web|android|macos|osx|io)
  (_plugin)?\.dart$` suffix, or `web/`/`web_impl/`/`windows/`/`linux/`
  /`android/` path segment). `_ios*` files and prefix-form data
  classes (e.g. `macos_device_info.dart`) are kept — UIKit/UIDevice
  exist on tvOS and data classes have no platform-specific imports.
- Scrubs `import`/`export` directives in the remaining files that
  point at dropped paths — including the `if (dart.library.js_interop)`
  conditional form — replacing them with a `// (pruned …)` placeholder
  so line numbers stay stable.
- Reports what was dropped via `ScaffoldResult.prunedDartFiles`, the
  CLI's "Pruned N cross-platform Dart file(s)" line, and a new
  "Cross-platform Dart pruned" section in `PORTING_REPORT.md`.

Verified end-to-end against `connectivity_plus` from pub: 3 files
dropped (`src/connectivity_plus_linux.dart`,
`src/connectivity_plus_web.dart`, `src/web/dart_html_connectivity_plugin.dart`),
conditional export in the entry file replaced with the pruner
placeholder, output count drops from 16 → 13 files. Two new scaffolder
tests cover the suffix/prefix distinction and the conditional-export
scrub; the rest of the porter suite (53 tests) stays green.
`_generateXcconfigs` only honoured `--build-name` / `--build-number`
CLI flags. When neither was passed (the usual case) it baked the
hardcoded defaults `1.0.0` / `1` into the generated xcconfig, so the
app's `CFBundleShortVersionString` / `CFBundleVersion` ignored the
`version: 1.2.3+4` line in `pubspec.yaml`. That broke
`package_info_plus_tvos` integration tests and would surface
incorrect data to any tvOS app reading version metadata.

Resolution order now matches what iOS gets through
`xcode_backend.dart`'s build phase script:
  1. CLI flag (`--build-name` / `--build-number`)
  2. `project.manifest.buildName` / `buildNumber` (parsed from
     `pubspec.yaml`'s `version:` field — e.g. `1.2.3+4`)
  3. Canonical defaults (`1.0.0` / `1`)

Verified end-to-end against `package_info_plus_tvos`'s upstream
integration test: previously failed `buildNumber` expected `4` got `1`;
now passes 2/2 (`fromPlatform`, `example`).
`flutter-tvos plugin port` had a long inline section in the top-level
README and no entry in doc/. Now there's a proper user-facing guide
(doc/port-plugin.md) matching the existing doc style (quick start,
flag table, what it transforms, what it can't do, after-porting
checklist, troubleshooting) with the published `fluttertv/plugins`
repo as a worked-examples reference.

README:
  - the dedicated "Add tvOS support to an existing plugin" section
    is now a 4-line stub that links to the new doc and to
    `fluttertv/plugins`;
  - the platform-key section's outdated "FlutterTV-curated index
    being assembled and will be published soon" line now links to
    the published packages and the new doc;
  - "Plugin development" docs list gains a third entry.
When `flutter-tvos build/run` prepares a project, any plugin in the
dep graph that has a FlutterTV-published `<name>_tvos` sibling the
user hasn't added is surfaced as a one-line warning:

    audioplayers_tvos is available on pub.dev under the fluttertv.dev
    verified publisher. Did you forget to add it to pubspec.yaml?

The known-plugins map (`_kKnownTvosPlugins`) is keyed only on the
user-facing aggregator name (`audioplayers`, not `audioplayers_darwin`
or `audioplayers_android`). Aggregator-vs-impl deduplication then
happens implicitly — when an app pulls in `audioplayers` the dep
graph lands `audioplayers`, `audioplayers_darwin`,
`audioplayers_android`, … but only the first matches a key, so the
warning fires exactly once.

The value is `List<String>` of acceptable alternative implementations
(e.g. one upstream plugin could in future have two competing `*_tvos`
ports). Empty list means the canonical `<name>_tvos` is the only fix.

Plugins outside the curated list are silently ignored — no hard-fail
and no auto-recommend-the-porter for every random plugin (that would
be presumptuous and noisy). The porter is one click away in
`doc/port-plugin.md`, linked from the README.

Implementation in `lib/tvos_plugins.dart`:
- new `_findAllPluginNames(project)` — names of every dep that
  declares `flutter.plugin`, regardless of platform support;
- new public `recommendTvosPluginsToInstall(...)` — pure function
  that turns the dep list into a list of warning lines (kept public
  so unit tests don't have to fake a project tree);
- `ensureReadyForTvosTooling` calls them in sequence and prints each
  message via `globals.logger.printWarning`.

Verified end-to-end against a synthetic `flutter-tvos create` project
with `audioplayers`, `connectivity_plus`, and `url_launcher`: three
matching `*_tvos` lines, no noise about Android/Linux/web siblings,
per-plugin lines disappear when the matching `_tvos` is added.

Tests: 6 cases in `tvos_plugins_test.dart` for the recommender
(empty dep list; single known; aggregator+impl dedupe; user already
installed; unknown silently ignored; multiple known plugins). All
pass.
Minor release — the "porter release". v1.0.1 had no
`lib/plugin_porting/` or `flutter-tvos plugin port` command at all;
v1.1.0 ships the entire 7-phase porter for scaffolding federated
`*_tvos` plugins from existing iOS / macOS plugins.

Highlights:

  • New `flutter-tvos plugin port` command (positional path,
    --from-pub, --from-git): Swift + Objective-C transformers
    that widen iOS guards and @available to tvOS, a compatibility
    database for tvOS-absent APIs (WebKit, SafariServices,
    LocalAuthentication, CoreLocation, CaptiveNetwork, NEHotspot,
    StoreKit code-redemption, AVAudioSession options,
    CoreTelephony, …), graceful partial port via #if !os(tvOS),
    multi-target SwiftPM collapsing, FFI-skeleton fallback,
    cross-platform Dart pruning, and a PORTING_REPORT.md per port.
    The 11 packages at github.com/fluttertv/plugins (pub.dev under
    fluttertv.dev) were all produced this way.
  • flutter-tvos create --platforms=tvos / --tvos-only — genuine
    tvOS-only scaffold (no more create-iOS-then-strip).
  • Build-time warning when an app pulls in a plugin that has a
    FlutterTV-published <name>_tvos sibling the user hasn't added.
  • Build pipeline fixes: FLTAssetsPath on device, xcodebuild
    -allowProvisioningUpdates, Dart-native-assets skip for tvOS
    (issue fluttertv#3), Generated.xcconfig now reads pubspec `version:`.
  • New `doc/port-plugin.md` user guide; README's inline porter
    section now a stub deferring to it; README updated to point
    at the now-published fluttertv/plugins repo and verified
    publisher.

Flutter SDK + tvOS engine artefact versions unchanged — pinned to
v1.0.1's `00b0c91f06` / `v1.0.0-flutter3.41.9`.

Release tag will be `v3.41.9-tvos.1.1.0` per the existing
`v<flutter-version>-tvos.<x.y.z>` scheme. CHANGELOG.md updated to
reflect the full scope.
Copilot AI review requested due to automatic review settings May 24, 2026 12:25
@MAUstaoglu MAUstaoglu requested review from DenisovAV and removed request for Copilot May 24, 2026 12:25
@MAUstaoglu MAUstaoglu changed the title Feat/plugin port tooling flutter-tvos v1.1.0 — the porter release May 24, 2026
Code-review follow-ups, no behaviour change.

  • Extract `_walkPluginDependencies` — `_discoverTvosPlugins` and
    `_findAllPluginNames` previously contained ~80 lines of identical
    code reading `.flutter-plugins-dependencies` +
    `.dart_tool/package_config.json` + each plugin's `pubspec.yaml`,
    differing only in the final filter (`platforms.tvos`-only vs
    "every `flutter.plugin`"). Both now reduce to a one-line
    comprehension over a shared walker. Net: 235 → 168 lines in
    the consumers, one source of truth for the parse/resolve logic
    and its error handling.
  • Rename `installed` → `depGraph` in `recommendTvosPluginsToInstall`.
    The set holds every plugin in the dep graph (the membership
    check then works correctly because the canonical `<name>_tvos`,
    if added, IS in the dep graph), but the old name read as
    "things the user has installed to fix things", which was
    misleading.
  • New `testUsingContext` integration test that exercises
    `ensureReadyForTvosTooling` end-to-end against a MemoryFileSystem
    project: writes `audioplayers` + `url_launcher` into the
    dep-graph and package_config, runs the function, asserts the
    BufferLogger captured the `audioplayers_tvos` recommendation
    and stayed silent about `url_launcher` (not in curated list).
    Guards against a future refactor silently dropping the call to
    `recommendTvosPluginsToInstall` — the existing pure-function
    unit tests wouldn't catch that.

192 / 192 tests pass, `dart analyze lib/` 0 errors / 0 warnings.
Copilot AI review requested due to automatic review settings May 24, 2026 17:56
@MAUstaoglu MAUstaoglu review requested due to automatic review settings May 24, 2026 17:58
Comment thread lib/plugin_porting/scaffolder.dart
Comment thread lib/plugin_porting/swift_porter.dart
Comment thread lib/plugin_porting/compatibility_database.dart
- scaffolder: extend directive-pruning regex to also cover `part` directives
  (previously only `import` and `export` were matched; a `part 'src/foo.dart'`
  pointing at a dropped path would survive unchanged and break the Dart build).
  `part of 'parent.dart'` is excluded by design — the `of` keyword sits
  between `part` and the opening quote so it does not match the regex.

- swift_porter: add `dotAll: true` to `_availabilityClause` so multi-line
  `@available` attributes (e.g. Pigeon-generated code split across two lines)
  are matched and widened with a `tvOS` entry. Previously such attributes were
  silently skipped, producing an "unavailable" compile error on tvOS.

- compatibility_database: rename `stripImports` → `stripSwiftImports` to
  make the Swift-only contract explicit at the type boundary. The ObjC porter
  derives framework names from these entries by stripping the `import ` prefix;
  an ObjC-syntax entry would silently produce the wrong key and never be
  commented out. The doc-comment is expanded to spell out the Swift-only
  requirement. (A `const` constructor precludes a runtime `assert` with a
  lambda, so an explicit name is the right enforcement mechanism here.)
Copilot AI review requested due to automatic review settings May 25, 2026 16:21
@MAUstaoglu MAUstaoglu requested review from DenisovAV and removed request for Copilot May 25, 2026 16:23
Copy link
Copy Markdown
Contributor

@DenisovAV DenisovAV left a comment

Choose a reason for hiding this comment

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

All three issues fixed — part directive scrubbing, multi-line @available via dotAll: true, and stripImports renamed to stripSwiftImports. LGTM.

@DenisovAV DenisovAV merged commit 30bca49 into fluttertv:main May 25, 2026
2 checks passed
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.

Target native_assets required define SdkRoot but it was not provided

2 participants