An open API service indexing awesome lists of open source software.

https://github.com/fluttersdk/wind_diagnostics_contracts

Pure abstract contracts for reading Wind UI widget state at runtime without a compile-time dep on fluttersdk_wind. Zero deps. plugin_platform_interface pattern applied to UI framework + debug tooling decoupling (MCP, AI agents, DevTools).
https://github.com/fluttersdk/wind_diagnostics_contracts

ai-agents contracts dart debug-tooling diagnostics e2e-testing flutter flutter-package flutter-testing llm mcp mcp-server platform-interface tailwind-flutter wind wind-ui

Last synced: about 1 month ago
JSON representation

Pure abstract contracts for reading Wind UI widget state at runtime without a compile-time dep on fluttersdk_wind. Zero deps. plugin_platform_interface pattern applied to UI framework + debug tooling decoupling (MCP, AI agents, DevTools).

Awesome Lists containing this project

README

          


Wind Diagnostics Contracts Logo

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.


pub package
CI
License: MIT
pub points
GitHub stars


Documentation ·
pub.dev ·
Issues

---

## Use cases

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](https://github.com/fluttersdk/wind) ([pub.dev](https://pub.dev/packages/fluttersdk_wind)) exposes runtime widget state (`className`, `breakpoint`, `brightness`, `platform`, `states`, `bgColor`, `textColor`) that debug-tooling packages such as [`fluttersdk_dusk`](https://github.com/fluttersdk/dusk) ([pub.dev](https://pub.dev/packages/fluttersdk_dusk)) 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`](https://pub.dev/packages/plugin_platform_interface) (the canonical precedent) sits at 4.97M downloads on pub.dev for exactly this reason.

---

## Install

```bash
flutter pub add fluttersdk_wind_diagnostics_contracts
```

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:

```dart
import 'package:flutter/foundation.dart' show kDebugMode;
import 'package:fluttersdk_wind/fluttersdk_wind.dart';

void main() {
if (kDebugMode) {
Wind.installDebugResolver();
}
runApp(const MyApp());
}
```

`Wind.installDebugResolver()` is a one-liner inside `fluttersdk_wind` that does:

```dart
import 'package:fluttersdk_wind_diagnostics_contracts/fluttersdk_wind_diagnostics_contracts.dart';

WindDebugRegistry.register(const WindDebugResolverImpl());
```

### Reading the resolver (in a debug-tooling package)

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:

```dart
import 'package:fluttersdk_wind_diagnostics_contracts/fluttersdk_wind_diagnostics_contracts.dart';

void emitWindBlock(StringBuffer buffer, Element element) {
final WindDebugResolver? resolver = WindDebugRegistry.current;
if (resolver == null) return; // wind not in this app, or release build.

final Map 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](CHANGELOG.md) for the full key list).

### Test seams

`WindDebugRegistry` exposes two `@visibleForTesting` helpers so debug-tool tests can register fake resolvers without going through Wind:

```dart
import 'package:flutter_test/flutter_test.dart';
import 'package:fluttersdk_wind_diagnostics_contracts/fluttersdk_wind_diagnostics_contracts.dart';

class _FakeResolver implements WindDebugResolver {
@override
Map resolve(Element element) {
return const {
'className': 'flex p-4',
'breakpoint': 'lg',
'brightness': 'light',
'platform': 'web',
'states': ['hover'],
};
}
}

void main() {
setUp(() => WindDebugRegistry.resetForTesting());

testWidgets('observe emits wind block from registry', (tester) async {
WindDebugRegistry.registerForTesting(_FakeResolver());
// ... rest of the test ...
});
}
```

---

## Versioning

This package follows [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html). The `WindDebugResolver.resolve` return-map key set is the load-bearing v1 contract: additive changes (new keys in the returned map) are non-breaking; renaming or removing existing keys requires a major bump.

---

## License

MIT. See [LICENSE](LICENSE).