@bdayadev/flutter-ultra-mcp 1.3.2 → 1.5.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/dart/ultra_flutter/pubspec.yaml +0 -2
- package/dart/ultra_flutter_devtools/pubspec.yaml +0 -2
- package/examples/counter-app/README.md +5 -5
- package/examples/counter-app/pubspec.yaml +2 -0
- package/examples/oidc-app/README.md +10 -8
- package/package.json +5 -2
- package/packages/flutter-ultra-browser/package.json +1 -1
- package/packages/flutter-ultra-build/package.json +1 -1
- package/packages/flutter-ultra-devtools/package.json +1 -1
- package/packages/flutter-ultra-gesture/package.json +1 -1
- package/packages/flutter-ultra-native-desktop/package.json +1 -1
- package/packages/flutter-ultra-native-mobile/package.json +1 -1
- package/packages/flutter-ultra-patrol/README.md +5 -5
- package/packages/flutter-ultra-patrol/package.json +1 -1
- package/packages/flutter-ultra-runtime/package.json +1 -1
- package/shared/contracts/package.json +2 -2
- package/shared/device-router/package.json +1 -1
- package/shared/keyring/package.json +1 -1
- package/shared/mcp-runtime/package.json +1 -1
- package/shared/state-store/package.json +1 -1
- package/shared/vm-service-client/package.json +1 -1
- package/skills/bisect/SKILL.md +202 -0
- package/skills/debug/SKILL.md +145 -9
- package/skills/devtools/SKILL.md +120 -10
- package/skills/drive/SKILL.md +132 -9
- package/skills/scaffold/SKILL.md +155 -9
- package/skills/setup/SKILL.md +140 -16
- package/skills/test/SKILL.md +159 -8
- package/skills/tour/SKILL.md +91 -12
|
@@ -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.5.0",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Bdaya-Dev",
|
|
16
16
|
"url": "https://github.com/Bdaya-Dev"
|
|
@@ -31,5 +31,5 @@
|
|
|
31
31
|
]
|
|
32
32
|
}
|
|
33
33
|
],
|
|
34
|
-
"version": "1.
|
|
34
|
+
"version": "1.5.0"
|
|
35
35
|
}
|
|
@@ -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.5.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",
|
|
@@ -7,8 +7,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
|
|
9
9
|
|
|
10
|
-
publish_to: none
|
|
11
|
-
|
|
12
10
|
environment:
|
|
13
11
|
sdk: ^3.5.0
|
|
14
12
|
flutter: '>=3.24.0'
|
|
@@ -17,8 +17,8 @@ In debug mode the `UltraFlutterBinding` is automatically initialized, exposing
|
|
|
17
17
|
|
|
18
18
|
## Widget Keys
|
|
19
19
|
|
|
20
|
-
| Key
|
|
21
|
-
|
|
22
|
-
| `counter_value` | Text
|
|
23
|
-
| `increment`
|
|
24
|
-
| `decrement`
|
|
20
|
+
| Key | Widget | Purpose |
|
|
21
|
+
| --------------- | ------ | ---------------------- |
|
|
22
|
+
| `counter_value` | Text | Displays current count |
|
|
23
|
+
| `increment` | FAB | Increments counter |
|
|
24
|
+
| `decrement` | FAB | Decrements counter |
|
|
@@ -19,11 +19,13 @@ interception and CCT (Custom Chrome Tab) OAuth solver on mobile.
|
|
|
19
19
|
## Running
|
|
20
20
|
|
|
21
21
|
1. Start the mock OIDC server:
|
|
22
|
+
|
|
22
23
|
```bash
|
|
23
24
|
dart run lib/mock_oidc_server.dart
|
|
24
25
|
```
|
|
25
26
|
|
|
26
27
|
2. Run the Flutter app:
|
|
28
|
+
|
|
27
29
|
```bash
|
|
28
30
|
flutter run -d chrome
|
|
29
31
|
```
|
|
@@ -32,14 +34,14 @@ interception and CCT (Custom Chrome Tab) OAuth solver on mobile.
|
|
|
32
34
|
|
|
33
35
|
## Widget Keys
|
|
34
36
|
|
|
35
|
-
| Key
|
|
36
|
-
|
|
37
|
-
| `login_button`
|
|
38
|
-
| `logout_button`
|
|
39
|
-
| `auth_status`
|
|
40
|
-
| `token_preview`
|
|
41
|
-
| `loading_indicator` | CircularProgressIndicator | During OAuth exchange
|
|
42
|
-
| `error_text`
|
|
37
|
+
| Key | Widget | Purpose |
|
|
38
|
+
| ------------------- | ------------------------- | -------------------------------------------- |
|
|
39
|
+
| `login_button` | FilledButton | Initiates OIDC flow |
|
|
40
|
+
| `logout_button` | FilledButton.tonal | Clears token |
|
|
41
|
+
| `auth_status` | Text | Shows "Authenticated" or "Not authenticated" |
|
|
42
|
+
| `token_preview` | SelectableText | Truncated token display |
|
|
43
|
+
| `loading_indicator` | CircularProgressIndicator | During OAuth exchange |
|
|
44
|
+
| `error_text` | Text | Error message |
|
|
43
45
|
|
|
44
46
|
## CI Usage
|
|
45
47
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bdayadev/flutter-ultra-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.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",
|
|
@@ -28,7 +28,10 @@
|
|
|
28
28
|
"format": "prettier --write \"**/*.{ts,tsx,js,json,md,yml,yaml}\"",
|
|
29
29
|
"format:check": "prettier --check \"**/*.{ts,tsx,js,json,md,yml,yaml}\"",
|
|
30
30
|
"bundle": "node scripts/bundle.mjs",
|
|
31
|
-
"prepare": "npm run build --if-present || true"
|
|
31
|
+
"prepare": "npm run build --if-present || true",
|
|
32
|
+
"test:e2e:web": "vitest run tests/e2e/web/",
|
|
33
|
+
"test:e2e:oidc": "vitest run tests/e2e/oidc/",
|
|
34
|
+
"test:dogfood": "vitest run tests/e2e/dogfood/"
|
|
32
35
|
},
|
|
33
36
|
"devDependencies": {
|
|
34
37
|
"@commitlint/cli": "^19.0.0",
|
|
@@ -22,12 +22,12 @@ MCP server orchestrating **Patrol E2E tests** across web, Android, iOS, and desk
|
|
|
22
22
|
|
|
23
23
|
## Environment contract (from plugin `.mcp.json`)
|
|
24
24
|
|
|
25
|
-
| Var | Used for
|
|
26
|
-
| --------------------------- |
|
|
27
|
-
| `FLUTTER_ULTRA_PATROL_FORK` | Absolute path to `vendor/patrol/`.
|
|
28
|
-
| `FLUTTER_ULTRA_STATE_DIR` | Future on-disk job persistence (in-memory in v1.0).
|
|
25
|
+
| Var | Used for |
|
|
26
|
+
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
27
|
+
| `FLUTTER_ULTRA_PATROL_FORK` | Absolute path to `vendor/patrol/`. |
|
|
28
|
+
| `FLUTTER_ULTRA_STATE_DIR` | Future on-disk job persistence (in-memory in v1.0). |
|
|
29
29
|
| `PATROL_WEB_BROWSER_ARGS` | Comma-separated Chromium flags merged into every `--web-browser-args`. Default: `--enable-unsafe-swiftshader,--disable-renderer-backgrounding,--disable-background-timer-throttling`. |
|
|
30
|
-
| `FLUTTER_ULTRA_LOG_LEVEL` | `debug`/`info`/`warn`/`error`. Default `info`.
|
|
30
|
+
| `FLUTTER_ULTRA_LOG_LEVEL` | `debug`/`info`/`warn`/`error`. Default `info`. |
|
|
31
31
|
|
|
32
32
|
## Invocation strategy
|
|
33
33
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@flutter-ultra/contracts",
|
|
3
3
|
"version": "0.0.0",
|
|
4
4
|
"private": true,
|
|
5
|
-
"description": "JSON Schema contract definitions for ext.flutter.ultra.* wire format
|
|
5
|
+
"description": "JSON Schema contract definitions for ext.flutter.ultra.* wire format — single source of truth for TS↔Dart interop.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"type": "module",
|
|
8
8
|
"main": "dist/index.js",
|
|
@@ -28,4 +28,4 @@
|
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"vitest": "^3.2.4"
|
|
30
30
|
}
|
|
31
|
-
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flutter-bisect
|
|
3
|
+
description: Automate git bisect to find the commit that introduced a bug. Flutter-aware: runs pub get + build_runner between commits. Use when a test passes on an older commit but fails on HEAD and you need to find exactly which commit broke it.
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# flutter-bisect — Automated Regression Finder
|
|
8
|
+
|
|
9
|
+
## When to use
|
|
10
|
+
|
|
11
|
+
Use this skill when a test (unit, widget, or patrol E2E) passes on an older commit but fails on HEAD and you need to pinpoint exactly which commit introduced the regression. Do not use for diagnosing a bug you already know the source of — use `flutter-debug` instead.
|
|
12
|
+
|
|
13
|
+
## Prerequisites
|
|
14
|
+
|
|
15
|
+
- The working tree is in a git repository (`git status` returns without error).
|
|
16
|
+
- The user provides (or you can infer) a **known-good** commit reference: a tag (`v1.2.0`), a branch name (`main@{7 days ago}`), or a full SHA.
|
|
17
|
+
- A **test oracle** is specified: a unit/widget test name pattern, a patrol test file, or a Bash-style exit-code command.
|
|
18
|
+
- No uncommitted changes that would interfere — stash them first (see Edge cases).
|
|
19
|
+
|
|
20
|
+
## Workflow
|
|
21
|
+
|
|
22
|
+
### 1. Confirm inputs before starting
|
|
23
|
+
|
|
24
|
+
Ask (or confirm from context) the following before calling any git command:
|
|
25
|
+
|
|
26
|
+
- **Good commit**: the last-known-good reference (tag, SHA, or relative ref).
|
|
27
|
+
- **Bad commit**: defaults to `HEAD`.
|
|
28
|
+
- **Oracle type**: `unit`, `widget`, or `patrol`.
|
|
29
|
+
- **Oracle selector**: test name pattern or patrol `testFilePath`.
|
|
30
|
+
- **Project**: call `mcp__plugin_flutter_flutter-ultra-build__list_projects` if not already known.
|
|
31
|
+
- **Has build_runner**: check for `build.yaml` in the project root — if present, set `needs_build_runner=true`.
|
|
32
|
+
|
|
33
|
+
### 2. Stash uncommitted changes
|
|
34
|
+
|
|
35
|
+
Before touching any git ref, check for dirty working tree:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
git -C <project-root> status --porcelain
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
If output is non-empty, stash:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git -C <project-root> stash push -m "flutter-bisect-autoStash"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Record whether a stash was created — you must restore it in step 7.
|
|
48
|
+
|
|
49
|
+
### 3. Start bisect
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
git -C <project-root> bisect start --first-parent HEAD <good-commit>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`--first-parent` skips noise from merged feature branches and keeps the walk on the mainline. Git prints the number of steps remaining — surface this to the user.
|
|
56
|
+
|
|
57
|
+
### 4. At each bisect step — the Flutter-aware oracle loop
|
|
58
|
+
|
|
59
|
+
Repeat until git prints `<sha> is the first bad commit`:
|
|
60
|
+
|
|
61
|
+
#### 4a. Get current commit info
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
git -C <project-root> log -1 --format="%H %s"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Show the user which commit is under test and how many steps remain.
|
|
68
|
+
|
|
69
|
+
#### 4b. Restore Flutter state for this commit
|
|
70
|
+
|
|
71
|
+
Run these in order — do not skip even if pubspec.yaml looks unchanged (lockfile may differ):
|
|
72
|
+
|
|
73
|
+
1. **pub get**: `mcp__plugin_flutter_flutter-ultra-build__pub_get` with the project name.
|
|
74
|
+
- If pub get fails (e.g. dependency conflict introduced by this commit), mark the commit **bad** and continue — a broken dep is a broken state.
|
|
75
|
+
2. **build_runner** (only if `needs_build_runner=true`):
|
|
76
|
+
- Call `mcp__plugin_flutter_flutter-ultra-build__start_build_runner_build`.
|
|
77
|
+
- Poll `mcp__plugin_flutter_flutter-ultra-build__poll_build_runner_job` until done.
|
|
78
|
+
- Get result via `mcp__plugin_flutter_flutter-ultra-build__get_build_runner_result`.
|
|
79
|
+
- If build_runner fails: mark the commit **bad** and continue.
|
|
80
|
+
|
|
81
|
+
#### 4c. Run the oracle
|
|
82
|
+
|
|
83
|
+
**Unit/widget oracle:**
|
|
84
|
+
|
|
85
|
+
- Start: `mcp__plugin_flutter_flutter-ultra-build__start_run_unit_tests` with `testNamePattern` (or `start_run_widget_tests`).
|
|
86
|
+
- Poll: `mcp__plugin_flutter_flutter-ultra-build__poll_run_unit_tests` (or `poll_run_widget_tests`).
|
|
87
|
+
- Result: `mcp__plugin_flutter_flutter-ultra-build__get_run_unit_tests_result` (or `get_run_widget_tests_result`).
|
|
88
|
+
- If all targeted tests pass → **good**. If any fail → **bad**.
|
|
89
|
+
|
|
90
|
+
**Patrol oracle:**
|
|
91
|
+
|
|
92
|
+
- Start: `mcp__plugin_flutter_flutter-ultra-patrol__start_patrol_test` with `testFilePath` and `device`.
|
|
93
|
+
- Poll: `mcp__plugin_flutter_flutter-ultra-patrol__poll_patrol_job`.
|
|
94
|
+
- Result: `mcp__plugin_flutter_flutter-ultra-patrol__get_patrol_result`.
|
|
95
|
+
- If all steps pass → **good**. If any fail → **bad**.
|
|
96
|
+
|
|
97
|
+
#### 4d. Mark the commit
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# if oracle passed:
|
|
101
|
+
git -C <project-root> bisect good
|
|
102
|
+
|
|
103
|
+
# if oracle failed:
|
|
104
|
+
git -C <project-root> bisect bad
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Git will output the next commit to test, or the final verdict. Loop back to 4a.
|
|
108
|
+
|
|
109
|
+
### 5. Capture and report the first bad commit
|
|
110
|
+
|
|
111
|
+
When git prints `<sha> is the first bad commit`, capture the full details:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
git -C <project-root> show --stat <sha>
|
|
115
|
+
git -C <project-root> log -1 --format="%H%n%an <%ae>%n%ad%n%s%n%b" --date=iso <sha>
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Present a structured report (see Output format).
|
|
119
|
+
|
|
120
|
+
### 6. Reset bisect
|
|
121
|
+
|
|
122
|
+
Always reset, even on error:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
git -C <project-root> bisect reset
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### 7. Restore stash (if created in step 2)
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
git -C <project-root> stash pop
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Edge cases
|
|
135
|
+
|
|
136
|
+
| Situation | Handling |
|
|
137
|
+
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
138
|
+
| **Merge commits on the walk** | `--first-parent` avoids them; if user omits it intentionally (to bisect a merged branch), remove the flag and warn that step count increases. |
|
|
139
|
+
| **Uncommitted changes** | Auto-stash in step 2; restore in step 7. If stash pop fails (conflict), warn the user and leave the stash intact — do not discard it. |
|
|
140
|
+
| **Submodule repo** | `git bisect` operates on the outer repo. If the Flutter project is a submodule, confirm the user wants to bisect the outer aggregate, not the inner submodule. Pass the correct `-C <path>` to all git commands. |
|
|
141
|
+
| **Flutter SDK version change** | If a commit changes the Flutter SDK constraint (`.tool-versions`, `fvm` config, `flutter.constraints`), call `mcp__plugin_flutter_flutter-ultra-build__flutter_clean` before pub get to clear the compiled kernel cache. |
|
|
142
|
+
| **pub get resolves a different lockfile** | Expected — this is exactly what the skill needs. Never pin the lockfile artificially during bisect. |
|
|
143
|
+
| **Oracle is flaky** | If the oracle fails on a commit that visually looks clean, re-run it once. If it fails again, mark bad. Do not retry more than once per commit — flakiness analysis is out of scope here; use `flutter-debug` after bisect completes. |
|
|
144
|
+
| **All commits bad (misconfigured good ref)** | If `git bisect start` immediately shows 0 steps or git says the good commit is not an ancestor, stop and ask the user to verify the good commit reference. |
|
|
145
|
+
|
|
146
|
+
## Output format
|
|
147
|
+
|
|
148
|
+
After bisect completes, produce:
|
|
149
|
+
|
|
150
|
+
```
|
|
151
|
+
## Bisect Result
|
|
152
|
+
|
|
153
|
+
First bad commit: <sha>
|
|
154
|
+
Author: <name> <<email>>
|
|
155
|
+
Date: <iso date>
|
|
156
|
+
Message: <subject line>
|
|
157
|
+
|
|
158
|
+
<body if present>
|
|
159
|
+
|
|
160
|
+
Files changed:
|
|
161
|
+
<git show --stat output>
|
|
162
|
+
|
|
163
|
+
Steps taken: <N> commits tested across <M> total candidates.
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
If bisect could not converge (user cancelled, all commits bad, or git error), produce:
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
## Bisect Aborted
|
|
170
|
+
|
|
171
|
+
Reason: <what went wrong>
|
|
172
|
+
Last tested commit: <sha or "none">
|
|
173
|
+
Recommendation: <next debugging step>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Example
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
User: "The InvoiceBloc unit test was green on v1.3.0 but fails on HEAD. Find which commit broke it."
|
|
180
|
+
|
|
181
|
+
1. list_projects → project: "invora-flutter"
|
|
182
|
+
2. Check build.yaml → present → needs_build_runner=true
|
|
183
|
+
3. git status → clean → no stash needed
|
|
184
|
+
4. git bisect start --first-parent HEAD v1.3.0 → "~6 steps (roughly 53 revisions)"
|
|
185
|
+
5. [commit abc123] pub_get → ok; build_runner → ok; run_unit_tests(pattern: "InvoiceBloc") → FAIL → bisect bad
|
|
186
|
+
6. [commit def456] pub_get → ok; build_runner → ok; run_unit_tests → FAIL → bisect bad
|
|
187
|
+
7. [commit ghi789] pub_get → ok; build_runner → ok; run_unit_tests → PASS → bisect good
|
|
188
|
+
8. [commit jkl012] pub_get → ok; build_runner → ok; run_unit_tests → FAIL → bisect bad
|
|
189
|
+
9. [commit mno345] pub_get → ok; build_runner → ok; run_unit_tests → PASS → bisect good
|
|
190
|
+
10. [commit pqr678] pub_get → ok; build_runner → ok; run_unit_tests → FAIL → bisect bad
|
|
191
|
+
11. git: "pqr678 is the first bad commit"
|
|
192
|
+
12. git show --stat pqr678 → "refactor(billing): collapse invoice state machine"
|
|
193
|
+
13. git bisect reset
|
|
194
|
+
→ Report: first bad commit pqr678, author, date, changed files
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## See also
|
|
198
|
+
|
|
199
|
+
- `flutter-test` — run the full test suite without bisecting
|
|
200
|
+
- `flutter-debug` — inspect live runtime state after bisect identifies a suspect commit
|
|
201
|
+
- `mcp__plugin_flutter_flutter-ultra-build__start_build_runner_build` — build_runner reference
|
|
202
|
+
- `mcp__plugin_flutter_flutter-ultra-patrol__start_patrol_test` — patrol oracle reference
|
package/skills/debug/SKILL.md
CHANGED
|
@@ -1,20 +1,156 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: debug
|
|
2
|
+
name: flutter-debug
|
|
3
3
|
description: Attaching to a running Flutter app and triaging an error from the stack trace, widget tree, render tree, and recent screenshot. Use when the user reports a runtime exception, layout overflow, or unexpected behaviour and you need to inspect live state.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
#
|
|
6
|
+
# flutter-debug — Attach, Inspect, and Triage
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
Use this skill when the user reports a runtime exception, a layout overflow, a blank screen, unexpected navigation, or any "it's broken" situation in a running Flutter app. The goal is to collect enough live evidence to diagnose the root cause without guessing. Propose code fixes only after inspecting live state — never before.
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
- A Flutter app is running in debug mode (VM Service available).
|
|
15
|
+
- The user has described the symptom: exception message, screen name, reproduction steps, or "just broke".
|
|
16
|
+
- Do **not** modify any source files during this skill unless the user explicitly asks for a fix.
|
|
9
17
|
|
|
10
18
|
## Workflow
|
|
11
19
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
-
|
|
20
|
+
Follow the triage ladder in order — stop at the level where the root cause becomes clear.
|
|
21
|
+
|
|
22
|
+
### 1. Attach to the session
|
|
23
|
+
|
|
24
|
+
- Call `mcp__plugin_flutter_flutter-ultra-runtime__discover_sessions` — list all active sessions.
|
|
25
|
+
- Pick the session matching the reported app (by name or port).
|
|
26
|
+
- Call `mcp__plugin_flutter_flutter-ultra-runtime__attach` with `sessionId`.
|
|
27
|
+
|
|
28
|
+
### 2. Capture initial state
|
|
29
|
+
|
|
30
|
+
Run these together immediately after attach:
|
|
31
|
+
|
|
32
|
+
- `mcp__plugin_flutter_flutter-ultra-runtime__screenshot` — visual snapshot of the current screen.
|
|
33
|
+
- `mcp__plugin_flutter_flutter-ultra-runtime__get_runtime_errors` — all unhandled exceptions since last clear.
|
|
34
|
+
- `mcp__plugin_flutter_flutter-ultra-runtime__get_logs` — recent `debugPrint` / `print` / framework log output.
|
|
35
|
+
|
|
36
|
+
### 3. Triage by error type
|
|
37
|
+
|
|
38
|
+
#### 3a. Runtime exception (stack trace present)
|
|
39
|
+
|
|
40
|
+
1. Read the stack trace from `get_runtime_errors` — identify the throwing file and line.
|
|
41
|
+
2. Call `mcp__plugin_flutter_flutter-ultra-runtime__evaluate` to inspect the problematic object:
|
|
42
|
+
```dart
|
|
43
|
+
// Example: check if a value is null
|
|
44
|
+
MyWidget.of(context)?.someField?.toString() ?? 'NULL'
|
|
45
|
+
```
|
|
46
|
+
3. Call `mcp__plugin_flutter_flutter-ultra-runtime__get_widget_tree` focused on the widget subtree around the reported location — pass `widgetId` if known from the stack frame.
|
|
47
|
+
4. Look for: null state, missing providers, incorrect key types, uninitialized controllers.
|
|
48
|
+
|
|
49
|
+
#### 3b. Layout overflow (RenderFlex / RenderBox overflow)
|
|
50
|
+
|
|
51
|
+
1. Call `mcp__plugin_flutter_flutter-ultra-runtime__dump_render_tree` — search the output for `OVERFLOWED` or constraint violations.
|
|
52
|
+
2. Call `mcp__plugin_flutter_flutter-ultra-runtime__toggle_debug_paint` to enable visual constraint overlays; take another `screenshot`.
|
|
53
|
+
3. Identify the overflowing `RenderFlex` or `RenderConstrainedBox` and trace it back to the widget via `get_widget_tree`.
|
|
54
|
+
4. Common causes: missing `Expanded`/`Flexible`, fixed height in a `Column` inside a scrollable, `Text` without `overflow: TextOverflow.ellipsis`.
|
|
55
|
+
|
|
56
|
+
#### 3c. Blank screen / wrong route
|
|
57
|
+
|
|
58
|
+
1. Call `mcp__plugin_flutter_flutter-ultra-runtime__evaluate`:
|
|
59
|
+
```dart
|
|
60
|
+
GoRouter.of(context).routerDelegate.currentConfiguration.fullPath
|
|
61
|
+
```
|
|
62
|
+
to confirm the actual current route.
|
|
63
|
+
2. Call `mcp__plugin_flutter_flutter-ultra-runtime__get_widget_tree` from root — look for `ErrorWidget`, empty `SizedBox`, or a redirect loop (same route repeated in the navigator stack).
|
|
64
|
+
3. Check `get_logs` for GoRouter redirect events or `debugPrint` from route guards.
|
|
65
|
+
|
|
66
|
+
#### 3d. State / data issue (wrong data shown, stale UI)
|
|
67
|
+
|
|
68
|
+
1. Call `mcp__plugin_flutter_flutter-ultra-runtime__evaluate` to read the current BLoC/Riverpod/Provider state:
|
|
69
|
+
```dart
|
|
70
|
+
context.read<MyBloc>().state.toString()
|
|
71
|
+
// or for Riverpod:
|
|
72
|
+
ProviderScope.containerOf(context).read(myProvider).toString()
|
|
73
|
+
```
|
|
74
|
+
2. Compare against expected values from the user's description.
|
|
75
|
+
3. Call `mcp__plugin_flutter_flutter-ultra-runtime__get_widget_tree` to verify the widget rebuilds are reaching the right subtree.
|
|
76
|
+
|
|
77
|
+
#### 3e. Accessibility / semantics issue
|
|
78
|
+
|
|
79
|
+
1. Call `mcp__plugin_flutter_flutter-ultra-runtime__dump_semantics_tree` — look for missing labels, incorrect roles, or hidden interactive elements.
|
|
80
|
+
2. Check for `excludeFromSemantics: true` incorrectly applied to interactive widgets.
|
|
81
|
+
|
|
82
|
+
### 4. Inspect the widget tree around the problem
|
|
83
|
+
|
|
84
|
+
- Call `mcp__plugin_flutter_flutter-ultra-runtime__get_widget_tree` with the suspected parent widget key or type as anchor.
|
|
85
|
+
- Call `mcp__plugin_flutter_flutter-ultra-runtime__get_widget_details` on a specific widget ID to get its full properties (constraints, size, key, state).
|
|
86
|
+
- Call `mcp__plugin_flutter_flutter-ultra-runtime__find_widget` to locate a specific widget by key or text when the tree is large.
|
|
87
|
+
|
|
88
|
+
### 5. Deeper render inspection
|
|
89
|
+
|
|
90
|
+
If layout is the issue and the widget tree alone is not enough:
|
|
91
|
+
|
|
92
|
+
- Call `mcp__plugin_flutter_flutter-ultra-runtime__dump_render_tree` — this shows sizes, constraints, and positions for every render object.
|
|
93
|
+
- Call `mcp__plugin_flutter_flutter-ultra-runtime__dump_layer_tree` for compositing and repaint boundary issues (useful for performance jank or incorrect clipping).
|
|
94
|
+
|
|
95
|
+
### 6. Evaluate in-app expressions
|
|
96
|
+
|
|
97
|
+
Use `mcp__plugin_flutter_flutter-ultra-runtime__evaluate` freely to inspect live objects:
|
|
98
|
+
|
|
99
|
+
- Check if a future completed: `myCompleter.isCompleted`
|
|
100
|
+
- Read a stream's last value: `myStreamController.stream` (wrap in a Future)
|
|
101
|
+
- Confirm a service is initialized: `MyService.instance != null`
|
|
102
|
+
|
|
103
|
+
### 7. Test the fix
|
|
104
|
+
|
|
105
|
+
Once the root cause is identified and a code fix is proposed (or applied at user request):
|
|
106
|
+
|
|
107
|
+
- Call `mcp__plugin_flutter_flutter-ultra-runtime__hot_reload` to apply changes without losing app state.
|
|
108
|
+
- Repeat step 3 (appropriate branch) to confirm the error is gone.
|
|
109
|
+
- Call `mcp__plugin_flutter_flutter-ultra-runtime__screenshot` for a before/after comparison.
|
|
110
|
+
- If hot reload is not sufficient (e.g. `initState` changed), call `mcp__plugin_flutter_flutter-ultra-runtime__hot_restart`.
|
|
111
|
+
|
|
112
|
+
## Common patterns and their diagnosis
|
|
113
|
+
|
|
114
|
+
| Symptom | First tool | What to look for |
|
|
115
|
+
| ------------------------------------------ | ----------------------------------------- | -------------------------------------------------------------------- |
|
|
116
|
+
| `Null check operator used on a null value` | `get_runtime_errors` + `evaluate` | Null state before async load completes; missing null guard |
|
|
117
|
+
| `RenderFlex overflowed by N pixels` | `dump_render_tree` + `toggle_debug_paint` | Column/Row child without `Expanded`; fixed height container |
|
|
118
|
+
| Blank white screen | `get_widget_tree` + `evaluate` (route) | `ErrorWidget` at root; redirect loop; unhandled exception in build |
|
|
119
|
+
| `setState called after dispose` | `get_runtime_errors` + `get_logs` | Async callback holding stale `BuildContext`; missing `mounted` check |
|
|
120
|
+
| Navigation not working | `evaluate` (GoRouter path) + `get_logs` | Route guard redirecting; wrong named route; deep link not registered |
|
|
121
|
+
| Infinite loading spinner | `evaluate` (state) + `get_logs` | Future never completing; stream not emitting; provider not notifying |
|
|
122
|
+
| Wrong data displayed | `evaluate` (BLoC/provider state) | Stale state; `context.watch` vs `context.read` misuse |
|
|
123
|
+
|
|
124
|
+
## Output format
|
|
125
|
+
|
|
126
|
+
After triage, produce:
|
|
127
|
+
|
|
128
|
+
1. **Root cause**: one sentence identifying the exact problem.
|
|
129
|
+
2. **Evidence**: which tool output revealed it (stack trace line, widget tree excerpt, render tree constraint).
|
|
130
|
+
3. **Proposed fix**: specific code change with file and line reference (do not edit unless asked).
|
|
131
|
+
4. **Screenshots**: before state screenshot path; after-fix screenshot path if hot_reload was applied.
|
|
132
|
+
|
|
133
|
+
## Example
|
|
134
|
+
|
|
135
|
+
```
|
|
136
|
+
User: "The invoices list is showing a blank white screen after I merged the new filter PR."
|
|
137
|
+
|
|
138
|
+
1. discover_sessions → attach(sessionId: "flutter-1")
|
|
139
|
+
2. screenshot → blank screen confirmed
|
|
140
|
+
3. get_runtime_errors → "Null check operator used on null value at invoice_list_bloc.dart:47"
|
|
141
|
+
4. evaluate: context.read<InvoiceListBloc>().state.toString() → "InvoiceListInitial"
|
|
142
|
+
(bloc never emitted data — the filter query returned null instead of empty list)
|
|
143
|
+
5. get_widget_tree → root is ErrorWidget wrapping the list scaffold
|
|
144
|
+
6. Root cause: InvoiceListBloc.mapEventToState at line 47 calls `event.filter!` but
|
|
145
|
+
filter is null on first load after the PR introduced a nullable field.
|
|
146
|
+
7. Proposed fix: change `event.filter!` to `event.filter ?? const InvoiceFilter()` at
|
|
147
|
+
invoice_list_bloc.dart:47.
|
|
148
|
+
8. hot_reload → screenshot → list loads correctly.
|
|
149
|
+
```
|
|
17
150
|
|
|
18
151
|
## See also
|
|
19
152
|
|
|
20
|
-
-
|
|
153
|
+
- Sibling skill: `flutter-drive` for driving interactive flows before/after a fix
|
|
154
|
+
- `mcp__plugin_flutter_flutter-ultra-runtime__get_runtime_errors` — unhandled exception log
|
|
155
|
+
- `mcp__plugin_flutter_flutter-ultra-runtime__dump_render_tree` — full render object tree with constraints
|
|
156
|
+
- `mcp__plugin_flutter_flutter-ultra-runtime__evaluate` — arbitrary Dart expression in live app context
|