@bdayadev/flutter-ultra-mcp 1.12.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +41 -13
  4. package/dart/ultra_flutter/pubspec.yaml +1 -1
  5. package/dart/ultra_flutter_devtools/pubspec.yaml +1 -1
  6. package/package.json +1 -1
  7. package/packages/flutter-ultra-native-desktop/package.json +2 -0
  8. package/packages/flutter-ultra-native-mobile/package.json +2 -0
  9. package/skills/add-integration-test/SKILL.md +72 -20
  10. package/skills/add-unit-test/SKILL.md +1 -0
  11. package/skills/add-widget-preview/SKILL.md +75 -26
  12. package/skills/add-widget-test/SKILL.md +51 -22
  13. package/skills/apply-architecture-best-practices/SKILL.md +55 -29
  14. package/skills/build-cli-app/SKILL.md +36 -9
  15. package/skills/build-responsive-layout/SKILL.md +65 -31
  16. package/skills/collect-coverage/SKILL.md +12 -1
  17. package/skills/debug/SKILL.md +19 -2
  18. package/skills/drive/SKILL.md +30 -15
  19. package/skills/fix-layout-issues/SKILL.md +71 -20
  20. package/skills/fix-runtime-errors/SKILL.md +79 -20
  21. package/skills/generate-test-mocks/SKILL.md +43 -13
  22. package/skills/implement-json-serialization/SKILL.md +82 -17
  23. package/skills/migrate-to-checks-package/SKILL.md +46 -14
  24. package/skills/record-demo/SKILL.md +20 -0
  25. package/skills/resolve-package-conflicts/SKILL.md +41 -12
  26. package/skills/run-static-analysis/SKILL.md +29 -16
  27. package/skills/setup/SKILL.md +1 -0
  28. package/skills/setup-declarative-routing/SKILL.md +166 -30
  29. package/skills/setup-localization/SKILL.md +90 -20
  30. package/skills/test/SKILL.md +2 -1
  31. package/skills/tour/SKILL.md +31 -22
  32. package/skills/use-http-package/SKILL.md +61 -23
  33. package/skills/use-pattern-matching/SKILL.md +63 -26
@@ -10,7 +10,7 @@
10
10
  {
11
11
  "name": "flutter",
12
12
  "description": "Durable cross-platform Flutter automation via 8 specialized MCP servers, in-app mixin binding, and an optional DevTools panel. Replaces marionette_mcp and the official dart mcp-server for Claude Code.",
13
- "version": "1.12.0",
13
+ "version": "1.14.0",
14
14
  "author": {
15
15
  "name": "Bdaya-Dev",
16
16
  "url": "https://github.com/Bdaya-Dev"
@@ -23,5 +23,5 @@
23
23
  "tags": ["flutter", "dart", "mcp", "testing", "automation", "patrol", "cross-platform"]
24
24
  }
25
25
  ],
26
- "version": "1.12.0"
26
+ "version": "1.14.0"
27
27
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
3
3
  "name": "flutter",
4
- "version": "1.12.0",
4
+ "version": "1.14.0",
5
5
  "description": "Durable cross-platform Flutter automation via 8 specialized MCP servers, in-app mixin binding, and an optional DevTools panel. Replaces marionette_mcp and the official dart mcp-server for Claude Code.",
6
6
  "author": {
7
7
  "name": "Bdaya-Dev",
package/README.md CHANGED
@@ -5,7 +5,7 @@
5
5
  [![CI](https://github.com/Bdaya-Dev/flutter-ultra-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/Bdaya-Dev/flutter-ultra-mcp/actions/workflows/ci.yml)
6
6
  [![License](https://img.shields.io/github/license/Bdaya-Dev/flutter-ultra-mcp)](LICENSE)
7
7
 
8
- A Claude Code plugin that gives your AI agent full control over Flutter apps across all platforms. 8 isolated MCP servers, 248 tools, 2 Dart packages, and 12 built-in skills — installed with a single command.
8
+ A Claude Code plugin that gives your AI agent full control over Flutter apps across all platforms. 8 isolated MCP servers, 295 tools, 2 Dart packages, and 31 built-in skills — installed with a single command.
9
9
 
10
10
  ## Why
11
11
 
@@ -44,17 +44,17 @@ Start a debug session (`flutter run -d chrome`), open Claude Code, and ask it to
44
44
 
45
45
  Each server runs as its own Node.js process. Crash isolation means a bug in one server cannot affect the others. Servers share session state through small JSON files on disk — if a server restarts, it picks up where it left off.
46
46
 
47
- | Server | Tools | What it does |
48
- | ------------------------------ | ------: | --------------------------------------------------------------------------------------------------------- |
49
- | `flutter-ultra-build` | 96 | Pub dependencies, code generation, analysis, formatting, tests, platform builds, l10n, assets, signing |
50
- | `flutter-ultra-runtime` | 55 | Attach to debug sessions, widget tree inspection, performance profiling, design audit, logs, HTTP capture |
51
- | `flutter-ultra-browser` | 26 | Playwright-driven web automation — OAuth flows, popups, console, network, storage, JS evaluation |
52
- | `flutter-ultra-native-mobile` | 24 | Android (UIAutomator) + iOS (XCUITest) — permissions, OAuth tabs, accessibility tree, device recording |
53
- | `flutter-ultra-gesture` | 18 | Tap, swipe, scroll, text input, screenshots, screencast via the in-app `ultra_flutter` mixin |
54
- | `flutter-ultra-patrol` | 15 | Orchestrate `patrol_cli` for E2E tests across web, Android, and iOS — run, poll, record, screenshot |
55
- | `flutter-ultra-native-desktop` | 9 | Windows (UIA), macOS (Accessibility), Linux (AT-SPI) — window listing, a11y tree, clicks, file dialogs |
56
- | `flutter-ultra-devtools` | 5 | Live MCP activity panel inside Flutter DevTools — sessions, tool calls, errors, screenshot grid |
57
- | **Total** | **248** | |
47
+ | Server | Tools | What it does |
48
+ | ------------------------------ | ------: | ------------------------------------------------------------------------------------------------------------------------ |
49
+ | `flutter-ultra-build` | 97 | Pub dependencies, code generation, analysis, formatting, tests, platform builds, l10n, assets, signing, project creation |
50
+ | `flutter-ultra-runtime` | 56 | Attach to debug sessions, widget tree, VM service method calls, performance profiling, design audit, logs, HTTP capture |
51
+ | `flutter-ultra-browser` | 35 | Playwright web automation — network mocking, offline sim, OAuth, drag-drop, dialogs, tracing, console, storage |
52
+ | `flutter-ultra-native-mobile` | 43 | Android + iOS — a11y tree, permissions, file picker, notifications, share sheet, CCT/SVC, GPS, deep links, app mgmt |
53
+ | `flutter-ultra-gesture` | 19 | Tap, swipe, scroll, text input, multi-touch W3C Actions, screenshots, screencast via `ultra_flutter` mixin |
54
+ | `flutter-ultra-patrol` | 31 | Orchestrate `patrol_cli` for E2E tests across web, Android, and iOS — run, poll, record, screenshot |
55
+ | `flutter-ultra-native-desktop` | 9 | Windows (UIA), macOS (Accessibility), Linux (AT-SPI) — window listing, a11y tree, clicks, file dialogs, remote SSH |
56
+ | `flutter-ultra-devtools` | 5 | Live MCP activity panel inside Flutter DevTools — sessions, tool calls, errors, screenshot grid |
57
+ | **Total** | **295** | |
58
58
 
59
59
  See [docs/architecture.md](docs/architecture.md) for the full design.
60
60
 
@@ -62,6 +62,8 @@ See [docs/architecture.md](docs/architecture.md) for the full design.
62
62
 
63
63
  Skills teach Claude the correct tool call sequences for common workflows. Invoke them with `/flutter:<name>`.
64
64
 
65
+ ### Workflow skills (12)
66
+
65
67
  | Skill | Description |
66
68
  | ------------------------ | -------------------------------------------------------------------------------------------- |
67
69
  | `/flutter:setup` | One-command setup of flutter-ultra in an existing codebase |
@@ -77,6 +79,32 @@ Skills teach Claude the correct tool call sequences for common workflows. Invoke
77
79
  | `/flutter:record-demo` | Record a video or GIF demo of an app flow (web browser or native device) |
78
80
  | `/flutter:devtools` | Wire up and use the DevTools panel for live MCP activity inspection |
79
81
 
82
+ ### Teaching skills (19 — vendored from [flutter/skills](https://github.com/flutter/skills) + [dart-lang/skills](https://github.com/dart-lang/skills))
83
+
84
+ Each teaching skill includes a **Flutter Ultra Integration** section mapping to the MCP tools that execute the workflow it teaches.
85
+
86
+ | Skill | Description |
87
+ | -------------------------------------------- | -------------------------------------------------------------------- |
88
+ | `/flutter:add-integration-test` | Configure Flutter Driver and write integration tests |
89
+ | `/flutter:add-widget-preview` | Add `@Preview` annotations for real-time widget previewing |
90
+ | `/flutter:add-widget-test` | Write component-level tests with `WidgetTester` |
91
+ | `/flutter:apply-architecture-best-practices` | Structure apps with MVVM + Repository layered architecture |
92
+ | `/flutter:build-responsive-layout` | Build adaptive layouts with `LayoutBuilder` and `MediaQuery` |
93
+ | `/flutter:fix-layout-issues` | Diagnose and fix RenderFlex overflow and unbounded constraint errors |
94
+ | `/flutter:implement-json-serialization` | Manual JSON mapping with `fromJson`/`toJson` |
95
+ | `/flutter:setup-declarative-routing` | Configure `go_router` with deep linking and nested navigation |
96
+ | `/flutter:setup-localization` | Set up `flutter_localizations` with ARB files |
97
+ | `/flutter:use-http-package` | Execute HTTP requests with the `http` package |
98
+ | `/flutter:add-unit-test` | Write unit tests with `package:test` |
99
+ | `/flutter:build-cli-app` | Build Dart CLI apps with argument parsing and compilation |
100
+ | `/flutter:collect-coverage` | Collect LCOV coverage reports |
101
+ | `/flutter:fix-runtime-errors` | Resolve type system, null safety, and static analysis errors |
102
+ | `/flutter:generate-test-mocks` | Generate mock objects with `package:mockito` + `build_runner` |
103
+ | `/flutter:migrate-to-checks-package` | Migrate from `expect`/`matcher` to `package:checks` |
104
+ | `/flutter:resolve-package-conflicts` | Fix `pub get` version conflicts |
105
+ | `/flutter:run-static-analysis` | Run `dart analyze` and `dart fix` |
106
+ | `/flutter:use-pattern-matching` | Apply switch expressions and Dart 3 pattern matching |
107
+
80
108
  ## Installation
81
109
 
82
110
  ### Plugin (required)
@@ -85,7 +113,7 @@ Skills teach Claude the correct tool call sequences for common workflows. Invoke
85
113
  /plugin install Bdaya-Dev/flutter-ultra-mcp
86
114
  ```
87
115
 
88
- This registers all 8 MCP servers and 12 skills automatically. No manual configuration needed.
116
+ This registers all 8 MCP servers and 31 skills automatically. No manual configuration needed.
89
117
 
90
118
  ### Dart package (required for gesture and screencast tools)
91
119
 
@@ -4,7 +4,7 @@ description: >-
4
4
  extensions (gesture, screenshot, inspector, screencast, log collection)
5
5
  for the flutter-ultra-mcp Claude Code plugin. Composable with Sentry and
6
6
  other WidgetsFlutterBinding subclasses via the mixin form.
7
- version: 1.12.0
7
+ version: 1.14.0
8
8
  homepage: https://github.com/Bdaya-Dev/flutter-ultra-mcp
9
9
  repository: https://github.com/Bdaya-Dev/flutter-ultra-mcp
10
10
  issue_tracker: https://github.com/Bdaya-Dev/flutter-ultra-mcp/issues
@@ -2,7 +2,7 @@ name: ultra_flutter_devtools
2
2
  description: >-
3
3
  Flutter DevTools extension showing live MCP activity for the flutter-ultra-mcp
4
4
  Claude Code plugin (sessions, recent tool calls, errors, screenshot grid).
5
- version: 1.12.0
5
+ version: 1.14.0
6
6
  homepage: https://github.com/Bdaya-Dev/flutter-ultra-mcp
7
7
  repository: https://github.com/Bdaya-Dev/flutter-ultra-mcp
8
8
  issue_tracker: https://github.com/Bdaya-Dev/flutter-ultra-mcp/issues
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bdayadev/flutter-ultra-mcp",
3
- "version": "1.12.0",
3
+ "version": "1.14.0",
4
4
  "description": "Flutter Ultra MCP plugin monorepo — 8 MCP servers + ultra_flutter Dart packages + skills for Claude Code.",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/Bdaya-Dev/flutter-ultra-mcp",
@@ -65,9 +65,11 @@
65
65
  "dependencies": {
66
66
  "@flutter-ultra/mcp-runtime": "^0.0.1",
67
67
  "@modelcontextprotocol/sdk": "^1.29.0",
68
+ "ssh2": "^1.16.0",
68
69
  "zod": "^3.23.8"
69
70
  },
70
71
  "devDependencies": {
72
+ "@types/ssh2": "^1.15.0",
71
73
  "rimraf": "^6.0.0",
72
74
  "typescript": "^5.6.0",
73
75
  "vitest": "^2.1.0"
@@ -56,9 +56,11 @@
56
56
  "@modelcontextprotocol/sdk": "^1.29.0",
57
57
  "fast-xml-parser": "^4.5.0",
58
58
  "playwright-core": "^1.49.0",
59
+ "ssh2": "^1.16.0",
59
60
  "zod": "^3.23.8"
60
61
  },
61
62
  "devDependencies": {
63
+ "@types/ssh2": "^1.15.0",
62
64
  "rimraf": "^6.0.0",
63
65
  "typescript": "^5.6.0",
64
66
  "vitest": "^2.1.0"
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: add-integration-test
3
3
  description: Configures Flutter Driver for app interaction and converts MCP actions into permanent integration tests. Use when adding integration testing to a project, exploring UI components via MCP, or automating user flows with the integration_test package.
4
+ last_modified: Tue, 21 Apr 2026 18:29:20 GMT
4
5
  ---
5
6
 
6
7
  # Implementing Flutter Integration Tests
@@ -26,17 +27,17 @@ Configure the project to support integration testing and Flutter Driver extensio
26
27
  2. Enable the Flutter Driver extension in your application entry point (typically `lib/main.dart` or a dedicated `lib/main_test.dart`):
27
28
  - Import `package:flutter_driver/driver_extension.dart`.
28
29
  - Call `enableFlutterDriverExtension();` before `runApp()`.
29
- 3. Add `Key` parameters (e.g., `ValueKey('login_button')`) to critical widgets for reliable targeting.
30
+ 3. Add `Key` parameters (e.g., `ValueKey('login_button')`) to critical widgets in the application code to ensure reliable targeting during tests.
30
31
 
31
32
  ## Interactive Exploration via MCP
32
33
 
33
- Use MCP tools to interactively explore and manipulate the application state before writing static tests.
34
+ Use the Dart/Flutter MCP server tools to interactively explore and manipulate the application state before writing static tests.
34
35
 
35
36
  - **Launch**: Execute `launch_app` with `target: "lib/main_test.dart"` to start the application and acquire the DTD URI.
36
37
  - **Inspect**: Execute `get_widget_tree` to discover available `Key`s, `Text` nodes, and widget `Type`s.
37
38
  - **Interact**: Execute `tap`, `enter_text`, and `scroll` to simulate user flows.
38
39
  - **Wait**: Always execute `waitFor` or verify state with `get_health` when navigating or triggering animations.
39
- - **Troubleshoot Unmounted Widgets**: If a widget is not found, it may be lazily loaded. Execute `scroll` or `scrollIntoView` to force the widget to mount.
40
+ - **Troubleshoot Unmounted Widgets**: If a widget is not found in the tree, it may be lazily loaded in a `SliverList` or `ListView`. Execute `scroll` or `scrollIntoView` to force the widget to mount before interacting with it.
40
41
 
41
42
  ## Test Authoring Guidelines
42
43
 
@@ -44,41 +45,54 @@ Structure integration tests using the `flutter_test` API paradigm.
44
45
 
45
46
  - Create a dedicated `integration_test/` directory at the project root.
46
47
  - Name all test files using the `<name>_test.dart` convention.
47
- - Initialize the binding: `IntegrationTestWidgetsFlutterBinding.ensureInitialized();`
48
- - Load the application UI: `await tester.pumpWidget(MyApp());`
49
- - Trigger frames: `await tester.pumpAndSettle();` after interactions.
50
- - Assert visibility: `expect(find.byKey(ValueKey('foo')), findsOneWidget);`
51
- - Scroll to off-screen widgets: `await tester.scrollUntilVisible(itemFinder, 500.0, scrollable: listFinder);`
48
+ - Initialize the binding by calling `IntegrationTestWidgetsFlutterBinding.ensureInitialized();` at the start of `main()`.
49
+ - Load the application UI using `await tester.pumpWidget(MyApp());`.
50
+ - Trigger frames and wait for animations to complete using `await tester.pumpAndSettle();` after interactions like `tester.tap()`.
51
+ - Assert widget visibility using `expect(find.byKey(ValueKey('foo')), findsOneWidget);` or `findsNothing`.
52
+ - Scroll to specific off-screen widgets using `await tester.scrollUntilVisible(itemFinder, 500.0, scrollable: listFinder);`.
53
+
54
+ **Conditional Logic for Legacy `flutter_driver`:**
55
+
56
+ - If maintaining or migrating legacy `flutter_driver` tests, use `driver.waitFor()`, `driver.waitForAbsent()`, `driver.tap()`, and `driver.scroll()` instead of the `WidgetTester` APIs.
52
57
 
53
58
  ## Execution and Profiling
54
59
 
55
- Execute tests using the `flutter drive` command. Require a host driver script at `test_driver/integration_test.dart`.
60
+ Execute tests using the `flutter drive` command. Require a host driver script located in `test_driver/integration_test.dart` that calls `integrationDriver()`.
56
61
 
57
- - **Chrome:** Launch `chromedriver --port=4444`, then: `flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart -d chrome`
58
- - **Headless web:** Run with `-d web-server`.
59
- - **Android (Local):** `flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart`
62
+ **Conditional Execution Targets:**
63
+
64
+ - **If testing on Chrome:** Launch `chromedriver --port=4444` in a separate terminal, then run:
65
+ `flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart -d chrome`
66
+ - **If testing headless web:** Run with `-d web-server`.
67
+ - **If testing on Android (Local):** Run `flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart`.
68
+ - **If testing on Firebase Test Lab (Android):**
69
+ 1. Build debug APK: `flutter build apk --debug`
70
+ 2. Build test APK: `./gradlew app:assembleAndroidTest`
71
+ 3. Upload both APKs to the Firebase Test Lab console.
60
72
 
61
73
  ## Workflow: End-to-End Integration Testing
62
74
 
63
- - [ ] **Setup**
75
+ Copy and follow this checklist to implement and verify integration tests.
76
+
77
+ - [ ] **Task Progress: Setup**
64
78
  - [ ] Add `integration_test` and `flutter_test` to `pubspec.yaml`.
65
79
  - [ ] Inject `enableFlutterDriverExtension()` into the app entry point.
66
80
  - [ ] Assign `ValueKey`s to target widgets.
67
- - [ ] **Exploration**
81
+ - [ ] **Task Progress: Exploration**
68
82
  - [ ] Run `launch_app` via MCP.
69
83
  - [ ] Map the widget tree using `get_widget_tree`.
70
84
  - [ ] Validate interaction paths using MCP tools (`tap`, `enter_text`).
71
- - [ ] **Authoring**
85
+ - [ ] **Task Progress: Authoring**
72
86
  - [ ] Create `integration_test/app_test.dart`.
73
87
  - [ ] Write test cases using `WidgetTester` APIs.
74
88
  - [ ] Create `test_driver/integration_test.dart` with `integrationDriver()`.
75
- - [ ] **Execution & Feedback Loop**
76
- - [ ] Run `flutter drive`.
77
- - [ ] Review output -> fix `PumpAndSettleTimedOutException` or missing widget issues -> re-run.
89
+ - [ ] **Task Progress: Execution & Feedback Loop**
90
+ - [ ] Run `flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart`.
91
+ - [ ] **Feedback Loop**: Review test output -> If `PumpAndSettleTimedOutException` occurs, check for infinite animations -> If widget not found, add `scrollUntilVisible` -> Re-run test until passing.
78
92
 
79
93
  ## Examples
80
94
 
81
- ### Standard Integration Test
95
+ ### Standard Integration Test (`integration_test/app_test.dart`)
82
96
 
83
97
  ```dart
84
98
  import 'package:flutter/material.dart';
@@ -91,20 +105,29 @@ void main() {
91
105
 
92
106
  group('End-to-end test', () {
93
107
  testWidgets('tap on the floating action button, verify counter', (tester) async {
108
+ // Load app widget.
94
109
  await tester.pumpWidget(const MyApp());
110
+
111
+ // Verify the counter starts at 0.
95
112
  expect(find.text('0'), findsOneWidget);
96
113
 
114
+ // Find the floating action button to tap on.
97
115
  final fab = find.byKey(const ValueKey('increment'));
116
+
117
+ // Emulate a tap on the floating action button.
98
118
  await tester.tap(fab);
119
+
120
+ // Trigger a frame and wait for animations.
99
121
  await tester.pumpAndSettle();
100
122
 
123
+ // Verify the counter increments by 1.
101
124
  expect(find.text('1'), findsOneWidget);
102
125
  });
103
126
  });
104
127
  }
105
128
  ```
106
129
 
107
- ### Host Driver Script
130
+ ### Host Driver Script (`test_driver/integration_test.dart`)
108
131
 
109
132
  ```dart
110
133
  import 'package:integration_test/integration_test_driver.dart';
@@ -112,6 +135,35 @@ import 'package:integration_test/integration_test_driver.dart';
112
135
  Future<void> main() => integrationDriver();
113
136
  ```
114
137
 
138
+ ### Performance Profiling Driver Script (`test_driver/perf_driver.dart`)
139
+
140
+ Use this driver script if you wrap your test actions in `binding.traceAction()` to capture performance metrics.
141
+
142
+ ```dart
143
+ import 'package:flutter_driver/flutter_driver.dart' as driver;
144
+ import 'package:integration_test/integration_test_driver.dart';
145
+
146
+ Future<void> main() {
147
+ return integrationDriver(
148
+ responseDataCallback: (data) async {
149
+ if (data != null) {
150
+ final timeline = driver.Timeline.fromJson(
151
+ data['scrolling_timeline'] as Map<String, dynamic>,
152
+ );
153
+
154
+ final summary = driver.TimelineSummary.summarize(timeline);
155
+
156
+ await summary.writeTimelineToFile(
157
+ 'scrolling_timeline',
158
+ pretty: true,
159
+ includeSummary: true,
160
+ );
161
+ }
162
+ },
163
+ );
164
+ }
165
+ ```
166
+
115
167
  ## Flutter Ultra Integration
116
168
 
117
169
  After writing the integration test, use these tools to launch the app and verify it:
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: add-unit-test
3
3
  description: Write and organize unit tests for functions, methods, and classes using `package:test`. Use when creating new logic or fixing bugs to ensure code remains correct and regression-free.
4
+ last_modified: Fri, 24 Apr 2026 15:07:58 GMT
4
5
  ---
5
6
 
6
7
  # Testing Dart and Flutter Applications
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: add-widget-preview
3
3
  description: Adds interactive widget previews to the project using the previews.dart system. Use when creating new UI components or updating existing screens to ensure consistent design and interactive testing.
4
+ last_modified: Tue, 21 Apr 2026 20:05:23 GMT
4
5
  ---
5
6
 
6
7
  # Previewing Flutter Widgets
@@ -16,44 +17,56 @@ description: Adds interactive widget previews to the project using the previews.
16
17
 
17
18
  Use the Flutter Widget Previewer to render widgets in real-time, isolated from the full application context.
18
19
 
19
- - **Target Elements:** Apply the `@Preview` annotation to top-level functions, static methods, or public widget constructors/factories that have no required arguments and return a `Widget` or `WidgetBuilder`.
20
- - **Imports:** Always import `package:flutter/widget_previews.dart`.
21
- - **Custom Annotations:** Extend the `Preview` class to create custom annotations that inject common properties (themes, wrappers).
22
- - **Multiple Configurations:** Apply multiple `@Preview` annotations to generate multiple preview instances. Or extend `MultiPreview`.
23
- - **Runtime Transformations:** Override `transform()` in custom `Preview` or `MultiPreview` classes for dynamic modification.
20
+ - **Target Elements:** Apply the `@Preview` annotation to top-level functions, static methods within a class, or public widget constructors/factories that have no required arguments and return a `Widget` or `WidgetBuilder`.
21
+ - **Imports:** Always import `package:flutter/widget_previews.dart` to access the preview annotations.
22
+ - **Custom Annotations:** Extend the `Preview` class to create custom annotations that inject common properties (e.g., themes, wrappers) across multiple widgets.
23
+ - **Multiple Configurations:** Apply multiple `@Preview` annotations to a single target to generate multiple preview instances. Alternatively, extend `MultiPreview` to encapsulate common multi-preview configurations.
24
+ - **Runtime Transformations:** Override the `transform()` method in custom `Preview` or `MultiPreview` classes to modify preview configurations dynamically at runtime (e.g., generating names based on dynamic values, which is impossible in a `const` context).
24
25
 
25
26
  ## Handling Limitations
26
27
 
27
- The Widget Previewer runs in a web environment:
28
+ Adhere to the following constraints when authoring previewable widgets, as the Widget Previewer runs in a web environment:
28
29
 
29
- - **No Native APIs:** Do not use `dart:io` or `dart:ffi`. Use conditional imports to mock or bypass.
30
- - **Asset Paths:** Use package-based paths (e.g., `packages/my_package_name/assets/my_image.png`).
31
- - **Public Callbacks:** Ensure all callback arguments are public and constant.
32
- - **Constraints:** Apply explicit constraints using the `size` parameter if your widget is unconstrained.
30
+ - **No Native APIs:** Do not use native plugins or APIs from `dart:io` or `dart:ffi`. Widgets with transitive dependencies on `dart:io` or `dart:ffi` will throw exceptions upon invocation. Use conditional imports to mock or bypass these in preview mode.
31
+ - **Asset Paths:** Use package-based paths for assets loaded via `dart:ui` `fromAsset` APIs (e.g., `packages/my_package_name/assets/my_image.png` instead of `assets/my_image.png`).
32
+ - **Public Callbacks:** Ensure all callback arguments provided to preview annotations are public and constant to satisfy code generation requirements.
33
+ - **Constraints:** Apply explicit constraints using the `size` parameter in the `@Preview` annotation if your widget is unconstrained, as the previewer defaults to constraining them to approximately half the viewport.
33
34
 
34
35
  ## Workflows
35
36
 
36
37
  ### Creating a Widget Preview
37
38
 
39
+ Copy and track this checklist when implementing a new widget preview:
40
+
38
41
  - [ ] Import `package:flutter/widget_previews.dart`.
39
- - [ ] Identify a valid target (top-level function, static method, or parameter-less constructor).
40
- - [ ] Apply the `@Preview` annotation.
41
- - [ ] Configure parameters (`name`, `group`, `size`, `theme`, `brightness`).
42
- - [ ] If applying same config to multiple widgets, extract into a custom class extending `Preview`.
42
+ - [ ] Identify a valid target (top-level function, static method, or parameter-less public constructor).
43
+ - [ ] Apply the `@Preview` annotation to the target.
44
+ - [ ] Configure preview parameters (`name`, `group`, `size`, `theme`, `brightness`, etc.) as needed.
45
+ - [ ] If applying the same configuration to multiple widgets, extract the configuration into a custom class extending `Preview`.
43
46
 
44
47
  ### Interacting with Previews
45
48
 
46
- **If using a supported IDE (Flutter 3.38+):**
49
+ Follow the appropriate conditional workflow to launch and interact with the Widget Previewer:
50
+
51
+ **If using a supported IDE (Android Studio, IntelliJ, VS Code with Flutter 3.38+):**
47
52
 
48
- 1. Launch the IDE. Widget Previewer starts automatically.
49
- 2. Open the "Flutter Widget Preview" tab.
50
- 3. Toggle "Filter previews by selected file" if needed.
53
+ 1. Launch the IDE. The Widget Previewer starts automatically.
54
+ 2. Open the "Flutter Widget Preview" tab in the sidebar.
55
+ 3. Toggle "Filter previews by selected file" at the bottom left if you want to view previews outside the currently active file.
51
56
 
52
57
  **If using the Command Line:**
53
58
 
54
- 1. Navigate to the project root.
59
+ 1. Navigate to the Flutter project's root directory.
55
60
  2. Run `flutter widget-preview start`.
56
- 3. View in the Chrome environment.
61
+ 3. View the automatically opened Chrome environment.
62
+
63
+ **Feedback Loop: Preview Iteration**
64
+
65
+ 1. Modify the widget code or preview configuration.
66
+ 2. Observe the automatic update in the Widget Previewer.
67
+ 3. If global state (e.g., static initializers) was modified: Click the global hot restart button at the bottom right.
68
+ 4. If only the local widget state needs resetting: Click the individual hot restart button on the specific preview card.
69
+ 5. Review errors in the IDE/CLI console -> fix -> repeat.
57
70
 
58
71
  ## Examples
59
72
 
@@ -69,21 +82,59 @@ Widget mySampleText() {
69
82
  }
70
83
  ```
71
84
 
85
+ ### Custom Preview with Runtime Transformation
86
+
87
+ ```dart
88
+ import 'package:flutter/widget_previews.dart';
89
+ import 'package:flutter/material.dart';
90
+
91
+ final class TransformativePreview extends Preview {
92
+ const TransformativePreview({
93
+ super.name,
94
+ super.group,
95
+ });
96
+
97
+ PreviewThemeData _themeBuilder() {
98
+ return PreviewThemeData(
99
+ materialLight: ThemeData.light(),
100
+ materialDark: ThemeData.dark(),
101
+ );
102
+ }
103
+
104
+ @override
105
+ Preview transform() {
106
+ final originalPreview = super.transform();
107
+ final builder = originalPreview.toBuilder();
108
+
109
+ builder
110
+ ..name = 'Transformed - ${originalPreview.name}'
111
+ ..theme = _themeBuilder;
112
+
113
+ return builder.toPreview();
114
+ }
115
+ }
116
+
117
+ @TransformativePreview(name: 'Custom Themed Button')
118
+ Widget myButton() => const ElevatedButton(onPressed: null, child: Text('Click'));
119
+ ```
120
+
72
121
  ### MultiPreview Implementation
73
122
 
74
123
  ```dart
75
124
  import 'package:flutter/widget_previews.dart';
76
125
  import 'package:flutter/material.dart';
77
126
 
127
+ /// Creates light and dark mode previews automatically.
78
128
  final class MultiBrightnessPreview extends MultiPreview {
79
129
  const MultiBrightnessPreview({required this.name});
130
+
80
131
  final String name;
81
132
 
82
133
  @override
83
134
  List<Preview> get previews => const [
84
- Preview(brightness: Brightness.light),
85
- Preview(brightness: Brightness.dark),
86
- ];
135
+ Preview(brightness: Brightness.light),
136
+ Preview(brightness: Brightness.dark),
137
+ ];
87
138
 
88
139
  @override
89
140
  List<Preview> transform() {
@@ -98,9 +149,7 @@ final class MultiBrightnessPreview extends MultiPreview {
98
149
  }
99
150
 
100
151
  @MultiBrightnessPreview(name: 'Primary Card')
101
- Widget cardPreview() => const Card(
102
- child: Padding(padding: EdgeInsets.all(8.0), child: Text('Content')),
103
- );
152
+ Widget cardPreview() => const Card(child: Padding(padding: EdgeInsets.all(8.0), child: Text('Content')));
104
153
  ```
105
154
 
106
155
  ## Flutter Ultra Integration
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  name: add-widget-test
3
3
  description: Implement a component-level test using `WidgetTester` to verify UI rendering and user interactions (tapping, scrolling, entering text). Use when validating that a specific widget displays correct data and responds to events as expected.
4
+ last_modified: Tue, 21 Apr 2026 21:15:41 GMT
4
5
  ---
5
6
 
6
7
  # Writing Flutter Widget Tests
@@ -15,41 +16,53 @@ description: Implement a component-level test using `WidgetTester` to verify UI
15
16
 
16
17
  ## Setup & Configuration
17
18
 
18
- 1. Add `flutter_test` to `dev_dependencies` in `pubspec.yaml`.
19
- 2. Place all test files in `test/` at the project root.
20
- 3. Suffix all test file names with `_test.dart`.
19
+ Ensure the testing environment is properly configured before authoring widget tests.
20
+
21
+ 1. Add the `flutter_test` dependency to the `dev_dependencies` section of `pubspec.yaml`.
22
+ 2. Place all test files in the `test/` directory at the root of the project.
23
+ 3. Suffix all test file names with `_test.dart` (e.g., `widget_test.dart`).
21
24
 
22
25
  ## Core Components
23
26
 
24
- - **`WidgetTester`**: Primary interface for building and interacting with widgets. Provided by `testWidgets()`.
25
- - **`Finder`**: Locates widgets (e.g., `find.text('Submit')`, `find.byType(TextField)`, `find.byKey(Key('submit_btn'))`).
26
- - **`Matcher`**: Verifies presence or state (e.g., `findsOneWidget`, `findsNothing`, `findsNWidgets(2)`).
27
+ Utilize the following `flutter_test` components to interact with and validate the widget tree:
28
+
29
+ - **`WidgetTester`**: The primary interface for building and interacting with widgets in the test environment. Provided automatically by the `testWidgets()` function.
30
+ - **`Finder`**: Locates widgets in the test environment (e.g., `find.text('Submit')`, `find.byType(TextField)`, `find.byKey(Key('submit_btn'))`).
31
+ - **`Matcher`**: Verifies the presence or state of widgets located by a `Finder` (e.g., `findsOneWidget`, `findsNothing`, `findsNWidgets(2)`, `matchesGoldenFile`).
27
32
 
28
33
  ## Workflow: Implementing a Widget Test
29
34
 
35
+ Copy the following checklist to track progress when implementing a new widget test.
36
+
30
37
  ### Task Progress
31
38
 
32
- - [ ] **Step 1:** Define the test with `testWidgets('description', (WidgetTester tester) async { ... })`.
33
- - [ ] **Step 2:** Build the widget with `await tester.pumpWidget(MyWidget())`. Wrap in `MaterialApp` if needed.
34
- - [ ] **Step 3:** Locate elements with `Finder` objects.
35
- - [ ] **Step 4:** Verify initial state with `expect(finder, matcher)`.
36
- - [ ] **Step 5:** Simulate interactions (e.g., `await tester.tap(buttonFinder)`).
37
- - [ ] **Step 6:** Rebuild the tree with `await tester.pump()` or `await tester.pumpAndSettle()`.
38
- - [ ] **Step 7:** Verify updated state with `expect()`.
39
- - [ ] **Step 8:** Run with `flutter test test/your_test_file_test.dart`.
40
- - [ ] **Step 9:** Feedback Loop: Review output -> fix assertions -> re-run.
39
+ - [ ] **Step 1: Define the test.** Use `testWidgets('description', (WidgetTester tester) async { ... })`.
40
+ - [ ] **Step 2: Build the widget.** Call `await tester.pumpWidget(MyWidget())` to render the UI. Wrap the widget in a `MaterialApp` or `Directionality` widget if it requires inherited directional or theme data.
41
+ - [ ] **Step 3: Locate elements.** Instantiate `Finder` objects for the target widgets.
42
+ - [ ] **Step 4: Verify initial state.** Use `expect(finder, matcher)` to validate the initial render.
43
+ - [ ] **Step 5: Simulate interactions.** Execute gestures or inputs (e.g., `await tester.tap(buttonFinder)`).
44
+ - [ ] **Step 6: Rebuild the tree.** Call `await tester.pump()` or `await tester.pumpAndSettle()` to process state changes.
45
+ - [ ] **Step 7: Verify updated state.** Use `expect()` to validate the UI after the interaction.
46
+ - [ ] **Step 8: Run and validate.** Execute `flutter test test/your_test_file_test.dart`.
47
+ - [ ] **Step 9: Feedback Loop.** Review test output -> identify failing matchers -> adjust widget logic or test assertions -> re-run until passing.
41
48
 
42
49
  ## Interaction & State Management
43
50
 
44
- - **Static rendering:** `pumpWidget()` once, then immediately assert.
45
- - **Standard state changes (button taps):** `tester.tap()` then `tester.pump()`.
46
- - **Animations/transitions:** Trigger the action then `tester.pumpAndSettle()`.
47
- - **Text input:** `await tester.enterText(textFieldFinder, 'Input string')`.
48
- - **Long lists:** `await tester.scrollUntilVisible(itemFinder, 500.0, scrollable: listFinder)`.
51
+ Apply the following conditional logic based on the type of interaction or state change being tested:
52
+
53
+ - **If testing static rendering:** Call `await tester.pumpWidget()` once, then immediately run `expect()` assertions.
54
+ - **If testing standard state changes (e.g., button taps):**
55
+ 1. Call `await tester.tap(finder)`.
56
+ 2. Call `await tester.pump()` to trigger a single frame rebuild.
57
+ - **If testing animations, transitions, or asynchronous UI updates:**
58
+ 1. Trigger the action (e.g., `await tester.drag(finder, Offset(500, 0))`).
59
+ 2. Call `await tester.pumpAndSettle()` to repeatedly pump frames until no more frames are scheduled (animation completes).
60
+ - **If testing text input:** Call `await tester.enterText(textFieldFinder, 'Input string')`.
61
+ - **If testing items in a dynamic or long list:** Call `await tester.scrollUntilVisible(itemFinder, 500.0, scrollable: listFinder)` to ensure the target widget is rendered before interacting with it.
49
62
 
50
63
  ## Examples
51
64
 
52
- ### TodoList Widget Test
65
+ ### High-Fidelity Widget Test Implementation
53
66
 
54
67
  **Target Widget (`lib/todo_list.dart`):**
55
68
 
@@ -58,6 +71,7 @@ import 'package:flutter/material.dart';
58
71
 
59
72
  class TodoList extends StatefulWidget {
60
73
  const TodoList({super.key});
74
+
61
75
  @override
62
76
  State<TodoList> createState() => _TodoListState();
63
77
  }
@@ -103,7 +117,7 @@ class _TodoListState extends State<TodoList> {
103
117
  }
104
118
  ```
105
119
 
106
- **Test (`test/todo_list_test.dart`):**
120
+ **Test Implementation (`test/todo_list_test.dart`):**
107
121
 
108
122
  ```dart
109
123
  import 'package:flutter/material.dart';
@@ -112,16 +126,31 @@ import 'package:my_app/todo_list.dart';
112
126
 
113
127
  void main() {
114
128
  testWidgets('Add and remove a todo item', (WidgetTester tester) async {
129
+ // 1. Build the widget
115
130
  await tester.pumpWidget(const TodoList());
131
+
132
+ // 2. Verify initial state
116
133
  expect(find.byType(ListTile), findsNothing);
117
134
 
135
+ // 3. Enter text into the TextField
118
136
  await tester.enterText(find.byType(TextField), 'Buy groceries');
137
+
138
+ // 4. Tap the add button
119
139
  await tester.tap(find.byType(FloatingActionButton));
140
+
141
+ // 5. Rebuild the widget to reflect the new state
120
142
  await tester.pump();
143
+
144
+ // 6. Verify the item was added
121
145
  expect(find.text('Buy groceries'), findsOneWidget);
122
146
 
147
+ // 7. Swipe the item to dismiss it
123
148
  await tester.drag(find.byType(Dismissible), const Offset(500, 0));
149
+
150
+ // 8. Build the widget until the dismiss animation ends
124
151
  await tester.pumpAndSettle();
152
+
153
+ // 9. Verify the item was removed
125
154
  expect(find.text('Buy groceries'), findsNothing);
126
155
  });
127
156
  }