Flutter에서 Wind UI 위젯 상태 계약을 정의하기 위한 의존성 없이 구현된 추상 인터페이스로, 디버그 도구 및 AI 에이전트(MCP)를 지원하도록 설계되었습니다. fluttersdk_wind에 구현되어 fluttersdk_dusk에서 사용되며, 다양한 플랫폼 간 일관된 진단 기능을 제공합니다. 개발 및 디버깅 중 위젯 상태를 검사하고 분석하는 표준화된 방법을 제공하여 도구와 에이전트 간 상호 운용성을 촉진합니다. Android, iOS, Linux, macOS, Web, Windows를 모두 지원하는 플랫폼 독립적인 구조입니다.
사용 사례
위젯 상태 문제 디버깅
AI 기반 코드 보조 기능 활성화
개발자 도구와의 통합
테스트 중 UI 동작 분석
자동 진단 지원
크로스 플랫폼 디버깅 지원
주요 기능
제로 종속성 설계
위젯 상태를 위한 추상 인터페이스
플랫폼 독립적인 구현
AI 에이전트(MCP)를 위한 설계
디버그 도구 통합을 위해 설계됨
일관된 계약 정의
적합한 대상
고급 디버깅 도구를 사용하는 Flutter 개발자
AI 보조 개발 기능을 구축하는 팀
도구 및 IDE 플러그인 개발자
크로스 플랫폼 UI 테스터
디버깅 프레임워크 기여자
고려 사항
fluttersdk_wind 및 fluttersdk_dusk 와의 통합이 필요합니다
앱 로직에서 직접 사용하기 위한 것이 아님
외부 도구 생태계에 의존합니다
진단 및 분석 워크플로우에 제한됨
격리 상태에서는 런타임 기능이 없습니다
원본 README
연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.
Wind Diagnostics Contracts
Wind UI widget state contracts for Flutter debug-tooling and AI agents (MCP).
Zero-dep abstract resolver interface plus process-global registry. The plugin_platform_interface pattern, applied to UI framework and debug tooling decoupling. ~80 LoC, frozen v1 contract.
This package is the integration seam for any tool that needs runtime Wind UI state without pulling Wind into its compile graph:
LLM-agent E2E testing for Flutter: MCP servers and AI assistants (Claude Code, Cursor, Copilot) that drive a Flutter app need structured widget state (className, breakpoint, brightness, platform, states, colors) per Element, not screenshots. This package is the typed contract those tools read.
DevTools extensions: build a custom Wind inspector tab in Dart DevTools without a hard fluttersdk_wind dependency that drags the rendering surface into the extension bundle.
E2E drivers (fluttersdk_dusk, patrol_mcp, marionette_mcp, custom): emit a wind: block in snapshot YAML / JSON without importing Wind. Tests stop breaking when a className is renamed because the contract surfaces semantic state, not selectors.
Tailwind-on-Flutter authors: any styling library that follows Wind's className convention can implement this contract to expose its state to the same debug-tooling ecosystem.
Why this package exists
Wind UI (pub.dev) exposes runtime widget state (className, breakpoint, brightness, platform, states, bgColor, textColor) that debug-tooling packages such as fluttersdk_dusk (pub.dev) embed in their snapshot YAML so LLM agents can reason about the rendered tree.
Shipping that handoff through fluttersdk_wind's own surface would force every debug-tool to compile-time depend on Wind, dragging the full rendering surface and bumping debug-tool builds on every Wind release. Shipping it the other way (Wind depending on each debug tool) is even worse.
This package breaks the loop. Both sides depend on the abstract contract here; neither side imports the other.
fluttersdk_wind_diagnostics_contracts
|
+---------------+---------------+
| |
fluttersdk_wind fluttersdk_dusk
(registers a resolver) (reads the resolver
at snapshot time)
The pattern mirrors Flutter's *_platform_interface convention. plugin_platform_interface (the canonical precedent) sits at 4.97M downloads on pub.dev for exactly this reason.
Most consumers never add this dep by hand. fluttersdk_wind declares it as a direct production dependency, so any app that already depends on Wind picks it up transitively. Add it explicitly when you are authoring a debug-tooling package that reads Wind state without depending on Wind itself.
Usage
Registering a resolver (in fluttersdk_wind)
Wind installs its concrete resolver at app boot, gated by kDebugMode so release builds tree-shake the entire registration site:
The consumer looks up the currently-installed resolver and calls resolve(element) per Element it wants to inspect. The resolver returns const {} for non-Wind widgets, so the walk is safe for any element:
import'package:fluttersdk_wind_diagnostics_contracts/fluttersdk_wind_diagnostics_contracts.dart';
voidemitWindBlock(StringBuffer buffer, Element element) {
finalWindDebugResolver? resolver = WindDebugRegistry.current;
if (resolver == null) return; // wind not in this app, or release build.finalMap<String, Object?> data = resolver.resolve(element);
if (data.isEmpty) return; // not a Wind widget.
buffer.writeln('wind:');
data.forEach((key, value) {
buffer.writeln(' $key: $value');
});
}
The returned map's key set is documented as the v1 frozen contract (see CHANGELOG.md for the full key list).
Reading aggregate performance counters
WindDebugResolver answers per-Element questions, so it has nowhere to put a number that belongs to no single element: a cache hit rate, a build count. WindPerfResolver is a second, independent contract for exactly those, with its own registry slot. Registering one never touches the other.
finalWindPerfResolver? perf = WindDebugRegistry.currentPerf;
if (perf == null) return; // wind not in this app, or the resolver was never installed.finalMap<String, Object?> stats = perf.stats();
// cacheHits, cacheMisses, cacheBypasses, cacheSize, wDivBuilds, wTextBuilds; all int.
Wind installs it with Wind.installPerfResolver(), separately from installDebugResolver(), because the two answer different questions and a host may want one without the other. Counting itself stays off until wind's own flag is set, so installing the resolver costs nothing. That method ships in the fluttersdk_wind release that pairs with this contract; a reader on an earlier wind will not find it yet.
The null is worth passing through rather than flattening to zeros: it is what lets a consumer tell "no resolver was ever registered" from "the counters really are zero".
Test seams
WindDebugRegistry exposes four @visibleForTesting helpers so debug-tool tests can register fake resolvers without going through Wind. resetForTesting() clears BOTH slots; registerForTesting and registerPerfForTesting install a fake into one each:
This package follows Semantic Versioning 2.0.0. Two return-map key sets are the load-bearing contract, WindDebugResolver.resolve's and WindPerfResolver.stats's, and the same rule governs both: additive changes (new keys in the returned map) are non-breaking; renaming or removing existing keys requires a major bump. Adding a METHOD to either contract is breaking, since it breaks every implementer including the test fakes, which is why the perf counters got a second contract rather than a second method on the first.