Skip to content

[flutter_tools] Add flutter capture command with screenshot/recording subcommands - #190953

Open
GhagSagar23 wants to merge 8 commits into
flutter:masterfrom
Kryonex-Labs:190587-flutter-capture-command
Open

GhagSagar23 wants to merge 8 commits into
flutter:masterfrom
Kryonex-Labs:190587-flutter-capture-command

Conversation

@GhagSagar23

@GhagSagar23 GhagSagar23 commented Aug 11, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Implements flutter capture screenshot and flutter capture recording as the new unified interface for device screenshots and screen recording (#190587).

  • flutter capture screenshot — wraps per-device screenshot logic (same as flutter screenshot)
  • flutter capture recording — wraps platform-native screen recording with --duration and --out/-o flags
  • flutter screenshot — kept as a hidden alias, points users to flutter capture screenshot

Platform support

Platform Screenshot Video Notes
Android Existing adb screenrecord Capped at 180s (platform limit)
iOS Simulator Existing simctl recordVideo SIGINT-based stop, waits for "Recording started"
iOS Device devicectl devicectl Requires Xcode 27+
macOS screencapture -x screencapture -v -V flag for duration; captures entire screen

Key design decisions

  • ScreenRecording handle returned by startScreenRecording with stop() and done
  • Signal handling via toolContext.signals.addHandler(...) with Completer<void> to keep the tool alive through adb pull/cleanup
  • Android: try/finally ensures adb pull runs even on Ctrl-C, exit code checked before pulling
  • iOS simulator: waits for stderr "Recording started" signal (with LineSplitter + 10s timeout) before starting duration timer
  • iOS device: gated on Xcode 27+, uses --json-output to verify outcome
  • Error messages are command-agnostic (works for both flutter screenshot and flutter capture screenshot)

Test plan

  • Unit tests: capture_command_test.dart — 19 tests (no device, unsupported device, screenshot/Skia success, duration validation, --out, -d device selection, SIGINT signal handling, double-SIGINT, empty file)
  • Unit tests: screenshot_command_test.dart — 11 tests (Skia success/disconnected, file validation, device screenshot, multi-device)
  • Unit tests: android_device_test.dart — screenshot + recording tests (success, 180s clamp, pull failure, cleanup, SIGINT/SIGTERM exit codes, Windows pkill fallback)
  • Unit tests: simulators_test.dart — recording success and failure
  • Unit tests: devices_test.dart — iOS device screenshot/recording (Xcode 27+, devicectl errors, locked device)
  • Unit tests: core_devices_test.dart — devicectl parameter forwarding (duration, json-output)
  • Unit tests: macos_device_test.dart — screenshot/recording with/without duration, failure
  • Manual: iOS simulator video recording produces valid MP4
  • Manual: flutter screenshot still works
  • Manual: Android emulator video recording (emulator booted, pending test)
  • Manual: macOS screenshot and video (requires Screen Recording permission)
  • Manual: iOS physical device (requires Xcode 27+, not yet available)

Fixes #190587

@GhagSagar23
GhagSagar23 requested review from a team as code owners August 11, 2026 20:21
@GhagSagar23
GhagSagar23 requested review from reidbaker and removed request for a team August 11, 2026 20:21
@github-actions github-actions Bot added platform-ios iOS applications specifically tool Affects the "flutter" command-line tool. See also t: labels. platform-macos Building on or for macOS specifically a: desktop Running on desktop team-android Owned by Android platform team team-ios Owned by iOS platform team team-macos Owned by the macOS platform team labels Aug 11, 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 introduces a new capture command to the Flutter tool, enabling users to take screenshots (flutter capture image) and record screens (flutter capture video) across Android, iOS, iOS Simulators, and macOS devices, while deprecating the old screenshot command. Feedback on the changes highlights critical issues, including incorrect usage of ProcessSignal.kill (which should be process.kill), potential error masking during Android recording failures, and the need to gracefully handle Ctrl-C signals to ensure recording cleanup and file retrieval can complete. Additionally, suggestions were made to simplify redundant file existence checks and capture stderr on macOS recording failures to improve diagnostics.

@jmagman jmagman added the CICD Run CI/CD label Aug 11, 2026
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Aug 17, 2026
@GhagSagar23
GhagSagar23 requested a review from jmagman August 17, 2026 09:22
@jmagman jmagman added the CICD Run CI/CD label Aug 17, 2026
@camsim99
camsim99 requested a review from bkonyi August 18, 2026 21:13
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Aug 19, 2026
@GhagSagar23
GhagSagar23 force-pushed the 190587-flutter-capture-command branch from 0585423 to 963368c Compare August 21, 2026 11:57

@bkonyi bkonyi 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.

First pass.

@mboetger
mboetger requested a review from bkonyi August 25, 2026 20:16
@jesswrd jesswrd added the CICD Run CI/CD label Sep 1, 2026
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Sep 5, 2026
@GhagSagar23
GhagSagar23 force-pushed the 190587-flutter-capture-command branch from 4a3e1c1 to 5761726 Compare September 6, 2026 09:21
@bkonyi bkonyi added the CICD Run CI/CD label Oct 27, 2026
@vashworth

Copy link
Copy Markdown
Contributor

Tests are failing:

flutter/packages/flutter_tools/lib/src/ios/devices.dart:1419:37: Error: The getter '_fileSystem' isn't defined for the type 'IOSDevice'.
  - 'IOSDevice' is from 'package:flutter_tools/src/ios/devices.dart' ('flutter/packages/flutter_tools/lib/src/ios/devices.dart').
 Try correcting the name to the name of an existing getter, or defining a getter or field named '_fileSystem'.
     final Directory tempDirectory = _fileSystem.systemTempDirectory.createTempSync('core_devices.');
                                     ^^^^^^^^^^^
 flutter/packages/flutter_tools/lib/src/ios/core_devices.dart:1071:12: Error: The getter '_processUtils' isn't defined for the type 'IOSCoreDeviceControl'.
  - 'IOSCoreDeviceControl' is from 'package:flutter_tools/src/ios/core_devices.dart' ('flutter/packages/flutter_tools/lib/src/ios/core_devices.dart').
 Try correcting the name to the name of an existing getter, or defining a getter or field named '_processUtils'.
     return _processUtils.start(<String>[
            ^^^^^^^^^^^^^

@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Oct 30, 2026
@GhagSagar23

GhagSagar23 commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor Author

@vashworth All fixed and tests passing locally:

  1. fd588f8 — fixed _fileSystem and _processUtils compile errors
  2. e1b935b — merged upstream/master, resolved conflicts in 3 files
  3. b23160a — fixed post-merge test failures in macos_device_test.dart

All devices_test.dart (36 tests) and macos_device_test.dart (19 tests) pass.

@GhagSagar23

Copy link
Copy Markdown
Contributor Author

@bkonyi Could you add the CICD label when you get a chance? The compile errors from the last round are fixed and tests are passing locally. Thanks!

@bkonyi bkonyi added the CICD Run CI/CD label Nov 2, 2026
@GhagSagar23
GhagSagar23 force-pushed the 190587-flutter-capture-command branch from 083c232 to e1768e3 Compare November 3, 2026 06:05
@GhagSagar23

Copy link
Copy Markdown
Contributor Author

@bkonyi Could you add the CICD label when you get a chance? The compile errors from the last round are fixed and tests are passing locally. Thanks!

@bkonyi bkonyi 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.

Thanks for the fixes @GhagSagar23! The lib compile errors are fixed, but a few things are still outstanding:

  • capture_command_test.dart and simulators_test.dart don't compile against the latest master, which is why Tree_analyze is failing (inline). Can you run dart analyze and the touched tests after merging master?
  • The Android pkill and daemon_test.dart comments from the last round were resolved but aren't addressed yet (inline). Let's keep threads open until they're addressed.
  • Still pending: the simulator --duration test, manual Android and macOS verification, and updating the stale PR description.

@GhagSagar23

Copy link
Copy Markdown
Contributor Author

Thanks for the thorough review @bkonyi! Pushed a rebase onto latest master with all inline comments addressed:

Compilation / CI fixes:

Android pkill fix:

  • Dropped the custom signalStop callback entirely. Now uses exec screenrecord so SIGHUP from adbd reaches the process directly. Added while pidof screenrecord > /dev/null; do sleep 0.1; done before adb pull to wait for MP4 finalization.
  • Updated android_device_test to match (exec screenrecord, pidof wait, removed pkill expectations).

daemon_test.dart:

  • Added supportsScreenRecording => true to FakeAndroidDevice and 'screenRecording': true to expected capabilities.

Simulator --duration test:

  • Rewrote using fakeAsync — process hangs on a Completer, time is advanced past the duration to trigger the timeout path, then the completer resolves. Verifies done stays pending until after the timeout fires.

Other cleanup:

  • core_devices.dart / devices.dart: removed single-field destructuring per nits.
  • ios/devices.dart: on Exception → on ProcessException in takeScreenshot.
  • core_devices_test.dart: removed dead takeScreenshotSuccess field.
  • macos_device.dart: added comment about whole-screen capture limitation (screencapture doesn't support per-window recording by PID).

Test results:

dart analyze — no issues on all changed files.

Suite Tests Status
capture_command_test.dart 14/14 ✅
daemon_test.dart 47/47 ✅
simulators_test.dart 56/56 ✅
android_device_test.dart 42/42 ✅
core_devices_test.dart 82/82 ✅
devices_test.dart 36/36 ✅
macos_device_test.dart 19/19 ✅
Total 296/296 All pass

@GhagSagar23

Copy link
Copy Markdown
Contributor Author

Hi @bkonyi — all review comments from Nov 4 have been addressed and pushed. Could you add the CICD label when you get a chance so the full test suite runs? Thanks.

@reidbaker

Copy link
Copy Markdown
Member

looks like the code is not formatted with dart format which causes other tests to not run
+

… subcommands

Implements `flutter capture screenshot` and `flutter capture recording`
as the unified interface for device screenshots and screen recording.

- ScreenRecording handle with stop()/done for clean signal handling
- Android: adb screenrecord with 180s clamp, exit code check, cleanup
- iOS sim: simctl recordVideo with LineSplitter + 10s start timeout
- iOS device: devicectl with --json-output outcome verification
- macOS: screencapture -v with -V duration flag
- Command-agnostic error messages for Xcode version checks
- flutter screenshot kept as hidden alias

Fixes flutter#190587
…rt, test refactors

- ScreenRecording: abstract interface → concrete class with stoppedExitCodes
  and signalStop callback; removed 4 platform _*ScreenRecording classes
- Android: pkill -2 screenrecord on all platforms (not just Windows)
- screenshot.dart: reverted to upstream + hidden/const-aliases only
- capture.dart: backward-compat comment, argParser cascade, removed misplaced
  macOS note from --duration help
- daemon.dart: added screenRecording to _deviceToMap capabilities
- devices.dart: narrowed catch to ProcessException
- Tests: deduplicated capture screenshot (5 removed), collapsed exit-code and
  duration tests into for loops, added JSON error-path + ProcessException tests
Fixes compile errors where _fileSystem on IOSDevice and _processUtils
on IOSCoreDeviceControl were undefined — destructure from
toolContext/_toolContext matching existing codebase pattern.
Post-merge fix: upstream added analytics/appleContext/featureFlags/
templateRenderer as required params; add to our screenshot/recording
tests + import fake_process_manager for hasNoRemainingExpectations.
@GhagSagar23

Copy link
Copy Markdown
Contributor Author

Thanks @reidbaker — good catch. Rebased onto latest master, resolved the merge conflicts, and ran dart format across all changed files. The formatting issue in devices.dart is fixed.

@bkonyi Could you add the CICD label when you get a chance? Ready for a full test run.

@GhagSagar23

Copy link
Copy Markdown
Contributor Author

Addressed all 4 comments from the latest review:

  1. Removed both toolContext! overrides — non-nullable on master now
  2. Added supportsScreenRecording => false to FakeAndroidDevice in proxied_devices_test
  3. Registered capture_command_test.dart in tool_test_durations.json
  4. Removed extra blank line in macos_device.dart imports

Also caught RPCError in runSkia to fix #196119 (Impeller + SKP crash), with a regression test.

@bkonyi Could you add the CICD label when you get a chance? Thanks!

@GhagSagar23

GhagSagar23 commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor Author

@bkonyi

bkonyi commented Nov 9, 2026

Copy link
Copy Markdown
Contributor

I don't think you have the correct permissions set. All of these captures are only showing system UI.

I've validated Android emulators + iOS simulators as well as macOS. Will validate physical Android and iOS devices today.

Android Emulator Validation https://github.com/user-attachments/assets/db3e015f-9f40-4d32-9e27-672e5ce9c3ab
android_emulator_recording_sigint.mp4
android_emulator_screenshot
iOS Simulator Validation
ios_simulator_recording_duration.mp4
ios_simulator_recording_sigint.mp4
ios_simulator_screenshot
macOS Validation
macos_desktop_recording_duration.mp4
macos_desktop_recording_sigint.mp4
macos_desktop_screenshot

It also looks like there's some analysis failures that need to be resolved before we can rerun the CI checks.

@bkonyi

bkonyi commented Nov 11, 2026

Copy link
Copy Markdown
Contributor

Hi @GhagSagar23, it looks like your commit from Nov 7 wasn't pushed (HEAD is still at 466c757b56be), and there are merge conflicts with master now as well. Can you merge master, resolve the conflicts, and push?

Also, FYI I already have #196132 up for review for #196119, so no need to include the runSkia fix here.

@GhagSagar23

Copy link
Copy Markdown
Contributor Author

@bkonyi Thanks for the heads up! Pushed the missing commit, merged master, and resolved conflicts. Also dropped the runSkia RPCError fix from this PR since your #196132 covers it. Should be good for another look.

@GhagSagar23

Copy link
Copy Markdown
Contributor Author

@bkonyi Rebased onto master — the RPCError fix from #196132 is now upstream so the duplicate in this branch is gone. Conflicts resolved, CI should be green shortly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

a: desktop Running on desktop CICD Run CI/CD platform-ios iOS applications specifically platform-macos Building on or for macOS specifically team-android Owned by Android platform team team-ios Owned by iOS platform team team-macos Owned by the macOS platform team tool Affects the "flutter" command-line tool. See also t: labels. triaged-android Triaged by Android platform team

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New flutter screenrecord command

9 participants