@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.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +41 -13
- package/dart/ultra_flutter/pubspec.yaml +1 -1
- package/dart/ultra_flutter_devtools/pubspec.yaml +1 -1
- package/package.json +1 -1
- package/packages/flutter-ultra-native-desktop/package.json +2 -0
- package/packages/flutter-ultra-native-mobile/package.json +2 -0
- package/skills/add-integration-test/SKILL.md +72 -20
- package/skills/add-unit-test/SKILL.md +1 -0
- package/skills/add-widget-preview/SKILL.md +75 -26
- package/skills/add-widget-test/SKILL.md +51 -22
- package/skills/apply-architecture-best-practices/SKILL.md +55 -29
- package/skills/build-cli-app/SKILL.md +36 -9
- package/skills/build-responsive-layout/SKILL.md +65 -31
- package/skills/collect-coverage/SKILL.md +12 -1
- package/skills/debug/SKILL.md +19 -2
- package/skills/drive/SKILL.md +30 -15
- package/skills/fix-layout-issues/SKILL.md +71 -20
- package/skills/fix-runtime-errors/SKILL.md +79 -20
- package/skills/generate-test-mocks/SKILL.md +43 -13
- package/skills/implement-json-serialization/SKILL.md +82 -17
- package/skills/migrate-to-checks-package/SKILL.md +46 -14
- package/skills/record-demo/SKILL.md +20 -0
- package/skills/resolve-package-conflicts/SKILL.md +41 -12
- package/skills/run-static-analysis/SKILL.md +29 -16
- package/skills/setup/SKILL.md +1 -0
- package/skills/setup-declarative-routing/SKILL.md +166 -30
- package/skills/setup-localization/SKILL.md +90 -20
- package/skills/test/SKILL.md +2 -1
- package/skills/tour/SKILL.md +31 -22
- package/skills/use-http-package/SKILL.md +61 -23
- 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.
|
|
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.
|
|
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.
|
|
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
|
[](https://github.com/Bdaya-Dev/flutter-ultra-mcp/actions/workflows/ci.yml)
|
|
6
6
|
[](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,
|
|
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` |
|
|
50
|
-
| `flutter-ultra-runtime` |
|
|
51
|
-
| `flutter-ultra-browser` |
|
|
52
|
-
| `flutter-ultra-native-mobile` |
|
|
53
|
-
| `flutter-ultra-gesture` |
|
|
54
|
-
| `flutter-ultra-patrol` |
|
|
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** | **
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
48
|
-
- Load the application UI
|
|
49
|
-
- Trigger frames
|
|
50
|
-
- Assert visibility
|
|
51
|
-
- Scroll to off-screen widgets
|
|
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
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
- **
|
|
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
|
-
|
|
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 ->
|
|
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.
|
|
23
|
-
- **Runtime Transformations:** Override `transform()` in custom `Preview` or `MultiPreview` classes
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
85
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
- **`
|
|
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
|
|
33
|
-
- [ ] **Step 2
|
|
34
|
-
- [ ] **Step 3
|
|
35
|
-
- [ ] **Step 4
|
|
36
|
-
- [ ] **Step 5
|
|
37
|
-
- [ ] **Step 6
|
|
38
|
-
- [ ] **Step 7
|
|
39
|
-
- [ ] **Step 8
|
|
40
|
-
- [ ] **Step 9
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
- **
|
|
47
|
-
- **
|
|
48
|
-
|
|
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
|
-
###
|
|
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
|
}
|