flutter-skill-mcp 0.2.19 → 0.2.20

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/README.md CHANGED
@@ -154,13 +154,42 @@ flutter-skill.inspect()
154
154
 
155
155
  ## Installation Methods
156
156
 
157
- | Method | Command | Auto-Update | Native Binary |
158
- |--------|---------|-------------|---------------|
159
- | npm | `npm install -g flutter-skill-mcp` | Manual | Auto-download |
160
- | Homebrew | `brew install ai-dashboad/flutter-skill/flutter-skill` | `brew upgrade` | Pre-compiled |
161
- | VSCode | Extensions "Flutter Skill" | Auto | Auto-download |
162
- | IntelliJ | Plugins "Flutter Skill" | Auto | Auto-download |
163
- | pub.dev | `dart pub global activate flutter_skill` | Manual | Dart runtime |
157
+ | Method | Command | Platform |
158
+ |--------|---------|----------|
159
+ | **npm** | `npm install -g flutter-skill-mcp` | All |
160
+ | **Homebrew** | `brew install ai-dashboad/flutter-skill/flutter-skill` | macOS/Linux |
161
+ | **Docker** | `docker pull ghcr.io/ai-dashboad/flutter-skill` | All |
162
+ | **Snap** | `snap install flutter-skill` | Linux |
163
+ | **Scoop** | `scoop install flutter-skill` | Windows |
164
+ | **Winget** | `winget install AIDashboard.FlutterSkill` | Windows |
165
+ | **pub.dev** | `dart pub global activate flutter_skill` | All |
166
+ | **VSCode** | Extensions → "Flutter Skill" | All |
167
+ | **IntelliJ** | Plugins → "Flutter Skill" | All |
168
+ | **Devcontainer** | See below | All |
169
+
170
+ ### Docker
171
+
172
+ ```bash
173
+ # Run MCP server
174
+ docker run --rm -it ghcr.io/ai-dashboad/flutter-skill server
175
+
176
+ # Or use in docker-compose
177
+ services:
178
+ flutter-skill:
179
+ image: ghcr.io/ai-dashboad/flutter-skill:latest
180
+ command: ["server"]
181
+ ```
182
+
183
+ ### Devcontainer Feature
184
+
185
+ Add to your `.devcontainer/devcontainer.json`:
186
+ ```json
187
+ {
188
+ "features": {
189
+ "ghcr.io/ai-dashboad/flutter-skill/flutter-skill:latest": {}
190
+ }
191
+ }
192
+ ```
164
193
 
165
194
  ### Native Binary Performance
166
195
  | Version | Startup Time |
@@ -189,7 +218,7 @@ flutter-skill launch /path/to/project
189
218
  1. Add dependency:
190
219
  ```yaml
191
220
  dependencies:
192
- flutter_skill: ^0.2.19
221
+ flutter_skill: ^0.2.20
193
222
  ```
194
223
 
195
224
  2. Initialize in main.dart:
@@ -25,7 +25,8 @@ class FlutterSkillBinding {
25
25
  // ==================== EXISTING EXTENSIONS ====================
26
26
 
27
27
  // 1. Interactive Elements
28
- developer.registerExtension('ext.flutter.flutter_skill.interactive', (method, parameters) async {
28
+ developer.registerExtension('ext.flutter.flutter_skill.interactive',
29
+ (method, parameters) async {
29
30
  try {
30
31
  final elements = _findInteractiveElements();
31
32
  return developer.ServiceExtensionResponse.result(
@@ -37,13 +38,17 @@ class FlutterSkillBinding {
37
38
  });
38
39
 
39
40
  // 2. Tap
40
- developer.registerExtension('ext.flutter.flutter_skill.tap', (method, parameters) async {
41
+ developer.registerExtension('ext.flutter.flutter_skill.tap',
42
+ (method, parameters) async {
41
43
  try {
42
44
  final key = parameters['key'];
43
45
  final text = parameters['text'];
44
46
  final success = await _performTap(key: key, text: text);
45
47
  return developer.ServiceExtensionResponse.result(
46
- jsonEncode({'success': success, 'message': success ? 'Tap successful' : 'Element not found'}),
48
+ jsonEncode({
49
+ 'success': success,
50
+ 'message': success ? 'Tap successful' : 'Element not found'
51
+ }),
47
52
  );
48
53
  } catch (e, stack) {
49
54
  return _errorResponse(e, stack);
@@ -51,7 +56,8 @@ class FlutterSkillBinding {
51
56
  });
52
57
 
53
58
  // 3. Enter Text
54
- developer.registerExtension('ext.flutter.flutter_skill.enterText', (method, parameters) async {
59
+ developer.registerExtension('ext.flutter.flutter_skill.enterText',
60
+ (method, parameters) async {
55
61
  try {
56
62
  final key = parameters['key'];
57
63
  final text = parameters['text'];
@@ -63,7 +69,10 @@ class FlutterSkillBinding {
63
69
  }
64
70
  final success = await _performEnterText(key: key, text: text);
65
71
  return developer.ServiceExtensionResponse.result(
66
- jsonEncode({'success': success, 'message': success ? 'Text entered' : 'TextField not found'}),
72
+ jsonEncode({
73
+ 'success': success,
74
+ 'message': success ? 'Text entered' : 'TextField not found'
75
+ }),
67
76
  );
68
77
  } catch (e, stack) {
69
78
  return _errorResponse(e, stack);
@@ -71,13 +80,17 @@ class FlutterSkillBinding {
71
80
  });
72
81
 
73
82
  // 4. Scroll
74
- developer.registerExtension('ext.flutter.flutter_skill.scroll', (method, parameters) async {
83
+ developer.registerExtension('ext.flutter.flutter_skill.scroll',
84
+ (method, parameters) async {
75
85
  try {
76
86
  final key = parameters['key'];
77
87
  final text = parameters['text'];
78
88
  final success = await _performScroll(key: key, text: text);
79
89
  return developer.ServiceExtensionResponse.result(
80
- jsonEncode({'success': success, 'message': success ? 'Scroll successful' : 'Element not found'}),
90
+ jsonEncode({
91
+ 'success': success,
92
+ 'message': success ? 'Scroll successful' : 'Element not found'
93
+ }),
81
94
  );
82
95
  } catch (e, stack) {
83
96
  return _errorResponse(e, stack);
@@ -87,18 +100,21 @@ class FlutterSkillBinding {
87
100
  // ==================== UI INSPECTION EXTENSIONS ====================
88
101
 
89
102
  // 5. Get Widget Tree
90
- developer.registerExtension('ext.flutter.flutter_skill.getWidgetTree', (method, parameters) async {
103
+ developer.registerExtension('ext.flutter.flutter_skill.getWidgetTree',
104
+ (method, parameters) async {
91
105
  try {
92
106
  final maxDepth = int.tryParse(parameters['maxDepth'] ?? '10') ?? 10;
93
107
  final tree = _getWidgetTree(maxDepth: maxDepth);
94
- return developer.ServiceExtensionResponse.result(jsonEncode({'tree': tree}));
108
+ return developer.ServiceExtensionResponse.result(
109
+ jsonEncode({'tree': tree}));
95
110
  } catch (e, stack) {
96
111
  return _errorResponse(e, stack);
97
112
  }
98
113
  });
99
114
 
100
115
  // 6. Get Widget Properties
101
- developer.registerExtension('ext.flutter.flutter_skill.getWidgetProperties', (method, parameters) async {
116
+ developer.registerExtension('ext.flutter.flutter_skill.getWidgetProperties',
117
+ (method, parameters) async {
102
118
  try {
103
119
  final key = parameters['key'];
104
120
  if (key == null) {
@@ -108,24 +124,28 @@ class FlutterSkillBinding {
108
124
  );
109
125
  }
110
126
  final properties = _getWidgetProperties(key);
111
- return developer.ServiceExtensionResponse.result(jsonEncode({'properties': properties}));
127
+ return developer.ServiceExtensionResponse.result(
128
+ jsonEncode({'properties': properties}));
112
129
  } catch (e, stack) {
113
130
  return _errorResponse(e, stack);
114
131
  }
115
132
  });
116
133
 
117
134
  // 7. Get Text Content
118
- developer.registerExtension('ext.flutter.flutter_skill.getTextContent', (method, parameters) async {
135
+ developer.registerExtension('ext.flutter.flutter_skill.getTextContent',
136
+ (method, parameters) async {
119
137
  try {
120
138
  final textList = _getTextContent();
121
- return developer.ServiceExtensionResponse.result(jsonEncode({'texts': textList}));
139
+ return developer.ServiceExtensionResponse.result(
140
+ jsonEncode({'texts': textList}));
122
141
  } catch (e, stack) {
123
142
  return _errorResponse(e, stack);
124
143
  }
125
144
  });
126
145
 
127
146
  // 8. Find By Type
128
- developer.registerExtension('ext.flutter.flutter_skill.findByType', (method, parameters) async {
147
+ developer.registerExtension('ext.flutter.flutter_skill.findByType',
148
+ (method, parameters) async {
129
149
  try {
130
150
  final type = parameters['type'];
131
151
  if (type == null) {
@@ -135,7 +155,8 @@ class FlutterSkillBinding {
135
155
  );
136
156
  }
137
157
  final elements = _findByType(type);
138
- return developer.ServiceExtensionResponse.result(jsonEncode({'elements': elements}));
158
+ return developer.ServiceExtensionResponse.result(
159
+ jsonEncode({'elements': elements}));
139
160
  } catch (e, stack) {
140
161
  return _errorResponse(e, stack);
141
162
  }
@@ -144,14 +165,19 @@ class FlutterSkillBinding {
144
165
  // ==================== MORE INTERACTIONS ====================
145
166
 
146
167
  // 9. Long Press
147
- developer.registerExtension('ext.flutter.flutter_skill.longPress', (method, parameters) async {
168
+ developer.registerExtension('ext.flutter.flutter_skill.longPress',
169
+ (method, parameters) async {
148
170
  try {
149
171
  final key = parameters['key'];
150
172
  final text = parameters['text'];
151
173
  final duration = int.tryParse(parameters['duration'] ?? '500') ?? 500;
152
- final success = await _performLongPress(key: key, text: text, duration: duration);
174
+ final success =
175
+ await _performLongPress(key: key, text: text, duration: duration);
153
176
  return developer.ServiceExtensionResponse.result(
154
- jsonEncode({'success': success, 'message': success ? 'Long press successful' : 'Element not found'}),
177
+ jsonEncode({
178
+ 'success': success,
179
+ 'message': success ? 'Long press successful' : 'Element not found'
180
+ }),
155
181
  );
156
182
  } catch (e, stack) {
157
183
  return _errorResponse(e, stack);
@@ -159,14 +185,20 @@ class FlutterSkillBinding {
159
185
  });
160
186
 
161
187
  // 10. Swipe
162
- developer.registerExtension('ext.flutter.flutter_skill.swipe', (method, parameters) async {
188
+ developer.registerExtension('ext.flutter.flutter_skill.swipe',
189
+ (method, parameters) async {
163
190
  try {
164
191
  final direction = parameters['direction'] ?? 'up';
165
- final distance = double.tryParse(parameters['distance'] ?? '300') ?? 300;
192
+ final distance =
193
+ double.tryParse(parameters['distance'] ?? '300') ?? 300;
166
194
  final key = parameters['key'];
167
- final success = await _performSwipe(direction: direction, distance: distance, key: key);
195
+ final success = await _performSwipe(
196
+ direction: direction, distance: distance, key: key);
168
197
  return developer.ServiceExtensionResponse.result(
169
- jsonEncode({'success': success, 'message': success ? 'Swipe successful' : 'Swipe failed'}),
198
+ jsonEncode({
199
+ 'success': success,
200
+ 'message': success ? 'Swipe successful' : 'Swipe failed'
201
+ }),
170
202
  );
171
203
  } catch (e, stack) {
172
204
  return _errorResponse(e, stack);
@@ -174,13 +206,17 @@ class FlutterSkillBinding {
174
206
  });
175
207
 
176
208
  // 11. Drag
177
- developer.registerExtension('ext.flutter.flutter_skill.drag', (method, parameters) async {
209
+ developer.registerExtension('ext.flutter.flutter_skill.drag',
210
+ (method, parameters) async {
178
211
  try {
179
212
  final fromKey = parameters['fromKey'];
180
213
  final toKey = parameters['toKey'];
181
214
  final success = await _performDrag(fromKey: fromKey, toKey: toKey);
182
215
  return developer.ServiceExtensionResponse.result(
183
- jsonEncode({'success': success, 'message': success ? 'Drag successful' : 'Drag failed'}),
216
+ jsonEncode({
217
+ 'success': success,
218
+ 'message': success ? 'Drag successful' : 'Drag failed'
219
+ }),
184
220
  );
185
221
  } catch (e, stack) {
186
222
  return _errorResponse(e, stack);
@@ -188,13 +224,17 @@ class FlutterSkillBinding {
188
224
  });
189
225
 
190
226
  // 12. Double Tap
191
- developer.registerExtension('ext.flutter.flutter_skill.doubleTap', (method, parameters) async {
227
+ developer.registerExtension('ext.flutter.flutter_skill.doubleTap',
228
+ (method, parameters) async {
192
229
  try {
193
230
  final key = parameters['key'];
194
231
  final text = parameters['text'];
195
232
  final success = await _performDoubleTap(key: key, text: text);
196
233
  return developer.ServiceExtensionResponse.result(
197
- jsonEncode({'success': success, 'message': success ? 'Double tap successful' : 'Element not found'}),
234
+ jsonEncode({
235
+ 'success': success,
236
+ 'message': success ? 'Double tap successful' : 'Element not found'
237
+ }),
198
238
  );
199
239
  } catch (e, stack) {
200
240
  return _errorResponse(e, stack);
@@ -204,7 +244,8 @@ class FlutterSkillBinding {
204
244
  // ==================== STATE & VALIDATION ====================
205
245
 
206
246
  // 13. Get Text Value
207
- developer.registerExtension('ext.flutter.flutter_skill.getTextValue', (method, parameters) async {
247
+ developer.registerExtension('ext.flutter.flutter_skill.getTextValue',
248
+ (method, parameters) async {
208
249
  try {
209
250
  final key = parameters['key'];
210
251
  if (key == null) {
@@ -214,14 +255,16 @@ class FlutterSkillBinding {
214
255
  );
215
256
  }
216
257
  final value = _getTextFieldValue(key);
217
- return developer.ServiceExtensionResponse.result(jsonEncode({'value': value}));
258
+ return developer.ServiceExtensionResponse.result(
259
+ jsonEncode({'value': value}));
218
260
  } catch (e, stack) {
219
261
  return _errorResponse(e, stack);
220
262
  }
221
263
  });
222
264
 
223
265
  // 14. Get Checkbox State
224
- developer.registerExtension('ext.flutter.flutter_skill.getCheckboxState', (method, parameters) async {
266
+ developer.registerExtension('ext.flutter.flutter_skill.getCheckboxState',
267
+ (method, parameters) async {
225
268
  try {
226
269
  final key = parameters['key'];
227
270
  if (key == null) {
@@ -231,14 +274,16 @@ class FlutterSkillBinding {
231
274
  );
232
275
  }
233
276
  final state = _getCheckboxState(key);
234
- return developer.ServiceExtensionResponse.result(jsonEncode({'checked': state}));
277
+ return developer.ServiceExtensionResponse.result(
278
+ jsonEncode({'checked': state}));
235
279
  } catch (e, stack) {
236
280
  return _errorResponse(e, stack);
237
281
  }
238
282
  });
239
283
 
240
284
  // 15. Get Slider Value
241
- developer.registerExtension('ext.flutter.flutter_skill.getSliderValue', (method, parameters) async {
285
+ developer.registerExtension('ext.flutter.flutter_skill.getSliderValue',
286
+ (method, parameters) async {
242
287
  try {
243
288
  final key = parameters['key'];
244
289
  if (key == null) {
@@ -248,21 +293,27 @@ class FlutterSkillBinding {
248
293
  );
249
294
  }
250
295
  final value = _getSliderValue(key);
251
- return developer.ServiceExtensionResponse.result(jsonEncode({'value': value}));
296
+ return developer.ServiceExtensionResponse.result(
297
+ jsonEncode({'value': value}));
252
298
  } catch (e, stack) {
253
299
  return _errorResponse(e, stack);
254
300
  }
255
301
  });
256
302
 
257
303
  // 16. Wait For Element
258
- developer.registerExtension('ext.flutter.flutter_skill.waitForElement', (method, parameters) async {
304
+ developer.registerExtension('ext.flutter.flutter_skill.waitForElement',
305
+ (method, parameters) async {
259
306
  try {
260
307
  final key = parameters['key'];
261
308
  final text = parameters['text'];
262
309
  final timeout = int.tryParse(parameters['timeout'] ?? '5000') ?? 5000;
263
- final found = await _waitForElement(key: key, text: text, timeout: timeout);
310
+ final found =
311
+ await _waitForElement(key: key, text: text, timeout: timeout);
264
312
  return developer.ServiceExtensionResponse.result(
265
- jsonEncode({'found': found, 'message': found ? 'Element found' : 'Timeout waiting for element'}),
313
+ jsonEncode({
314
+ 'found': found,
315
+ 'message': found ? 'Element found' : 'Timeout waiting for element'
316
+ }),
266
317
  );
267
318
  } catch (e, stack) {
268
319
  return _errorResponse(e, stack);
@@ -270,14 +321,18 @@ class FlutterSkillBinding {
270
321
  });
271
322
 
272
323
  // 17. Wait For Gone
273
- developer.registerExtension('ext.flutter.flutter_skill.waitForGone', (method, parameters) async {
324
+ developer.registerExtension('ext.flutter.flutter_skill.waitForGone',
325
+ (method, parameters) async {
274
326
  try {
275
327
  final key = parameters['key'];
276
328
  final text = parameters['text'];
277
329
  final timeout = int.tryParse(parameters['timeout'] ?? '5000') ?? 5000;
278
330
  final gone = await _waitForGone(key: key, text: text, timeout: timeout);
279
331
  return developer.ServiceExtensionResponse.result(
280
- jsonEncode({'gone': gone, 'message': gone ? 'Element is gone' : 'Element still present'}),
332
+ jsonEncode({
333
+ 'gone': gone,
334
+ 'message': gone ? 'Element is gone' : 'Element still present'
335
+ }),
281
336
  );
282
337
  } catch (e, stack) {
283
338
  return _errorResponse(e, stack);
@@ -287,17 +342,20 @@ class FlutterSkillBinding {
287
342
  // ==================== SCREENSHOT ====================
288
343
 
289
344
  // 18. Screenshot
290
- developer.registerExtension('ext.flutter.flutter_skill.screenshot', (method, parameters) async {
345
+ developer.registerExtension('ext.flutter.flutter_skill.screenshot',
346
+ (method, parameters) async {
291
347
  try {
292
348
  final base64Image = await _takeScreenshot();
293
- return developer.ServiceExtensionResponse.result(jsonEncode({'image': base64Image}));
349
+ return developer.ServiceExtensionResponse.result(
350
+ jsonEncode({'image': base64Image}));
294
351
  } catch (e, stack) {
295
352
  return _errorResponse(e, stack);
296
353
  }
297
354
  });
298
355
 
299
356
  // 19. Screenshot Element
300
- developer.registerExtension('ext.flutter.flutter_skill.screenshotElement', (method, parameters) async {
357
+ developer.registerExtension('ext.flutter.flutter_skill.screenshotElement',
358
+ (method, parameters) async {
301
359
  try {
302
360
  final key = parameters['key'];
303
361
  if (key == null) {
@@ -307,7 +365,8 @@ class FlutterSkillBinding {
307
365
  );
308
366
  }
309
367
  final base64Image = await _takeElementScreenshot(key);
310
- return developer.ServiceExtensionResponse.result(jsonEncode({'image': base64Image}));
368
+ return developer.ServiceExtensionResponse.result(
369
+ jsonEncode({'image': base64Image}));
311
370
  } catch (e, stack) {
312
371
  return _errorResponse(e, stack);
313
372
  }
@@ -316,21 +375,27 @@ class FlutterSkillBinding {
316
375
  // ==================== NAVIGATION ====================
317
376
 
318
377
  // 20. Get Current Route
319
- developer.registerExtension('ext.flutter.flutter_skill.getCurrentRoute', (method, parameters) async {
378
+ developer.registerExtension('ext.flutter.flutter_skill.getCurrentRoute',
379
+ (method, parameters) async {
320
380
  try {
321
381
  final route = _getCurrentRoute();
322
- return developer.ServiceExtensionResponse.result(jsonEncode({'route': route}));
382
+ return developer.ServiceExtensionResponse.result(
383
+ jsonEncode({'route': route}));
323
384
  } catch (e, stack) {
324
385
  return _errorResponse(e, stack);
325
386
  }
326
387
  });
327
388
 
328
389
  // 21. Go Back
329
- developer.registerExtension('ext.flutter.flutter_skill.goBack', (method, parameters) async {
390
+ developer.registerExtension('ext.flutter.flutter_skill.goBack',
391
+ (method, parameters) async {
330
392
  try {
331
393
  final success = _goBack();
332
394
  return developer.ServiceExtensionResponse.result(
333
- jsonEncode({'success': success, 'message': success ? 'Navigated back' : 'Cannot go back'}),
395
+ jsonEncode({
396
+ 'success': success,
397
+ 'message': success ? 'Navigated back' : 'Cannot go back'
398
+ }),
334
399
  );
335
400
  } catch (e, stack) {
336
401
  return _errorResponse(e, stack);
@@ -338,10 +403,12 @@ class FlutterSkillBinding {
338
403
  });
339
404
 
340
405
  // 22. Get Navigation Stack
341
- developer.registerExtension('ext.flutter.flutter_skill.getNavigationStack', (method, parameters) async {
406
+ developer.registerExtension('ext.flutter.flutter_skill.getNavigationStack',
407
+ (method, parameters) async {
342
408
  try {
343
409
  final stack = _getNavigationStack();
344
- return developer.ServiceExtensionResponse.result(jsonEncode({'stack': stack}));
410
+ return developer.ServiceExtensionResponse.result(
411
+ jsonEncode({'stack': stack}));
345
412
  } catch (e, stack) {
346
413
  return _errorResponse(e, stack);
347
414
  }
@@ -350,36 +417,43 @@ class FlutterSkillBinding {
350
417
  // ==================== DEBUG & LOGS ====================
351
418
 
352
419
  // 23. Get Logs
353
- developer.registerExtension('ext.flutter.flutter_skill.getLogs', (method, parameters) async {
420
+ developer.registerExtension('ext.flutter.flutter_skill.getLogs',
421
+ (method, parameters) async {
354
422
  try {
355
- return developer.ServiceExtensionResponse.result(jsonEncode({'logs': _logs}));
423
+ return developer.ServiceExtensionResponse.result(
424
+ jsonEncode({'logs': _logs}));
356
425
  } catch (e, stack) {
357
426
  return _errorResponse(e, stack);
358
427
  }
359
428
  });
360
429
 
361
430
  // 24. Get Errors
362
- developer.registerExtension('ext.flutter.flutter_skill.getErrors', (method, parameters) async {
431
+ developer.registerExtension('ext.flutter.flutter_skill.getErrors',
432
+ (method, parameters) async {
363
433
  try {
364
- return developer.ServiceExtensionResponse.result(jsonEncode({'errors': _errors}));
434
+ return developer.ServiceExtensionResponse.result(
435
+ jsonEncode({'errors': _errors}));
365
436
  } catch (e, stack) {
366
437
  return _errorResponse(e, stack);
367
438
  }
368
439
  });
369
440
 
370
441
  // 25. Clear Logs
371
- developer.registerExtension('ext.flutter.flutter_skill.clearLogs', (method, parameters) async {
442
+ developer.registerExtension('ext.flutter.flutter_skill.clearLogs',
443
+ (method, parameters) async {
372
444
  try {
373
445
  _logs.clear();
374
446
  _errors.clear();
375
- return developer.ServiceExtensionResponse.result(jsonEncode({'success': true}));
447
+ return developer.ServiceExtensionResponse.result(
448
+ jsonEncode({'success': true}));
376
449
  } catch (e, stack) {
377
450
  return _errorResponse(e, stack);
378
451
  }
379
452
  });
380
453
 
381
454
  // 26. Get Performance
382
- developer.registerExtension('ext.flutter.flutter_skill.getPerformance', (method, parameters) async {
455
+ developer.registerExtension('ext.flutter.flutter_skill.getPerformance',
456
+ (method, parameters) async {
383
457
  try {
384
458
  final perf = _getPerformanceMetrics();
385
459
  return developer.ServiceExtensionResponse.result(jsonEncode(perf));
@@ -400,7 +474,8 @@ class FlutterSkillBinding {
400
474
  };
401
475
  }
402
476
 
403
- static developer.ServiceExtensionResponse _errorResponse(Object e, StackTrace stack) {
477
+ static developer.ServiceExtensionResponse _errorResponse(
478
+ Object e, StackTrace stack) {
404
479
  return developer.ServiceExtensionResponse.error(
405
480
  developer.ServiceExtensionResponse.extensionError,
406
481
  '$e\n$stack',
@@ -414,7 +489,8 @@ class FlutterSkillBinding {
414
489
  void visit(Element element) {
415
490
  if (found != null) return;
416
491
  final widget = element.widget;
417
- if (widget.key is ValueKey<String> && (widget.key as ValueKey<String>).value == key) {
492
+ if (widget.key is ValueKey<String> &&
493
+ (widget.key as ValueKey<String>).value == key) {
418
494
  found = element;
419
495
  return;
420
496
  }
@@ -474,7 +550,8 @@ class FlutterSkillBinding {
474
550
  return false;
475
551
  }
476
552
 
477
- final center = renderObject.localToGlobal(renderObject.size.center(Offset.zero));
553
+ final center =
554
+ renderObject.localToGlobal(renderObject.size.center(Offset.zero));
478
555
  _log('Tapping at $center (key: $key, text: $text)');
479
556
 
480
557
  await _dispatchTap(center);
@@ -486,13 +563,16 @@ class FlutterSkillBinding {
486
563
  final binding = WidgetsBinding.instance;
487
564
  final pointer = _pointerCounter++;
488
565
 
489
- binding.handlePointerEvent(PointerDownEvent(position: position, pointer: pointer));
566
+ binding.handlePointerEvent(
567
+ PointerDownEvent(position: position, pointer: pointer));
490
568
  await Future.delayed(const Duration(milliseconds: 50));
491
- binding.handlePointerEvent(PointerUpEvent(position: position, pointer: pointer));
569
+ binding.handlePointerEvent(
570
+ PointerUpEvent(position: position, pointer: pointer));
492
571
  await Future.delayed(const Duration(milliseconds: 100));
493
572
  }
494
573
 
495
- static Future<bool> _performEnterText({String? key, required String text}) async {
574
+ static Future<bool> _performEnterText(
575
+ {String? key, required String text}) async {
496
576
  final element = _findElement(key: key);
497
577
  if (element == null) {
498
578
  _log('TextField not found (key: $key)');
@@ -502,7 +582,8 @@ class FlutterSkillBinding {
502
582
  final renderObject = element.renderObject;
503
583
  if (renderObject is! RenderBox) return false;
504
584
 
505
- final center = renderObject.localToGlobal(renderObject.size.center(Offset.zero));
585
+ final center =
586
+ renderObject.localToGlobal(renderObject.size.center(Offset.zero));
506
587
  await _dispatchTap(center);
507
588
  await Future.delayed(const Duration(milliseconds: 200));
508
589
 
@@ -515,6 +596,7 @@ class FlutterSkillBinding {
515
596
  }
516
597
  e.visitChildren(findEditable);
517
598
  }
599
+
518
600
  findEditable(element);
519
601
 
520
602
  if (editableTextState != null) {
@@ -545,7 +627,8 @@ class FlutterSkillBinding {
545
627
  }
546
628
 
547
629
  try {
548
- await Scrollable.ensureVisible(element, duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
630
+ await Scrollable.ensureVisible(element,
631
+ duration: const Duration(milliseconds: 300), curve: Curves.easeInOut);
549
632
  _log('Scrolled to element (key: $key, text: $text)');
550
633
  return true;
551
634
  } catch (e) {
@@ -554,27 +637,32 @@ class FlutterSkillBinding {
554
637
  }
555
638
  }
556
639
 
557
- static Future<bool> _performLongPress({String? key, String? text, int duration = 500}) async {
640
+ static Future<bool> _performLongPress(
641
+ {String? key, String? text, int duration = 500}) async {
558
642
  final element = _findElement(key: key, text: text);
559
643
  if (element == null) return false;
560
644
 
561
645
  final renderObject = element.renderObject;
562
646
  if (renderObject is! RenderBox || !renderObject.hasSize) return false;
563
647
 
564
- final center = renderObject.localToGlobal(renderObject.size.center(Offset.zero));
648
+ final center =
649
+ renderObject.localToGlobal(renderObject.size.center(Offset.zero));
565
650
  final binding = WidgetsBinding.instance;
566
651
  final pointer = _pointerCounter++;
567
652
 
568
- binding.handlePointerEvent(PointerDownEvent(position: center, pointer: pointer));
653
+ binding.handlePointerEvent(
654
+ PointerDownEvent(position: center, pointer: pointer));
569
655
  await Future.delayed(Duration(milliseconds: duration));
570
- binding.handlePointerEvent(PointerUpEvent(position: center, pointer: pointer));
656
+ binding
657
+ .handlePointerEvent(PointerUpEvent(position: center, pointer: pointer));
571
658
  await Future.delayed(const Duration(milliseconds: 100));
572
659
 
573
660
  _log('Long press completed (key: $key, text: $text)');
574
661
  return true;
575
662
  }
576
663
 
577
- static Future<bool> _performSwipe({required String direction, double distance = 300, String? key}) async {
664
+ static Future<bool> _performSwipe(
665
+ {required String direction, double distance = 300, String? key}) async {
578
666
  final binding = WidgetsBinding.instance;
579
667
  Offset start;
580
668
 
@@ -612,13 +700,17 @@ class FlutterSkillBinding {
612
700
  final pointer = _pointerCounter++;
613
701
  final end = start + delta;
614
702
 
615
- binding.handlePointerEvent(PointerDownEvent(position: start, pointer: pointer));
703
+ binding.handlePointerEvent(
704
+ PointerDownEvent(position: start, pointer: pointer));
616
705
  await Future.delayed(const Duration(milliseconds: 16));
617
706
 
618
707
  const steps = 10;
619
708
  for (int i = 1; i <= steps; i++) {
620
709
  final current = Offset.lerp(start, end, i / steps)!;
621
- binding.handlePointerEvent(PointerMoveEvent(position: current, pointer: pointer, delta: delta / steps.toDouble()));
710
+ binding.handlePointerEvent(PointerMoveEvent(
711
+ position: current,
712
+ pointer: pointer,
713
+ delta: delta / steps.toDouble()));
622
714
  await Future.delayed(const Duration(milliseconds: 16));
623
715
  }
624
716
 
@@ -646,13 +738,15 @@ class FlutterSkillBinding {
646
738
  final binding = WidgetsBinding.instance;
647
739
  final pointer = _pointerCounter++;
648
740
 
649
- binding.handlePointerEvent(PointerDownEvent(position: start, pointer: pointer));
741
+ binding.handlePointerEvent(
742
+ PointerDownEvent(position: start, pointer: pointer));
650
743
  await Future.delayed(const Duration(milliseconds: 100));
651
744
 
652
745
  const steps = 20;
653
746
  for (int i = 1; i <= steps; i++) {
654
747
  final current = Offset.lerp(start, end, i / steps)!;
655
- binding.handlePointerEvent(PointerMoveEvent(position: current, pointer: pointer));
748
+ binding.handlePointerEvent(
749
+ PointerMoveEvent(position: current, pointer: pointer));
656
750
  await Future.delayed(const Duration(milliseconds: 16));
657
751
  }
658
752
 
@@ -670,7 +764,8 @@ class FlutterSkillBinding {
670
764
  final renderObject = element.renderObject;
671
765
  if (renderObject is! RenderBox || !renderObject.hasSize) return false;
672
766
 
673
- final center = renderObject.localToGlobal(renderObject.size.center(Offset.zero));
767
+ final center =
768
+ renderObject.localToGlobal(renderObject.size.center(Offset.zero));
674
769
 
675
770
  await _dispatchTap(center);
676
771
  await Future.delayed(const Duration(milliseconds: 50));
@@ -695,8 +790,11 @@ class FlutterSkillBinding {
695
790
  key = (widget.key as ValueKey<String>).value;
696
791
  }
697
792
 
698
- if (widget is ElevatedButton || widget is TextButton || widget is OutlinedButton ||
699
- widget is IconButton || widget is FloatingActionButton) {
793
+ if (widget is ElevatedButton ||
794
+ widget is TextButton ||
795
+ widget is OutlinedButton ||
796
+ widget is IconButton ||
797
+ widget is FloatingActionButton) {
700
798
  type = 'Button';
701
799
  text = _extractTextFrom(element);
702
800
  } else if (widget is TextField || widget is TextFormField) {
@@ -752,7 +850,10 @@ class FlutterSkillBinding {
752
850
 
753
851
  final renderObject = element.renderObject;
754
852
  if (renderObject is RenderBox && renderObject.hasSize) {
755
- node['size'] = {'width': renderObject.size.width, 'height': renderObject.size.height};
853
+ node['size'] = {
854
+ 'width': renderObject.size.width,
855
+ 'height': renderObject.size.height
856
+ };
756
857
  final offset = renderObject.localToGlobal(Offset.zero);
757
858
  node['position'] = {'x': offset.dx, 'y': offset.dy};
758
859
  }
@@ -790,10 +891,15 @@ class FlutterSkillBinding {
790
891
 
791
892
  final renderObject = element.renderObject;
792
893
  if (renderObject is RenderBox && renderObject.hasSize) {
793
- props['size'] = {'width': renderObject.size.width, 'height': renderObject.size.height};
894
+ props['size'] = {
895
+ 'width': renderObject.size.width,
896
+ 'height': renderObject.size.height
897
+ };
794
898
  final offset = renderObject.localToGlobal(Offset.zero);
795
899
  props['position'] = {'x': offset.dx, 'y': offset.dy};
796
- props['visible'] = renderObject.attached && renderObject.size.width > 0 && renderObject.size.height > 0;
900
+ props['visible'] = renderObject.attached &&
901
+ renderObject.size.width > 0 &&
902
+ renderObject.size.height > 0;
797
903
  }
798
904
 
799
905
  if (widget is Text) {
@@ -864,7 +970,10 @@ class FlutterSkillBinding {
864
970
  if (renderObject is RenderBox && renderObject.hasSize) {
865
971
  final offset = renderObject.localToGlobal(Offset.zero);
866
972
  node['position'] = {'x': offset.dx, 'y': offset.dy};
867
- node['size'] = {'width': renderObject.size.width, 'height': renderObject.size.height};
973
+ node['size'] = {
974
+ 'width': renderObject.size.width,
975
+ 'height': renderObject.size.height
976
+ };
868
977
  }
869
978
 
870
979
  results.add(node);
@@ -897,6 +1006,7 @@ class FlutterSkillBinding {
897
1006
  }
898
1007
  e.visitChildren(findEditable);
899
1008
  }
1009
+
900
1010
  findEditable(element);
901
1011
 
902
1012
  return editableTextState?.textEditingValue.text;
@@ -927,7 +1037,8 @@ class FlutterSkillBinding {
927
1037
  return null;
928
1038
  }
929
1039
 
930
- static Future<bool> _waitForElement({String? key, String? text, int timeout = 5000}) async {
1040
+ static Future<bool> _waitForElement(
1041
+ {String? key, String? text, int timeout = 5000}) async {
931
1042
  final endTime = DateTime.now().add(Duration(milliseconds: timeout));
932
1043
 
933
1044
  while (DateTime.now().isBefore(endTime)) {
@@ -939,7 +1050,8 @@ class FlutterSkillBinding {
939
1050
  return false;
940
1051
  }
941
1052
 
942
- static Future<bool> _waitForGone({String? key, String? text, int timeout = 5000}) async {
1053
+ static Future<bool> _waitForGone(
1054
+ {String? key, String? text, int timeout = 5000}) async {
943
1055
  final endTime = DateTime.now().add(Duration(milliseconds: timeout));
944
1056
 
945
1057
  while (DateTime.now().isBefore(endTime)) {
@@ -969,6 +1081,7 @@ class FlutterSkillBinding {
969
1081
  }
970
1082
  obj.visitChildren(findBoundary);
971
1083
  }
1084
+
972
1085
  renderObject?.visitChildren(findBoundary);
973
1086
 
974
1087
  if (boundary == null) {
@@ -1024,7 +1137,8 @@ class FlutterSkillBinding {
1024
1137
  final boundaryBox = current;
1025
1138
 
1026
1139
  // Get element position relative to boundary
1027
- final offset = box.localToGlobal(Offset.zero, ancestor: boundaryBox);
1140
+ final offset =
1141
+ box.localToGlobal(Offset.zero, ancestor: boundaryBox);
1028
1142
  final size = box.size;
1029
1143
 
1030
1144
  // Capture the boundary
@@ -1043,8 +1157,10 @@ class FlutterSkillBinding {
1043
1157
  );
1044
1158
 
1045
1159
  final picture = recorder.endRecording();
1046
- final croppedImage = await picture.toImage(size.width.toInt(), size.height.toInt());
1047
- final byteData = await croppedImage.toByteData(format: ui.ImageByteFormat.png);
1160
+ final croppedImage =
1161
+ await picture.toImage(size.width.toInt(), size.height.toInt());
1162
+ final byteData =
1163
+ await croppedImage.toByteData(format: ui.ImageByteFormat.png);
1048
1164
  if (byteData == null) return null;
1049
1165
  return base64Encode(byteData.buffer.asUint8List());
1050
1166
  }
@@ -19,24 +19,35 @@ Future<void> runServer(List<String> args) async {
19
19
  /// Check pub.dev for newer version
20
20
  Future<void> _checkForUpdates() async {
21
21
  try {
22
- final response = await http.get(
23
- Uri.parse('https://pub.dev/api/packages/flutter_skill'),
24
- ).timeout(const Duration(seconds: 5));
22
+ final response = await http
23
+ .get(
24
+ Uri.parse('https://pub.dev/api/packages/flutter_skill'),
25
+ )
26
+ .timeout(const Duration(seconds: 5));
25
27
 
26
28
  if (response.statusCode == 200) {
27
29
  final data = jsonDecode(response.body);
28
30
  final latestVersion = data['latest']?['version'] as String?;
29
31
 
30
- if (latestVersion != null && _isNewerVersion(latestVersion, _currentVersion)) {
32
+ if (latestVersion != null &&
33
+ _isNewerVersion(latestVersion, _currentVersion)) {
31
34
  stderr.writeln('');
32
- stderr.writeln('╔══════════════════════════════════════════════════════════╗');
33
- stderr.writeln('║ flutter-skill v$latestVersion available (current: v$_currentVersion)');
34
- stderr.writeln('║ ║');
35
- stderr.writeln('║ Update with:');
36
- stderr.writeln('║ dart pub global activate flutter_skill ║');
37
- stderr.writeln('║ Or: ║');
38
- stderr.writeln('║ npm update -g flutter-skill-mcp ║');
39
- stderr.writeln('╚══════════════════════════════════════════════════════════╝');
35
+ stderr.writeln(
36
+ '╔══════════════════════════════════════════════════════════╗');
37
+ stderr.writeln(
38
+ '║ flutter-skill v$latestVersion available (current: v$_currentVersion)');
39
+ stderr.writeln(
40
+ '║ ║');
41
+ stderr.writeln(
42
+ '║ Update with: ║');
43
+ stderr.writeln(
44
+ '║ dart pub global activate flutter_skill ║');
45
+ stderr.writeln(
46
+ '║ Or: ║');
47
+ stderr.writeln(
48
+ '║ npm update -g flutter-skill-mcp ║');
49
+ stderr.writeln(
50
+ '╚══════════════════════════════════════════════════════════╝');
40
51
  stderr.writeln('');
41
52
  }
42
53
  }
@@ -64,7 +75,10 @@ class FlutterMcpServer {
64
75
  Process? _flutterProcess;
65
76
 
66
77
  Future<void> run() async {
67
- stdin.transform(utf8.decoder).transform(const LineSplitter()).listen((line) async {
78
+ stdin
79
+ .transform(utf8.decoder)
80
+ .transform(const LineSplitter())
81
+ .listen((line) async {
68
82
  if (line.trim().isEmpty) return;
69
83
  try {
70
84
  final request = jsonDecode(line);
@@ -87,7 +101,10 @@ class FlutterMcpServer {
87
101
  _sendResult(id, {
88
102
  "capabilities": {"tools": {}, "resources": {}},
89
103
  "protocolVersion": "2024-11-05",
90
- "serverInfo": {"name": "flutter-skill-mcp", "version": _currentVersion},
104
+ "serverInfo": {
105
+ "name": "flutter-skill-mcp",
106
+ "version": _currentVersion
107
+ },
91
108
  });
92
109
  } else if (method == 'notifications/initialized') {
93
110
  // No op
@@ -119,7 +136,10 @@ class FlutterMcpServer {
119
136
  "inputSchema": {
120
137
  "type": "object",
121
138
  "properties": {
122
- "uri": {"type": "string", "description": "WebSocket URI (ws://...)"},
139
+ "uri": {
140
+ "type": "string",
141
+ "description": "WebSocket URI (ws://...)"
142
+ },
123
143
  },
124
144
  "required": ["uri"],
125
145
  },
@@ -130,7 +150,10 @@ class FlutterMcpServer {
130
150
  "inputSchema": {
131
151
  "type": "object",
132
152
  "properties": {
133
- "project_path": {"type": "string", "description": "Path to Flutter project"},
153
+ "project_path": {
154
+ "type": "string",
155
+ "description": "Path to Flutter project"
156
+ },
134
157
  "device_id": {"type": "string", "description": "Target device"},
135
158
  },
136
159
  },
@@ -148,7 +171,10 @@ class FlutterMcpServer {
148
171
  "inputSchema": {
149
172
  "type": "object",
150
173
  "properties": {
151
- "max_depth": {"type": "integer", "description": "Maximum tree depth (default: 10)"},
174
+ "max_depth": {
175
+ "type": "integer",
176
+ "description": "Maximum tree depth (default: 10)"
177
+ },
152
178
  },
153
179
  },
154
180
  },
@@ -174,7 +200,10 @@ class FlutterMcpServer {
174
200
  "inputSchema": {
175
201
  "type": "object",
176
202
  "properties": {
177
- "type": {"type": "string", "description": "Widget type name to search"},
203
+ "type": {
204
+ "type": "string",
205
+ "description": "Widget type name to search"
206
+ },
178
207
  },
179
208
  "required": ["type"],
180
209
  },
@@ -225,7 +254,10 @@ class FlutterMcpServer {
225
254
  "properties": {
226
255
  "key": {"type": "string", "description": "Widget key"},
227
256
  "text": {"type": "string", "description": "Text to find"},
228
- "duration": {"type": "integer", "description": "Duration in ms (default: 500)"},
257
+ "duration": {
258
+ "type": "integer",
259
+ "description": "Duration in ms (default: 500)"
260
+ },
229
261
  },
230
262
  },
231
263
  },
@@ -246,9 +278,18 @@ class FlutterMcpServer {
246
278
  "inputSchema": {
247
279
  "type": "object",
248
280
  "properties": {
249
- "direction": {"type": "string", "enum": ["up", "down", "left", "right"]},
250
- "distance": {"type": "number", "description": "Swipe distance in pixels (default: 300)"},
251
- "key": {"type": "string", "description": "Start from element (optional)"},
281
+ "direction": {
282
+ "type": "string",
283
+ "enum": ["up", "down", "left", "right"]
284
+ },
285
+ "distance": {
286
+ "type": "number",
287
+ "description": "Swipe distance in pixels (default: 300)"
288
+ },
289
+ "key": {
290
+ "type": "string",
291
+ "description": "Start from element (optional)"
292
+ },
252
293
  },
253
294
  "required": ["direction"],
254
295
  },
@@ -308,7 +349,10 @@ class FlutterMcpServer {
308
349
  "properties": {
309
350
  "key": {"type": "string", "description": "Widget key"},
310
351
  "text": {"type": "string", "description": "Text to find"},
311
- "timeout": {"type": "integer", "description": "Timeout in ms (default: 5000)"},
352
+ "timeout": {
353
+ "type": "integer",
354
+ "description": "Timeout in ms (default: 5000)"
355
+ },
312
356
  },
313
357
  },
314
358
  },
@@ -320,7 +364,10 @@ class FlutterMcpServer {
320
364
  "properties": {
321
365
  "key": {"type": "string", "description": "Widget key"},
322
366
  "text": {"type": "string", "description": "Text to find"},
323
- "timeout": {"type": "integer", "description": "Timeout in ms (default: 5000)"},
367
+ "timeout": {
368
+ "type": "integer",
369
+ "description": "Timeout in ms (default: 5000)"
370
+ },
324
371
  },
325
372
  },
326
373
  },
@@ -430,11 +477,15 @@ class FlutterMcpServer {
430
477
  // Continue even if setup fails
431
478
  }
432
479
 
433
- _flutterProcess = await Process.start('flutter', processArgs, workingDirectory: projectPath);
480
+ _flutterProcess = await Process.start('flutter', processArgs,
481
+ workingDirectory: projectPath);
434
482
 
435
483
  final completer = Completer<String>();
436
484
 
437
- _flutterProcess!.stdout.transform(utf8.decoder).transform(const LineSplitter()).listen((line) {
485
+ _flutterProcess!.stdout
486
+ .transform(utf8.decoder)
487
+ .transform(const LineSplitter())
488
+ .listen((line) {
438
489
  if (line.contains('ws://')) {
439
490
  final uriRegex = RegExp(r'ws://[a-zA-Z0-9.:/-]+');
440
491
  final match = uriRegex.firstMatch(line);
@@ -443,9 +494,11 @@ class FlutterMcpServer {
443
494
  _client?.disconnect();
444
495
  _client = FlutterSkillClient(uri);
445
496
  _client!.connect().then((_) {
446
- if (!completer.isCompleted) completer.complete("Launched and connected to $uri");
497
+ if (!completer.isCompleted)
498
+ completer.complete("Launched and connected to $uri");
447
499
  }).catchError((e) {
448
- if (!completer.isCompleted) completer.completeError("Found URI but failed to connect: $e");
500
+ if (!completer.isCompleted)
501
+ completer.completeError("Found URI but failed to connect: $e");
449
502
  });
450
503
  }
451
504
  }
@@ -510,17 +563,21 @@ class FlutterMcpServer {
510
563
  // Advanced Actions
511
564
  case 'long_press':
512
565
  final duration = args['duration'] ?? 500;
513
- final success = await _client!.longPress(key: args['key'], text: args['text'], duration: duration);
566
+ final success = await _client!.longPress(
567
+ key: args['key'], text: args['text'], duration: duration);
514
568
  return success ? "Long pressed" : "Long press failed";
515
569
  case 'double_tap':
516
- final success = await _client!.doubleTap(key: args['key'], text: args['text']);
570
+ final success =
571
+ await _client!.doubleTap(key: args['key'], text: args['text']);
517
572
  return success ? "Double tapped" : "Double tap failed";
518
573
  case 'swipe':
519
574
  final distance = (args['distance'] ?? 300).toDouble();
520
- final success = await _client!.swipe(direction: args['direction'], distance: distance, key: args['key']);
575
+ final success = await _client!.swipe(
576
+ direction: args['direction'], distance: distance, key: args['key']);
521
577
  return success ? "Swiped ${args['direction']}" : "Swipe failed";
522
578
  case 'drag':
523
- final success = await _client!.drag(fromKey: args['from_key'], toKey: args['to_key']);
579
+ final success = await _client!
580
+ .drag(fromKey: args['from_key'], toKey: args['to_key']);
524
581
  return success ? "Dragged" : "Drag failed";
525
582
 
526
583
  // State & Validation
@@ -532,11 +589,13 @@ class FlutterMcpServer {
532
589
  return await _client!.getSliderValue(args['key']);
533
590
  case 'wait_for_element':
534
591
  final timeout = args['timeout'] ?? 5000;
535
- final found = await _client!.waitForElement(key: args['key'], text: args['text'], timeout: timeout);
592
+ final found = await _client!.waitForElement(
593
+ key: args['key'], text: args['text'], timeout: timeout);
536
594
  return {"found": found};
537
595
  case 'wait_for_gone':
538
596
  final timeout = args['timeout'] ?? 5000;
539
- final gone = await _client!.waitForGone(key: args['key'], text: args['text'], timeout: timeout);
597
+ final gone = await _client!.waitForGone(
598
+ key: args['key'], text: args['text'], timeout: timeout);
540
599
  return {"gone": gone};
541
600
 
542
601
  // Screenshot
@@ -574,7 +633,8 @@ class FlutterMcpServer {
574
633
 
575
634
  void _requireConnection() {
576
635
  if (_client == null || !_client!.isConnected) {
577
- throw Exception("Not connected. Call 'connect_app' or 'launch_app' first.");
636
+ throw Exception(
637
+ "Not connected. Call 'connect_app' or 'launch_app' first.");
578
638
  }
579
639
  }
580
640
 
@@ -29,7 +29,8 @@ class FlutterSkillClient {
29
29
  _isolateId = null;
30
30
  }
31
31
 
32
- Future<Map<String, dynamic>> _call(String method, [Map<String, dynamic>? args]) async {
32
+ Future<Map<String, dynamic>> _call(String method,
33
+ [Map<String, dynamic>? args]) async {
33
34
  if (_service == null || _isolateId == null) {
34
35
  throw Exception('Not connected');
35
36
  }
@@ -88,7 +89,8 @@ class FlutterSkillClient {
88
89
  }
89
90
 
90
91
  Future<Map<String, dynamic>?> getWidgetProperties(String key) async {
91
- final result = await _call('ext.flutter.flutter_skill.getWidgetProperties', {
92
+ final result =
93
+ await _call('ext.flutter.flutter_skill.getWidgetProperties', {
92
94
  'key': key,
93
95
  });
94
96
  return result['properties'];
@@ -108,7 +110,8 @@ class FlutterSkillClient {
108
110
 
109
111
  // ==================== MORE INTERACTIONS ====================
110
112
 
111
- Future<bool> longPress({String? key, String? text, int duration = 500}) async {
113
+ Future<bool> longPress(
114
+ {String? key, String? text, int duration = 500}) async {
112
115
  final result = await _call('ext.flutter.flutter_skill.longPress', {
113
116
  if (key != null) 'key': key,
114
117
  if (text != null) 'text': text,
@@ -117,7 +120,8 @@ class FlutterSkillClient {
117
120
  return result['success'] == true;
118
121
  }
119
122
 
120
- Future<bool> swipe({required String direction, double distance = 300, String? key}) async {
123
+ Future<bool> swipe(
124
+ {required String direction, double distance = 300, String? key}) async {
121
125
  final result = await _call('ext.flutter.flutter_skill.swipe', {
122
126
  'direction': direction,
123
127
  'distance': distance.toString(),
@@ -165,7 +169,8 @@ class FlutterSkillClient {
165
169
  return result['value']?.toDouble();
166
170
  }
167
171
 
168
- Future<bool> waitForElement({String? key, String? text, int timeout = 5000}) async {
172
+ Future<bool> waitForElement(
173
+ {String? key, String? text, int timeout = 5000}) async {
169
174
  final result = await _call('ext.flutter.flutter_skill.waitForElement', {
170
175
  if (key != null) 'key': key,
171
176
  if (text != null) 'text': text,
@@ -174,7 +179,8 @@ class FlutterSkillClient {
174
179
  return result['found'] == true;
175
180
  }
176
181
 
177
- Future<bool> waitForGone({String? key, String? text, int timeout = 5000}) async {
182
+ Future<bool> waitForGone(
183
+ {String? key, String? text, int timeout = 5000}) async {
178
184
  final result = await _call('ext.flutter.flutter_skill.waitForGone', {
179
185
  if (key != null) 'key': key,
180
186
  if (text != null) 'text': text,
@@ -252,8 +258,10 @@ class FlutterSkillClient {
252
258
 
253
259
  Future<Map<String, dynamic>> getLayoutTree() async {
254
260
  try {
255
- final groupName = 'flutter_skill_${DateTime.now().millisecondsSinceEpoch}';
256
- final result = await _call('ext.flutter.inspector.getRootWidgetSummaryTree', {
261
+ final groupName =
262
+ 'flutter_skill_${DateTime.now().millisecondsSinceEpoch}';
263
+ final result =
264
+ await _call('ext.flutter.inspector.getRootWidgetSummaryTree', {
257
265
  'objectGroup': groupName,
258
266
  });
259
267
  return result;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flutter-skill-mcp",
3
- "version": "0.2.19",
3
+ "version": "0.2.20",
4
4
  "description": "MCP Server for Flutter app automation - Give your AI Agent eyes and hands inside your Flutter app",
5
5
  "main": "index.js",
6
6
  "bin": {