Install
$ agentstack add skill-iml1s-flutter-claude-skills-flutter-windows-ui-testing ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Flutter Windows Desktop UI Testing via Win32 API
Overview
Flutter desktop apps on Windows render everything inside a single FLUTTERVIEW pane — the Windows UI Automation accessibility tree only exposes this single element. Therefore, coordinate-based automation via user32.dll is the practical approach.
Architecture
PowerShell Script
├── Add-Type (C# interop for user32.dll)
├── Process → MainWindowHandle (HWND)
├── Focus: AttachThreadInput + BringWindowToTop + SetForegroundWindow
├── Coordinates: GetClientRect + ClientToScreen (excludes title bar)
├── Click: SetCursorPos + mouse_event
├── Key: keybd_event
└── Screenshot: System.Drawing.Graphics.CopyFromScreen
Critical Patterns
1. Getting Window Focus (MANDATORY)
Regular SetForegroundWindow fails when called from a background terminal. You MUST use AttachThreadInput to steal focus:
Add-Type @"
using System;
using System.Runtime.InteropServices;
using System.Threading;
public class FlutterTest {
[DllImport("user32.dll")] public static extern bool SetForegroundWindow(IntPtr h);
[DllImport("user32.dll")] public static extern bool BringWindowToTop(IntPtr h);
[DllImport("user32.dll")] public static extern bool GetWindowRect(IntPtr h, out RECT r);
[DllImport("user32.dll")] public static extern bool SetCursorPos(int X, int Y);
[DllImport("user32.dll")] public static extern void mouse_event(uint f, int dx, int dy, uint d, UIntPtr e);
[DllImport("user32.dll")] public static extern void keybd_event(byte vk, byte sc, uint f, UIntPtr e);
[DllImport("user32.dll")] public static extern bool ShowWindow(IntPtr h, int c);
[DllImport("user32.dll")] public static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")] public static extern uint GetWindowThreadProcessId(IntPtr h, out uint pid);
[DllImport("user32.dll")] public static extern bool AttachThreadInput(uint a, uint b, bool f);
[DllImport("user32.dll")] public static extern bool GetClientRect(IntPtr h, out RECT r);
[DllImport("user32.dll")] public static extern bool ClientToScreen(IntPtr h, ref POINT p);
[DllImport("kernel32.dll")] public static extern uint GetCurrentThreadId();
[StructLayout(LayoutKind.Sequential)] public struct RECT { public int L,T,R,B; }
[StructLayout(LayoutKind.Sequential)] public struct POINT { public int X,Y; }
public static bool Focus(IntPtr h) {
IntPtr fg = GetForegroundWindow();
uint ft = GetWindowThreadProcessId(fg, out uint fp);
uint mt = GetCurrentThreadId();
if (ft != mt) AttachThreadInput(mt, ft, true);
ShowWindow(h, 5); // SW_SHOW
BringWindowToTop(h);
bool r = SetForegroundWindow(h);
if (ft != mt) AttachThreadInput(mt, ft, false);
return r;
}
public static void Click(int x, int y) {
SetCursorPos(x, y);
Thread.Sleep(100);
mouse_event(0x0002, 0, 0, 0, UIntPtr.Zero); // MOUSEEVENTF_LEFTDOWN
Thread.Sleep(50);
mouse_event(0x0004, 0, 0, 0, UIntPtr.Zero); // MOUSEEVENTF_LEFTUP
}
public static void Key(byte vk) {
keybd_event(vk, 0, 0, UIntPtr.Zero);
Thread.Sleep(30);
keybd_event(vk, 0, 2, UIntPtr.Zero); // KEYEVENTF_KEYUP
}
}
"@
2. Getting Client Area Coordinates
Flutter borderless windows have a 1px title bar. Always use client-relative coordinates to avoid offset errors:
$p = Get-Process -Name "your_app" -ErrorAction SilentlyContinue | Select-Object -First 1
$hwnd = $p.MainWindowHandle
# Get client area origin and size (excludes title bar)
$origin = New-Object FlutterTest+POINT; $origin.X = 0; $origin.Y = 0
[FlutterTest]::ClientToScreen($hwnd, [ref]$origin)
$clientRect = New-Object FlutterTest+RECT
[FlutterTest]::GetClientRect($hwnd, [ref]$clientRect)
$cx = $origin.X; $cy = $origin.Y
$cw = $clientRect.R; $ch = $clientRect.B
3. Click at Relative Position
function ClickRel([double]$relX, [double]$relY, [string]$desc) {
$absX = $cx + [int]($cw * $relX)
$absY = $cy + [int]($ch * $relY)
Write-Host " Click '$desc' at ($absX,$absY)"
[FlutterTest]::Click($absX, $absY)
Start-Sleep -Milliseconds 500
}
4. Take Window-Only Screenshot
Add-Type -AssemblyName System.Drawing
function TakeScreenshot($name) {
Start-Sleep -Milliseconds 600
$bmp = New-Object System.Drawing.Bitmap($cw, $ch)
$g = [System.Drawing.Graphics]::FromImage($bmp)
$g.CopyFromScreen($cx, $cy, 0, 0, (New-Object System.Drawing.Size($cw, $ch)))
$path = "/path/to/output/$name.png"
$bmp.Save($path)
$g.Dispose(); $bmp.Dispose()
Write-Host " => $name.png"
}
5. Dismiss Dialogs
function PressEscape { [FlutterTest]::Key(0x1B); Start-Sleep -Milliseconds 400 }
# Dismiss paywall/dialog on launch:
PressEscape; PressEscape; PressEscape
Coordinate Mapping Workflow
Since we can't inspect Flutter's widget tree from Win32, use this iterative approach:
- Take a screenshot first — capture the client area to see the layout
- Estimate coordinates from the screenshot (e.g., button at pixel 170,180 in a 459×841 window = relX 0.37, relY 0.21)
- Click and screenshot — verify if the click landed correctly
- Adjust coordinates if the test shows incorrect position (popup didn't open, wrong page navigation)
Common Coordinate References (Voxboard 459×841)
| Element | relX | relY | Notes | |---------|------|------|-------| | Title bar back arrow | 0.08 | 0.03 | Top-left | | Settings gear | 0.93 | 0.03 | Top-right | | Mode tabs (聽寫) | 0.15 | 0.14 | First tab | | STT chip | 0.37 | 0.21 | Whisper chip | | LLM chip | 0.68 | 0.21 | LlamaCpp chip | | Mic button | 0.50 | 0.88 | Center bottom |
Key VK Codes
| Key | VK Code | Usage | |-----|---------|-------| | Escape | 0x1B | Dismiss dialogs/popups | | Enter | 0x0D | Confirm | | Back | 0x08 | Backspace | | Tab | 0x09 | Navigate |
Gotchas
SetForegroundWindowreturns False from background processes — MUST useAttachThreadInputtrick- Flutter borderless windows — title bar is 1px, use
GetClientRect+ClientToScreen - Screenshot captures wrong window — always call
Focus()beforeCopyFromScreen - Windows UI Automation tree is useless for Flutter — only exposes
FLUTTERVIEWpane - Popup menus render outside the window rect — screenshot the full screen if popup extends beyond window bounds
- DPI scaling — coordinates may be off on high-DPI screens, ensure your PowerShell process is DPI-aware
Full Test Flow Example
# 1. Launch app
taskkill /IM myapp.exe /F 2>$null
Start-Process "path\to\myapp.exe"
Start-Sleep -Seconds 5
# 2. Get handle and focus
$p = Get-Process -Name myapp | Select -First 1
$hwnd = $p.MainWindowHandle
[FlutterTest]::Focus($hwnd)
# 3. Get client coordinates
# ... (see section 2 above)
# 4. Dismiss paywall
PressEscape; PressEscape; PressEscape
# 5. Test flow
TakeScreenshot "01_main_page"
ClickRel 0.37 0.21 "stt-chip" # Open STT popup
TakeScreenshot "02_stt_popup"
ClickRel 0.37 0.31 "select-model" # Select 2nd model
TakeScreenshot "03_after_select"
PressEscape
ClickRel 0.93 0.03 "settings" # Navigate to settings
TakeScreenshot "04_settings"
Related skills
flutter-verify— use to verify Windows desktop UI behavior on real hardware after flutter-windows-ui-testing screenshots.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: ImL1s
- Source: ImL1s/flutter-claude-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.