@bdayadev/flutter-ultra-mcp 1.11.7 → 1.12.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 +1 -1
- package/dart/ultra_flutter_devtools/pubspec.yaml +1 -1
- package/package.json +1 -1
- package/skills/UPSTREAM-LICENSES +75 -0
- package/skills/add-integration-test/SKILL.md +127 -0
- package/skills/add-unit-test/SKILL.md +140 -0
- package/skills/add-widget-preview/SKILL.md +117 -0
- package/skills/add-widget-test/SKILL.md +142 -0
- package/skills/apply-architecture-best-practices/SKILL.md +156 -0
- package/skills/build-cli-app/SKILL.md +173 -0
- package/skills/build-responsive-layout/SKILL.md +126 -0
- package/skills/collect-coverage/SKILL.md +157 -0
- package/skills/fix-layout-issues/SKILL.md +101 -0
- package/skills/fix-runtime-errors/SKILL.md +138 -0
- package/skills/generate-test-mocks/SKILL.md +147 -0
- package/skills/implement-json-serialization/SKILL.md +103 -0
- package/skills/migrate-to-checks-package/SKILL.md +124 -0
- package/skills/resolve-package-conflicts/SKILL.md +111 -0
- package/skills/run-static-analysis/SKILL.md +107 -0
- package/skills/setup-declarative-routing/SKILL.md +157 -0
- package/skills/setup-localization/SKILL.md +177 -0
- package/skills/use-http-package/SKILL.md +151 -0
- package/skills/use-pattern-matching/SKILL.md +132 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fix-layout-issues
|
|
3
|
+
description: Fixes Flutter layout errors (overflows, unbounded constraints) using Dart and Flutter MCP tools. Use when addressing "RenderFlex overflowed", "Vertical viewport was given unbounded height", or similar layout issues.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Resolving Flutter Layout Errors
|
|
7
|
+
|
|
8
|
+
## Contents
|
|
9
|
+
|
|
10
|
+
- [Constraint Violation Diagnostics](#constraint-violation-diagnostics)
|
|
11
|
+
- [Layout Error Resolution Workflow](#layout-error-resolution-workflow)
|
|
12
|
+
- [Examples](#examples)
|
|
13
|
+
|
|
14
|
+
## Constraint Violation Diagnostics
|
|
15
|
+
|
|
16
|
+
Flutter layout operates on: **Constraints go down. Sizes go up. Parent sets position.** Layout errors occur when this negotiation fails.
|
|
17
|
+
|
|
18
|
+
- **"Vertical viewport was given unbounded height"**: Scrollable widget (`ListView`, `GridView`) inside unconstrained vertical parent (`Column`).
|
|
19
|
+
- **"An InputDecorator...cannot have an unbounded width"**: `TextField` inside unconstrained horizontal parent (`Row`).
|
|
20
|
+
- **"RenderFlex overflowed"**: Child of `Row` or `Column` requests more size than available. Yellow and black warning stripes.
|
|
21
|
+
- **"Incorrect use of ParentData widget"**: `ParentDataWidget` not a direct descendant of required ancestor (e.g., `Expanded` outside a `Flex`).
|
|
22
|
+
- **"RenderBox was not laid out"**: Cascading side-effect. Look further up the stack trace for the primary violation.
|
|
23
|
+
|
|
24
|
+
## Layout Error Resolution Workflow
|
|
25
|
+
|
|
26
|
+
### Task Progress
|
|
27
|
+
|
|
28
|
+
- [ ] Run the application in debug mode to capture the exception.
|
|
29
|
+
- [ ] Identify the primary error message (ignore cascading errors).
|
|
30
|
+
- [ ] Apply the conditional fix:
|
|
31
|
+
- **"Unbounded height"**: Wrap scrollable child in `Expanded` or `SizedBox`.
|
|
32
|
+
- **"Unbounded width"**: Wrap `TextField` in `Expanded` or `Flexible`.
|
|
33
|
+
- **"RenderFlex overflowed"**: Wrap overflowing child in `Expanded` or `Flexible`.
|
|
34
|
+
- **"Incorrect ParentData"**: Move the widget to be a direct child of its required parent.
|
|
35
|
+
- [ ] Execute Flutter hot reload.
|
|
36
|
+
- [ ] Verify the error screen or overflow stripes are resolved. Repeat if new errors appear.
|
|
37
|
+
|
|
38
|
+
## Examples
|
|
39
|
+
|
|
40
|
+
### Fixing Unbounded Height (ListView in Column)
|
|
41
|
+
|
|
42
|
+
**Before (Error):**
|
|
43
|
+
|
|
44
|
+
```dart
|
|
45
|
+
Column(
|
|
46
|
+
children: <Widget>[
|
|
47
|
+
const Text('Header'),
|
|
48
|
+
ListView(
|
|
49
|
+
children: const <Widget>[
|
|
50
|
+
ListTile(title: Text('Item 1')),
|
|
51
|
+
ListTile(title: Text('Item 2')),
|
|
52
|
+
],
|
|
53
|
+
),
|
|
54
|
+
],
|
|
55
|
+
)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**After (Fixed):**
|
|
59
|
+
|
|
60
|
+
```dart
|
|
61
|
+
Column(
|
|
62
|
+
children: <Widget>[
|
|
63
|
+
const Text('Header'),
|
|
64
|
+
Expanded(
|
|
65
|
+
child: ListView(
|
|
66
|
+
children: const <Widget>[
|
|
67
|
+
ListTile(title: Text('Item 1')),
|
|
68
|
+
ListTile(title: Text('Item 2')),
|
|
69
|
+
],
|
|
70
|
+
),
|
|
71
|
+
),
|
|
72
|
+
],
|
|
73
|
+
)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Fixing Unbounded Width (TextField in Row)
|
|
77
|
+
|
|
78
|
+
**Before:** `Row(children: [Icon(Icons.search), TextField()])`
|
|
79
|
+
|
|
80
|
+
**After:** `Row(children: [Icon(Icons.search), Expanded(child: TextField())])`
|
|
81
|
+
|
|
82
|
+
### Fixing RenderFlex Overflow
|
|
83
|
+
|
|
84
|
+
**Before:** `Row(children: [Icon(Icons.info), Text('Very long text...')])`
|
|
85
|
+
|
|
86
|
+
**After:** `Row(children: [Icon(Icons.info), Expanded(child: Text('Very long text...'))])`
|
|
87
|
+
|
|
88
|
+
## Flutter Ultra Integration
|
|
89
|
+
|
|
90
|
+
Diagnose and verify layout fixes with live app inspection:
|
|
91
|
+
|
|
92
|
+
- `mcp__plugin_flutter_flutter-ultra-runtime__get_runtime_errors` — Fetch current runtime errors including RenderFlex overflows
|
|
93
|
+
- `mcp__plugin_flutter_flutter-ultra-runtime__dump_render_tree` — Inspect render tree to see constraint propagation
|
|
94
|
+
- `mcp__plugin_flutter_flutter-ultra-runtime__toggle_debug_paint` — Toggle debug paint to visualize layout boundaries
|
|
95
|
+
- `mcp__plugin_flutter_flutter-ultra-runtime__hot_reload` — Hot reload after applying fix to verify immediately
|
|
96
|
+
- `mcp__plugin_flutter_flutter-ultra-runtime__screenshot` — Capture before/after screenshots of the fix
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
> **Attribution:** This skill is vendored from [flutter/skills](https://github.com/flutter/skills) (BSD-3-Clause).
|
|
101
|
+
> Synced by `scripts/sync-upstream-skills.mjs`. Do not edit manually — changes will be overwritten on next sync.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fix-runtime-errors
|
|
3
|
+
description: Uses get_runtime_errors and lsp to fetch an active stack trace, locate the failing line, apply a fix, and verify resolution via hot_reload.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Resolving Dart Static Analysis Errors
|
|
7
|
+
|
|
8
|
+
## Contents
|
|
9
|
+
|
|
10
|
+
- [Core Concepts & Guidelines](#core-concepts--guidelines)
|
|
11
|
+
- [Workflows](#workflows)
|
|
12
|
+
- [Examples](#examples)
|
|
13
|
+
|
|
14
|
+
## Core Concepts & Guidelines
|
|
15
|
+
|
|
16
|
+
### Type System & Soundness
|
|
17
|
+
|
|
18
|
+
Enforce Dart's sound type system to prevent runtime invalid states.
|
|
19
|
+
|
|
20
|
+
- **Method Overrides:** Maintain sound return types (covariant) and parameter types (contravariant). Never tighten a parameter type in a subclass unless explicitly marked with the `covariant` keyword.
|
|
21
|
+
- **Generics & Collections:** Add explicit type annotations to generic classes (e.g., `List<T>`, `Map<K, V>`). Never assign a `List<dynamic>` to a typed list.
|
|
22
|
+
- **Downcasting:** Avoid implicit downcasts from `dynamic`. Use explicit casts (e.g., `as List<Cat>`) when necessary, but ensure the underlying runtime type matches to prevent `TypeError` exceptions.
|
|
23
|
+
- **Strict Casts:** Enable `strict-casts: true` in `analysis_options.yaml` under `analyzer: language:` to force explicit casting.
|
|
24
|
+
|
|
25
|
+
### Null Safety
|
|
26
|
+
|
|
27
|
+
Eliminate static errors related to null safety by correctly managing variable initialization and nullability.
|
|
28
|
+
|
|
29
|
+
- **Modifiers:** Apply `?` for nullable types, `!` for null assertions, and `required` for named parameters that cannot be null.
|
|
30
|
+
- **Late Initialization:** Use the `late` keyword for non-nullable variables guaranteed to be initialized before use.
|
|
31
|
+
- **Wildcards:** Use the `_` wildcard variable (Dart 3.7+) for non-binding local variables or parameters.
|
|
32
|
+
|
|
33
|
+
### Error Handling
|
|
34
|
+
|
|
35
|
+
Distinguish between recoverable exceptions and unrecoverable errors.
|
|
36
|
+
|
|
37
|
+
- **Catching:** Catch `Exception` subtypes for recoverable failures.
|
|
38
|
+
- **Errors:** Never explicitly catch `Error` or its subtypes (e.g., `TypeError`, `ArgumentError`). Errors indicate programming bugs that must be fixed, not caught.
|
|
39
|
+
- **Rethrowing:** Use `rethrow` inside a `catch` block to propagate an exception while preserving its original stack trace.
|
|
40
|
+
|
|
41
|
+
## Workflows
|
|
42
|
+
|
|
43
|
+
### Workflow: Static Analysis Resolution
|
|
44
|
+
|
|
45
|
+
**Task Progress:**
|
|
46
|
+
|
|
47
|
+
- [ ] 1. Run static analyzer.
|
|
48
|
+
- [ ] 2. Apply automated fixes.
|
|
49
|
+
- [ ] 3. Resolve remaining errors manually.
|
|
50
|
+
- [ ] 4. Verify fixes (Feedback Loop).
|
|
51
|
+
|
|
52
|
+
**1. Run static analyzer**
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
dart analyze . --fatal-infos
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**2. Apply automated fixes**
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
dart fix --dry-run
|
|
62
|
+
dart fix --apply
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**3. Resolve remaining errors manually**
|
|
66
|
+
|
|
67
|
+
- **If Null Safety issue:** Verify if the variable can logically be null. Use `?.` or `??` if yes, `late` if initialization is guaranteed elsewhere.
|
|
68
|
+
- **If Type Mismatch:** Add explicit generic type annotations to the instantiation.
|
|
69
|
+
- **If Invalid Override:** Widen the parameter type or add `covariant`.
|
|
70
|
+
|
|
71
|
+
**4. Verify fixes (Feedback Loop)**
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
dart analyze .
|
|
75
|
+
dart test
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Examples
|
|
79
|
+
|
|
80
|
+
### Fixing Dynamic List Assignments
|
|
81
|
+
|
|
82
|
+
**Input (Fails):**
|
|
83
|
+
|
|
84
|
+
```dart
|
|
85
|
+
void printInts(List<int> a) => print(a);
|
|
86
|
+
|
|
87
|
+
void main() {
|
|
88
|
+
final list = []; // Inferred as List<dynamic>
|
|
89
|
+
list.add(1);
|
|
90
|
+
printInts(list); // Error
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
**Output (Passes):**
|
|
95
|
+
|
|
96
|
+
```dart
|
|
97
|
+
void printInts(List<int> a) => print(a);
|
|
98
|
+
|
|
99
|
+
void main() {
|
|
100
|
+
final list = <int>[]; // Explicitly typed
|
|
101
|
+
list.add(1);
|
|
102
|
+
printInts(list);
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Fixing Null Safety with `late`
|
|
107
|
+
|
|
108
|
+
**Input (Fails):**
|
|
109
|
+
|
|
110
|
+
```dart
|
|
111
|
+
class Thermometer {
|
|
112
|
+
String temperature; // Error: Non-nullable must be initialized
|
|
113
|
+
void read() { temperature = '20C'; }
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**Output (Passes):**
|
|
118
|
+
|
|
119
|
+
```dart
|
|
120
|
+
class Thermometer {
|
|
121
|
+
late String temperature;
|
|
122
|
+
void read() { temperature = '20C'; }
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Flutter Ultra Integration
|
|
127
|
+
|
|
128
|
+
Diagnose and fix runtime errors in the live app:
|
|
129
|
+
|
|
130
|
+
- `mcp__plugin_flutter_flutter-ultra-runtime__get_runtime_errors` — Fetch active stack traces from the running app
|
|
131
|
+
- `mcp__plugin_flutter_flutter-ultra-runtime__evaluate` — Evaluate expressions in the running isolate
|
|
132
|
+
- `mcp__plugin_flutter_flutter-ultra-runtime__hot_reload` — Hot reload after applying fix to verify immediately
|
|
133
|
+
- `mcp__plugin_flutter_flutter-ultra-runtime__get_logs` — View recent log output for context
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
> **Attribution:** This skill is vendored from [dart-lang/skills](https://github.com/dart-lang/skills) (BSD-3-Clause).
|
|
138
|
+
> Synced by `scripts/sync-upstream-skills.mjs`. Do not edit manually — changes will be overwritten on next sync.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: generate-test-mocks
|
|
3
|
+
description: Define and generate mock objects for external dependencies using `package:mockito` and `build_runner`. Use when unit testing classes that depend on complex external services like APIs or databases.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Testing and Mocking Dart Applications
|
|
7
|
+
|
|
8
|
+
## Contents
|
|
9
|
+
|
|
10
|
+
- [Structuring Code for Testability](#structuring-code-for-testability)
|
|
11
|
+
- [Managing Dependencies](#managing-dependencies)
|
|
12
|
+
- [Generating Mocks](#generating-mocks)
|
|
13
|
+
- [Implementing Unit Tests](#implementing-unit-tests)
|
|
14
|
+
- [Workflow: Creating and Running Mocked Tests](#workflow-creating-and-running-mocked-tests)
|
|
15
|
+
- [Examples](#examples)
|
|
16
|
+
|
|
17
|
+
## Structuring Code for Testability
|
|
18
|
+
|
|
19
|
+
Design Dart classes to support dependency injection. Isolate complex external dependencies so they can be replaced with mock objects during testing.
|
|
20
|
+
|
|
21
|
+
- Inject external services (e.g., `http.Client`) through class constructors.
|
|
22
|
+
- Represent URLs strictly as `Uri` objects using `Uri.parse(string)`.
|
|
23
|
+
|
|
24
|
+
## Managing Dependencies
|
|
25
|
+
|
|
26
|
+
Configure the `pubspec.yaml` file with the necessary testing and code generation packages.
|
|
27
|
+
|
|
28
|
+
- Add runtime dependencies (e.g., `package:http`) using `dart pub add http`.
|
|
29
|
+
- Add testing dependencies using `dart pub add dev:test dev:mockito dev:build_runner`.
|
|
30
|
+
- Import HTTP libraries with a prefix: `import 'package:http/http.dart' as http;`.
|
|
31
|
+
|
|
32
|
+
## Generating Mocks
|
|
33
|
+
|
|
34
|
+
Use `package:mockito` and `build_runner` to automatically generate mock classes.
|
|
35
|
+
|
|
36
|
+
- Always use the `@GenerateNiceMocks` annotation (preferable to `@GenerateMocks`).
|
|
37
|
+
- Place the annotation in the test file, passing a list of `MockSpec<Type>()` objects.
|
|
38
|
+
- Import the generated file using the `.mocks.dart` extension.
|
|
39
|
+
- Execute `build_runner` to generate the mock files: `dart run build_runner build`.
|
|
40
|
+
|
|
41
|
+
## Implementing Unit Tests
|
|
42
|
+
|
|
43
|
+
Isolate the system under test using the generated mock objects.
|
|
44
|
+
|
|
45
|
+
- **Stubbing:** Use `when(mock.method()).thenReturn(value)` for synchronous methods.
|
|
46
|
+
- **CRITICAL:** Always use `thenAnswer((_) async => value)` for methods returning a `Future` or `Stream`. Never use `thenReturn` for asynchronous returns.
|
|
47
|
+
- **Verification:** Use `verify(mock.method()).called(1)` to check exact invocation counts.
|
|
48
|
+
|
|
49
|
+
## Workflow: Creating and Running Mocked Tests
|
|
50
|
+
|
|
51
|
+
### Task Progress
|
|
52
|
+
|
|
53
|
+
- [ ] 1. Identify the external dependency to mock.
|
|
54
|
+
- [ ] 2. Inject the dependency into the target class constructor.
|
|
55
|
+
- [ ] 3. Create a test file and add `@GenerateNiceMocks([MockSpec<Dependency>()])`.
|
|
56
|
+
- [ ] 4. Add the import directive for the generated `.mocks.dart` file.
|
|
57
|
+
- [ ] 5. Run `dart run build_runner build` to generate the mock classes.
|
|
58
|
+
- [ ] 6. Write the test cases using `group()` and `test()`.
|
|
59
|
+
- [ ] 7. Stub required behaviors using `when()`.
|
|
60
|
+
- [ ] 8. Execute the target method.
|
|
61
|
+
- [ ] 9. Verify interactions using `verify()` and assert outcomes using `expect()`.
|
|
62
|
+
- [ ] 10. Run the test suite using `dart test`.
|
|
63
|
+
|
|
64
|
+
## Examples
|
|
65
|
+
|
|
66
|
+
### High-Fidelity Mocking and Testing Example
|
|
67
|
+
|
|
68
|
+
**System Under Test (`lib/api_service.dart`):**
|
|
69
|
+
|
|
70
|
+
```dart
|
|
71
|
+
import 'dart:convert';
|
|
72
|
+
import 'package:http/http.dart' as http;
|
|
73
|
+
|
|
74
|
+
class ApiService {
|
|
75
|
+
final http.Client client;
|
|
76
|
+
ApiService(this.client);
|
|
77
|
+
|
|
78
|
+
Future<String> fetchData(String urlString) async {
|
|
79
|
+
final uri = Uri.parse(urlString);
|
|
80
|
+
final response = await client.get(uri);
|
|
81
|
+
if (response.statusCode == 200) {
|
|
82
|
+
return jsonDecode(response.body)['data'];
|
|
83
|
+
} else {
|
|
84
|
+
throw Exception('Failed to load data');
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
**Test Implementation (`test/api_service_test.dart`):**
|
|
91
|
+
|
|
92
|
+
```dart
|
|
93
|
+
import 'package:test/test.dart';
|
|
94
|
+
import 'package:mockito/annotations.dart';
|
|
95
|
+
import 'package:mockito/mockito.dart';
|
|
96
|
+
import 'package:http/http.dart' as http;
|
|
97
|
+
import 'package:my_app/api_service.dart';
|
|
98
|
+
|
|
99
|
+
@GenerateNiceMocks([MockSpec<http.Client>()])
|
|
100
|
+
import 'api_service_test.mocks.dart';
|
|
101
|
+
|
|
102
|
+
void main() {
|
|
103
|
+
group('ApiService', () {
|
|
104
|
+
late ApiService apiService;
|
|
105
|
+
late MockClient mockHttpClient;
|
|
106
|
+
|
|
107
|
+
setUp(() {
|
|
108
|
+
mockHttpClient = MockClient();
|
|
109
|
+
apiService = ApiService(mockHttpClient);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('returns data if the http call completes successfully', () async {
|
|
113
|
+
when(mockHttpClient.get(any)).thenAnswer(
|
|
114
|
+
(_) async => http.Response('{"data": "Success"}', 200),
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
final result = await apiService.fetchData('https://api.example.com/data');
|
|
118
|
+
expect(result, 'Success');
|
|
119
|
+
verify(mockHttpClient.get(Uri.parse('https://api.example.com/data'))).called(1);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('throws an exception if the http call completes with an error', () {
|
|
123
|
+
when(mockHttpClient.get(any)).thenAnswer(
|
|
124
|
+
(_) async => http.Response('Not Found', 404),
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
expect(
|
|
128
|
+
apiService.fetchData('https://api.example.com/data'),
|
|
129
|
+
throwsException,
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Flutter Ultra Integration
|
|
137
|
+
|
|
138
|
+
Run build_runner to generate mock classes:
|
|
139
|
+
|
|
140
|
+
- `mcp__plugin_flutter_flutter-ultra-build__start_build_runner_build` — Run build_runner to generate \*.mocks.dart files
|
|
141
|
+
- `mcp__plugin_flutter_flutter-ultra-build__poll_build_runner_job` — Monitor generation progress
|
|
142
|
+
- `mcp__plugin_flutter_flutter-ultra-build__get_build_runner_result` — Check for generation errors
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
> **Attribution:** This skill is vendored from [dart-lang/skills](https://github.com/dart-lang/skills) (BSD-3-Clause).
|
|
147
|
+
> Synced by `scripts/sync-upstream-skills.mjs`. Do not edit manually — changes will be overwritten on next sync.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: implement-json-serialization
|
|
3
|
+
description: Create model classes with `fromJson` and `toJson` methods using `dart:convert`. Use when manually mapping JSON keys to class properties for simple data structures.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Serializing JSON Manually in Flutter
|
|
7
|
+
|
|
8
|
+
## Contents
|
|
9
|
+
|
|
10
|
+
- [Core Guidelines](#core-guidelines)
|
|
11
|
+
- [Workflow: Implementing a Serializable Model](#workflow-implementing-a-serializable-model)
|
|
12
|
+
- [Workflow: Fetching and Parsing JSON](#workflow-fetching-and-parsing-json)
|
|
13
|
+
- [Examples](#examples)
|
|
14
|
+
|
|
15
|
+
## Core Guidelines
|
|
16
|
+
|
|
17
|
+
- **Import `dart:convert`**: Use `jsonEncode` and `jsonDecode`.
|
|
18
|
+
- **Enforce Type Safety**: Cast `jsonDecode()` result to `Map<String, dynamic>` or `List<dynamic>`.
|
|
19
|
+
- **Encapsulate Logic**: Define `fromJson` factory constructor and `toJson` method in model classes.
|
|
20
|
+
- **Handle Background Parsing**: If parsing takes >16ms, use `compute()` to prevent UI jank.
|
|
21
|
+
- **Throw on Failure**: Throw an exception on non-success HTTP status codes. Do not return `null`.
|
|
22
|
+
|
|
23
|
+
## Workflow: Implementing a Serializable Model
|
|
24
|
+
|
|
25
|
+
**Task Progress:**
|
|
26
|
+
|
|
27
|
+
- [ ] Define the plain model class with `final` properties.
|
|
28
|
+
- [ ] Implement `factory Model.fromJson(Map<String, dynamic> json)`.
|
|
29
|
+
- [ ] Implement `Map<String, dynamic> toJson()`.
|
|
30
|
+
- [ ] Write unit tests for both methods.
|
|
31
|
+
- [ ] Run validator -> review type mismatch errors -> fix casting logic.
|
|
32
|
+
|
|
33
|
+
## Workflow: Fetching and Parsing JSON
|
|
34
|
+
|
|
35
|
+
**Task Progress:**
|
|
36
|
+
|
|
37
|
+
- [ ] Execute the HTTP request.
|
|
38
|
+
- [ ] Validate the response status code.
|
|
39
|
+
- [ ] Determine parsing strategy:
|
|
40
|
+
- **Small payload**: Parse synchronously on the main thread.
|
|
41
|
+
- **Large payload**: Use `compute(parseFunction, response.body)`.
|
|
42
|
+
- [ ] Decode and map the JSON to the model.
|
|
43
|
+
|
|
44
|
+
## Examples
|
|
45
|
+
|
|
46
|
+
### Model Implementation
|
|
47
|
+
|
|
48
|
+
```dart
|
|
49
|
+
import 'dart:convert';
|
|
50
|
+
|
|
51
|
+
class User {
|
|
52
|
+
final int id;
|
|
53
|
+
final String name;
|
|
54
|
+
final String email;
|
|
55
|
+
|
|
56
|
+
const User({required this.id, required this.name, required this.email});
|
|
57
|
+
|
|
58
|
+
factory User.fromJson(Map<String, dynamic> json) {
|
|
59
|
+
return switch (json) {
|
|
60
|
+
{'id': int id, 'name': String name, 'email': String email} =>
|
|
61
|
+
User(id: id, name: name, email: email),
|
|
62
|
+
_ => throw const FormatException('Failed to load User.'),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
Map<String, dynamic> toJson() => {'id': id, 'name': name, 'email': email};
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Background Parsing (Large Payload)
|
|
71
|
+
|
|
72
|
+
```dart
|
|
73
|
+
import 'dart:convert';
|
|
74
|
+
import 'package:flutter/foundation.dart';
|
|
75
|
+
import 'package:http/http.dart' as http;
|
|
76
|
+
|
|
77
|
+
List<User> parseUsers(String responseBody) {
|
|
78
|
+
final parsed = (jsonDecode(responseBody) as List<dynamic>).cast<Map<String, dynamic>>();
|
|
79
|
+
return parsed.map<User>((json) => User.fromJson(json)).toList();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
Future<List<User>> fetchUsers(http.Client client) async {
|
|
83
|
+
final response = await client.get(Uri.parse('https://api.example.com/users'));
|
|
84
|
+
if (response.statusCode == 200) {
|
|
85
|
+
return compute(parseUsers, response.body);
|
|
86
|
+
} else {
|
|
87
|
+
throw Exception('Failed to load users');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Flutter Ultra Integration
|
|
93
|
+
|
|
94
|
+
If using code generation (json_serializable), run the build runner:
|
|
95
|
+
|
|
96
|
+
- `mcp__plugin_flutter_flutter-ultra-build__start_build_runner_build` — Run build_runner to generate serialization code
|
|
97
|
+
- `mcp__plugin_flutter_flutter-ultra-build__poll_build_runner_job` — Monitor code generation progress
|
|
98
|
+
- `mcp__plugin_flutter_flutter-ultra-build__analyze` — Verify generated code has no analysis errors
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
> **Attribution:** This skill is vendored from [flutter/skills](https://github.com/flutter/skills) (BSD-3-Clause).
|
|
103
|
+
> Synced by `scripts/sync-upstream-skills.mjs`. Do not edit manually — changes will be overwritten on next sync.
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: migrate-to-checks-package
|
|
3
|
+
description: Replace the usage of `expect` and similar functions from `package:matcher` to `package:checks` equivalents.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Migrating Dart Tests to Package Checks
|
|
7
|
+
|
|
8
|
+
## Contents
|
|
9
|
+
|
|
10
|
+
- [Dependency Management](#dependency-management)
|
|
11
|
+
- [Syntax Migration Guidelines](#syntax-migration-guidelines)
|
|
12
|
+
- [Migration Workflow](#migration-workflow)
|
|
13
|
+
- [Examples](#examples)
|
|
14
|
+
|
|
15
|
+
## Dependency Management
|
|
16
|
+
|
|
17
|
+
- Add `package:checks` as a `dev_dependency` using `dart pub add dev:checks`.
|
|
18
|
+
- Remove `package:matcher` if it is explicitly listed in the `pubspec.yaml` (note: it is often transitively included by `package:test`, which is fine).
|
|
19
|
+
- Import `package:checks/checks.dart` in all test files undergoing migration.
|
|
20
|
+
|
|
21
|
+
## Syntax Migration Guidelines
|
|
22
|
+
|
|
23
|
+
Transition test assertions from the `package:matcher` syntax to the literate API provided by `package:checks`.
|
|
24
|
+
|
|
25
|
+
- **Basic Equality:** Replace `expect(actual, equals(expected))` with `check(actual).equals(expected)`.
|
|
26
|
+
- **Type Checking:** Replace `expect(actual, isA<Type>())` with `check(actual).isA<Type>()`.
|
|
27
|
+
- **Property Extraction:** Replace `expect(actual.property, expected)` with `check(actual).has((a) => a.property, 'property name').equals(expected)`.
|
|
28
|
+
- **Cascades for Multiple Checks:** Use Dart's cascade operator (`..`) to chain multiple expectations on a single subject.
|
|
29
|
+
- **Asynchronous Expectations:**
|
|
30
|
+
- If checking a `Future`, `await` the `check` call: `await check(someFuture).completes((r) => r.equals(expected));`.
|
|
31
|
+
- If checking a `Stream`, wrap it in a `StreamQueue` for multiple checks.
|
|
32
|
+
|
|
33
|
+
## Migration Workflow
|
|
34
|
+
|
|
35
|
+
- [ ] Add `package:checks` as a dev dependency.
|
|
36
|
+
- [ ] Identify all test files using `package:matcher` (`expect` calls).
|
|
37
|
+
- [ ] Import `package:checks/checks.dart` in target test files.
|
|
38
|
+
- [ ] Rewrite all `expect(...)` statements to `check(...)` statements.
|
|
39
|
+
- [ ] Run static analyzer.
|
|
40
|
+
- [ ] Run tests.
|
|
41
|
+
|
|
42
|
+
### Feedback Loop: Static Analysis
|
|
43
|
+
|
|
44
|
+
1. Run the analyzer on the modified test directories.
|
|
45
|
+
2. Review any static analysis warnings or errors.
|
|
46
|
+
3. Fix the warnings.
|
|
47
|
+
4. Repeat until zero issues.
|
|
48
|
+
|
|
49
|
+
### Feedback Loop: Test Validation
|
|
50
|
+
|
|
51
|
+
1. Run the tests.
|
|
52
|
+
2. If tests fail, review the failure output. `package:checks` provides detailed context.
|
|
53
|
+
3. Adjust the `check()` expectations or the underlying code.
|
|
54
|
+
4. Repeat until all tests pass.
|
|
55
|
+
|
|
56
|
+
## Examples
|
|
57
|
+
|
|
58
|
+
### Basic Assertions
|
|
59
|
+
|
|
60
|
+
**Input (`matcher`):**
|
|
61
|
+
|
|
62
|
+
```dart
|
|
63
|
+
expect(someList.length, 1);
|
|
64
|
+
expect(someString, startsWith('a'));
|
|
65
|
+
expect(someObject, isA<Map>());
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Output (`checks`):**
|
|
69
|
+
|
|
70
|
+
```dart
|
|
71
|
+
check(someList).length.equals(1);
|
|
72
|
+
check(someString).startsWith('a');
|
|
73
|
+
check(someObject).isA<Map>();
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Composed Expectations
|
|
77
|
+
|
|
78
|
+
**Input (`matcher`):**
|
|
79
|
+
|
|
80
|
+
```dart
|
|
81
|
+
expect('foo,bar,baz', allOf([
|
|
82
|
+
contains('foo'),
|
|
83
|
+
isNot(startsWith('bar')),
|
|
84
|
+
endsWith('baz')
|
|
85
|
+
]));
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Output (`checks`):**
|
|
89
|
+
|
|
90
|
+
```dart
|
|
91
|
+
check('foo,bar,baz')
|
|
92
|
+
..contains('foo')
|
|
93
|
+
..not((s) => s.startsWith('bar'))
|
|
94
|
+
..endsWith('baz');
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Asynchronous Futures
|
|
98
|
+
|
|
99
|
+
**Input (`matcher`):**
|
|
100
|
+
|
|
101
|
+
```dart
|
|
102
|
+
expect(Future.value(10), completion(equals(10)));
|
|
103
|
+
expect(Future.error('oh no'), throwsA(equals('oh no')));
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Output (`checks`):**
|
|
107
|
+
|
|
108
|
+
```dart
|
|
109
|
+
await check(Future.value(10)).completes((it) => it.equals(10));
|
|
110
|
+
await check(Future.error('oh no')).throws<String>().equals('oh no');
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Flutter Ultra Integration
|
|
114
|
+
|
|
115
|
+
Validate the migration with static analysis and tests:
|
|
116
|
+
|
|
117
|
+
- `mcp__plugin_flutter_flutter-ultra-build__analyze` — Check for type errors after migration
|
|
118
|
+
- `mcp__plugin_flutter_flutter-ultra-build__fix` — Apply automated fixes for simple migrations
|
|
119
|
+
- `mcp__plugin_flutter_flutter-ultra-build__start_run_unit_tests` — Run tests to verify migration didn't break assertions
|
|
120
|
+
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
> **Attribution:** This skill is vendored from [dart-lang/skills](https://github.com/dart-lang/skills) (BSD-3-Clause).
|
|
124
|
+
> Synced by `scripts/sync-upstream-skills.mjs`. Do not edit manually — changes will be overwritten on next sync.
|