@bdayadev/flutter-ultra-mcp 1.12.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +41 -13
- package/dart/ultra_flutter/pubspec.yaml +1 -1
- package/dart/ultra_flutter_devtools/pubspec.yaml +1 -1
- package/package.json +1 -1
- package/packages/flutter-ultra-native-desktop/package.json +2 -0
- package/packages/flutter-ultra-native-mobile/package.json +2 -0
- package/skills/add-integration-test/SKILL.md +72 -20
- package/skills/add-unit-test/SKILL.md +1 -0
- package/skills/add-widget-preview/SKILL.md +75 -26
- package/skills/add-widget-test/SKILL.md +51 -22
- package/skills/apply-architecture-best-practices/SKILL.md +55 -29
- package/skills/build-cli-app/SKILL.md +36 -9
- package/skills/build-responsive-layout/SKILL.md +65 -31
- package/skills/collect-coverage/SKILL.md +12 -1
- package/skills/debug/SKILL.md +19 -2
- package/skills/drive/SKILL.md +30 -15
- package/skills/fix-layout-issues/SKILL.md +71 -20
- package/skills/fix-runtime-errors/SKILL.md +79 -20
- package/skills/generate-test-mocks/SKILL.md +43 -13
- package/skills/implement-json-serialization/SKILL.md +82 -17
- package/skills/migrate-to-checks-package/SKILL.md +46 -14
- package/skills/record-demo/SKILL.md +20 -0
- package/skills/resolve-package-conflicts/SKILL.md +41 -12
- package/skills/run-static-analysis/SKILL.md +29 -16
- package/skills/setup/SKILL.md +1 -0
- package/skills/setup-declarative-routing/SKILL.md +166 -30
- package/skills/setup-localization/SKILL.md +90 -20
- package/skills/test/SKILL.md +2 -1
- package/skills/tour/SKILL.md +31 -22
- package/skills/use-http-package/SKILL.md +61 -23
- package/skills/use-pattern-matching/SKILL.md +63 -26
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: apply-architecture-best-practices
|
|
3
3
|
description: Architects a Flutter application using the recommended layered approach (UI, Logic, Data). Use when structuring a new project or refactoring for scalability.
|
|
4
|
+
last_modified: Tue, 21 Apr 2026 20:11:20 GMT
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Architecting Flutter Applications
|
|
@@ -14,28 +15,30 @@ description: Architects a Flutter application using the recommended layered appr
|
|
|
14
15
|
|
|
15
16
|
## Architectural Layers
|
|
16
17
|
|
|
17
|
-
Enforce strict Separation of Concerns by dividing the application into distinct layers.
|
|
18
|
+
Enforce strict Separation of Concerns by dividing the application into distinct layers. Never mix UI rendering with business logic or data fetching.
|
|
18
19
|
|
|
19
20
|
### UI Layer (Presentation)
|
|
20
21
|
|
|
21
|
-
Implement the MVVM (Model-View-ViewModel) pattern.
|
|
22
|
+
Implement the MVVM (Model-View-ViewModel) pattern to manage UI state and logic.
|
|
22
23
|
|
|
23
|
-
- **Views:**
|
|
24
|
-
- **ViewModels:** Manage UI state. Extend `ChangeNotifier
|
|
24
|
+
- **Views:** Write reusable, lean widgets. Restrict logic in Views to UI-specific operations (e.g., animations, layout constraints, simple routing). Pass all required data from the ViewModel.
|
|
25
|
+
- **ViewModels:** Manage UI state and handle user interactions. Extend `ChangeNotifier` (or use `Listenable`) to expose state. Expose immutable state snapshots to the View. Inject Repositories into ViewModels via the constructor.
|
|
25
26
|
|
|
26
27
|
### Data Layer
|
|
27
28
|
|
|
28
|
-
Implement the Repository pattern
|
|
29
|
+
Implement the Repository pattern to isolate data access logic and create a single source of truth.
|
|
29
30
|
|
|
30
|
-
- **Services:**
|
|
31
|
-
- **Repositories:** Consume Services. Transform raw models into Domain Models. Handle caching and retry logic.
|
|
31
|
+
- **Services:** Create stateless classes to wrap external APIs (HTTP clients, local databases, platform plugins). Return raw API models or `Result` wrappers.
|
|
32
|
+
- **Repositories:** Consume one or more Services. Transform raw API models into clean Domain Models. Handle caching, offline synchronization, and retry logic. Expose Domain Models to ViewModels.
|
|
32
33
|
|
|
33
34
|
### Logic Layer (Domain - Optional)
|
|
34
35
|
|
|
35
|
-
- **Use Cases:**
|
|
36
|
+
- **Use Cases:** Implement this layer only if the application contains complex business logic that clutters the ViewModel, or if logic must be reused across multiple ViewModels. Extract this logic into dedicated Use Case (interactor) classes that sit between ViewModels and Repositories.
|
|
36
37
|
|
|
37
38
|
## Project Structure
|
|
38
39
|
|
|
40
|
+
Organize the codebase using a hybrid approach: group UI components by feature, and group Data/Domain components by type.
|
|
41
|
+
|
|
39
42
|
```text
|
|
40
43
|
lib/
|
|
41
44
|
├── data/
|
|
@@ -55,37 +58,46 @@ lib/
|
|
|
55
58
|
|
|
56
59
|
## Workflow: Implementing a New Feature
|
|
57
60
|
|
|
61
|
+
Follow this sequential workflow when adding a new feature to the application. Copy the checklist to track progress.
|
|
62
|
+
|
|
58
63
|
### Task Progress
|
|
59
64
|
|
|
60
|
-
- [ ] **Step 1
|
|
61
|
-
- [ ] **Step 2
|
|
62
|
-
- [ ] **Step 3
|
|
63
|
-
- [ ] **Step 4
|
|
64
|
-
-
|
|
65
|
-
-
|
|
66
|
-
- [ ] **Step 5
|
|
67
|
-
- [ ] **Step 6
|
|
68
|
-
- [ ] **Step 7
|
|
69
|
-
- [ ] **Step 8
|
|
65
|
+
- [ ] **Step 1: Define Domain Models.** Create immutable data classes for the feature using `freezed` or `built_value`.
|
|
66
|
+
- [ ] **Step 2: Implement Services.** Create or update Service classes to handle external API communication.
|
|
67
|
+
- [ ] **Step 3: Implement Repositories.** Create the Repository to consume Services and return Domain Models.
|
|
68
|
+
- [ ] **Step 4: Apply Conditional Logic (Domain Layer).**
|
|
69
|
+
- _If the feature requires complex data transformation or cross-repository logic:_ Create a Use Case class.
|
|
70
|
+
- _If the feature is a simple CRUD operation:_ Skip to Step 5.
|
|
71
|
+
- [ ] **Step 5: Implement the ViewModel.** Create the ViewModel extending `ChangeNotifier`. Inject required Repositories/Use Cases. Expose immutable state and command methods.
|
|
72
|
+
- [ ] **Step 6: Implement the View.** Create the UI widget. Use `ListenableBuilder` or `AnimatedBuilder` to listen to ViewModel changes.
|
|
73
|
+
- [ ] **Step 7: Inject Dependencies.** Register the new Service, Repository, and ViewModel in the dependency injection container (e.g., `provider` or `get_it`).
|
|
74
|
+
- [ ] **Step 8: Run Validator.** Execute unit tests for the ViewModel and Repository.
|
|
75
|
+
- _Feedback Loop:_ Run tests -> Review failures -> Fix logic -> Re-run until passing.
|
|
70
76
|
|
|
71
77
|
## Examples
|
|
72
78
|
|
|
73
79
|
### Data Layer: Service and Repository
|
|
74
80
|
|
|
75
81
|
```dart
|
|
82
|
+
// 1. Service (Raw API interaction)
|
|
76
83
|
class ApiClient {
|
|
77
|
-
Future<UserApiModel> fetchUser(String id) async {
|
|
84
|
+
Future<UserApiModel> fetchUser(String id) async {
|
|
85
|
+
// HTTP GET implementation...
|
|
86
|
+
}
|
|
78
87
|
}
|
|
79
88
|
|
|
89
|
+
// 2. Repository (Single source of truth, returns Domain Model)
|
|
80
90
|
class UserRepository {
|
|
81
91
|
UserRepository({required ApiClient apiClient}) : _apiClient = apiClient;
|
|
92
|
+
|
|
82
93
|
final ApiClient _apiClient;
|
|
83
94
|
User? _cachedUser;
|
|
84
95
|
|
|
85
96
|
Future<User> getUser(String id) async {
|
|
86
97
|
if (_cachedUser != null) return _cachedUser!;
|
|
98
|
+
|
|
87
99
|
final apiModel = await _apiClient.fetchUser(id);
|
|
88
|
-
_cachedUser = User(id: apiModel.id, name: apiModel.fullName);
|
|
100
|
+
_cachedUser = User(id: apiModel.id, name: apiModel.fullName); // Transform to Domain Model
|
|
89
101
|
return _cachedUser!;
|
|
90
102
|
}
|
|
91
103
|
}
|
|
@@ -94,19 +106,23 @@ class UserRepository {
|
|
|
94
106
|
### UI Layer: ViewModel and View
|
|
95
107
|
|
|
96
108
|
```dart
|
|
109
|
+
// 3. ViewModel (State management and presentation logic)
|
|
97
110
|
class ProfileViewModel extends ChangeNotifier {
|
|
98
111
|
ProfileViewModel({required UserRepository userRepository})
|
|
99
112
|
: _userRepository = userRepository;
|
|
113
|
+
|
|
100
114
|
final UserRepository _userRepository;
|
|
101
115
|
|
|
102
116
|
User? _user;
|
|
103
117
|
User? get user => _user;
|
|
118
|
+
|
|
104
119
|
bool _isLoading = false;
|
|
105
120
|
bool get isLoading => _isLoading;
|
|
106
121
|
|
|
107
122
|
Future<void> loadProfile(String id) async {
|
|
108
123
|
_isLoading = true;
|
|
109
124
|
notifyListeners();
|
|
125
|
+
|
|
110
126
|
try {
|
|
111
127
|
_user = await _userRepository.getUser(id);
|
|
112
128
|
} finally {
|
|
@@ -116,8 +132,10 @@ class ProfileViewModel extends ChangeNotifier {
|
|
|
116
132
|
}
|
|
117
133
|
}
|
|
118
134
|
|
|
135
|
+
// 4. View (Dumb UI component)
|
|
119
136
|
class ProfileView extends StatelessWidget {
|
|
120
137
|
const ProfileView({super.key, required this.viewModel});
|
|
138
|
+
|
|
121
139
|
final ProfileViewModel viewModel;
|
|
122
140
|
|
|
123
141
|
@override
|
|
@@ -125,16 +143,24 @@ class ProfileView extends StatelessWidget {
|
|
|
125
143
|
return ListenableBuilder(
|
|
126
144
|
listenable: viewModel,
|
|
127
145
|
builder: (context, _) {
|
|
128
|
-
if (viewModel.isLoading)
|
|
146
|
+
if (viewModel.isLoading) {
|
|
147
|
+
return const Center(child: CircularProgressIndicator());
|
|
148
|
+
}
|
|
149
|
+
|
|
129
150
|
final user = viewModel.user;
|
|
130
|
-
if (user == null)
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
151
|
+
if (user == null) {
|
|
152
|
+
return const Center(child: Text('User not found'));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return Column(
|
|
156
|
+
children: [
|
|
157
|
+
Text(user.name),
|
|
158
|
+
ElevatedButton(
|
|
159
|
+
onPressed: () => viewModel.loadProfile(user.id),
|
|
160
|
+
child: const Text('Refresh'),
|
|
161
|
+
),
|
|
162
|
+
],
|
|
163
|
+
);
|
|
138
164
|
},
|
|
139
165
|
);
|
|
140
166
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: build-cli-app
|
|
3
3
|
description: Entrypoint structure, exit codes, cross-platform scripts. Use when building command line utilities, scripts, or applications.
|
|
4
|
+
last_modified: Fri, 04 May 2026 17:41:00 GMT
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Building Dart CLI Applications
|
|
@@ -32,7 +33,7 @@ Import the `args` package to manage command-line arguments, flags, and subcomman
|
|
|
32
33
|
- If building a complex, multi-command CLI (like `git`): Implement `CommandRunner` and extend `Command` for each subcommand.
|
|
33
34
|
- Define global arguments on the `CommandRunner.argParser` and command-specific arguments on the individual `Command.argParser`.
|
|
34
35
|
- Catch `UsageException` to gracefully handle invalid arguments and display the automatically generated help text.
|
|
35
|
-
- **Validate Help Text Accuracy**: Ensure the help text provides all necessary information to run the tool.
|
|
36
|
+
- **Validate Help Text Accuracy**: Ensure the help text provides all necessary information to run the tool. If the help text references a compiled executable name, and the user needs to add it to their PATH to run it that way, provide clear instructions on how to do so in the help text or description.
|
|
36
37
|
|
|
37
38
|
## Execution & Error Handling
|
|
38
39
|
|
|
@@ -42,11 +43,14 @@ Leverage the `io` and `stack_trace` packages to build robust, production-ready C
|
|
|
42
43
|
- Use `sharedStdIn` from the `io` package if multiple asynchronous listeners need sequential access to standard input.
|
|
43
44
|
- Wrap the application execution in `Chain.capture()` from the `stack_trace` package to track asynchronous stack chains.
|
|
44
45
|
- Format output stack traces using `Trace.terse` or `Chain.terse` to strip noisy core library frames and present readable errors to the user.
|
|
45
|
-
- **Do not swallow exceptions** in lower-level logic unless recovery is possible. Let them bubble up.
|
|
46
|
-
- **Fail fast and with non-zero exit codes**: Ensure operation failures result in descriptive error messages to `stderr` and appropriate non-zero exit codes.
|
|
46
|
+
- **Do not swallow exceptions** in lower-level logic or storage classes unless recovery is possible. Let them bubble up or rethrow them so higher-level commands know operations failed.
|
|
47
|
+
- **Fail fast and with non-zero exit codes**: Ensure operation failures result in descriptive error messages to `stderr` and appropriate non-zero exit codes (e.g., using `exit(1)` or triggering a 64 exit code after a caught `UsageException`).
|
|
47
48
|
|
|
48
49
|
## Testing CLI Applications
|
|
49
50
|
|
|
51
|
+
> [!IMPORTANT]
|
|
52
|
+
> **All new commands and significant features must be covered by automated tests.** Manual verification is not sufficient for testing logic. However, manual verification of help text and user experience (UX) is still required to ensure the interface is intuitive and correct.
|
|
53
|
+
|
|
50
54
|
Use `test_process` and `test_descriptor` to write high-fidelity integration tests for your CLI.
|
|
51
55
|
|
|
52
56
|
- Define expected filesystem states using `test_descriptor` (`d.dir`, `d.file`).
|
|
@@ -61,9 +65,25 @@ Use `test_process` and `test_descriptor` to write high-fidelity integration test
|
|
|
61
65
|
Select the appropriate compilation target based on your distribution requirements.
|
|
62
66
|
|
|
63
67
|
- **If testing locally during development:** Use `dart run bin/cli.dart`. This uses the JIT compiler for rapid iteration.
|
|
64
|
-
- **If bundling code assets and dynamic libraries:** Use `dart build cli`.
|
|
65
|
-
- **If distributing a standalone native executable:** Use `dart compile exe bin/cli.dart -o <output_path>`.
|
|
66
|
-
- **If distributing multiple apps with strict disk space limits:** Use `dart compile aot-snapshot bin/cli.dart`.
|
|
68
|
+
- **If bundling code assets and dynamic libraries:** Use `dart build cli`. This runs build hooks and outputs to `build/cli/_/bundle/`.
|
|
69
|
+
- **If distributing a standalone native executable:** Use `dart compile exe bin/cli.dart -o <output_path>`. This bundles the Dart runtime and machine code into a single file.
|
|
70
|
+
- **If distributing multiple apps with strict disk space limits:** Use `dart compile aot-snapshot bin/cli.dart`. Run the resulting `.aot` file using `dartaotruntime`.
|
|
71
|
+
|
|
72
|
+
<details>
|
|
73
|
+
<summary>Cross-Compilation Targets (Linux Only)</summary>
|
|
74
|
+
|
|
75
|
+
Dart supports cross-compiling to Linux from macOS, Windows, or Linux hosts.
|
|
76
|
+
Use the `--target-os` and `--target-arch` flags with `dart compile exe` or `dart compile aot-snapshot`.
|
|
77
|
+
|
|
78
|
+
- `--target-os=linux` (Only Linux is currently supported as a cross-compilation target)
|
|
79
|
+
- `--target-arch=arm64` (64-bit ARM)
|
|
80
|
+
- `--target-arch=x64` (x86-64)
|
|
81
|
+
- `--target-arch=arm` (32-bit ARM)
|
|
82
|
+
- `--target-arch=riscv64` (64-bit RISC-V)
|
|
83
|
+
|
|
84
|
+
Example: `dart compile exe --target-os=linux --target-arch=arm64 bin/cli.dart`
|
|
85
|
+
|
|
86
|
+
</details>
|
|
67
87
|
|
|
68
88
|
## Workflows
|
|
69
89
|
|
|
@@ -74,9 +94,9 @@ Select the appropriate compilation target based on your distribution requirement
|
|
|
74
94
|
- [ ] Register command-specific flags in the constructor using `argParser.addFlag()` or `argParser.addOption()`.
|
|
75
95
|
- [ ] Implement the `run()` method with the core logic.
|
|
76
96
|
- [ ] Register the new command in the `CommandRunner` instance in `bin/cli.dart` using `addCommand()`.
|
|
77
|
-
- [ ] Create tests for the new command in the `test/` directory.
|
|
97
|
+
- [ ] Create tests for the new command in the `test/` directory using `test_process` or standard tests.
|
|
78
98
|
- [ ] Run validator -> Execute `dart run bin/cli.dart help <command_name>` to verify help text generation.
|
|
79
|
-
- [ ] Verify final UX: Compile the application using `dart compile exe` and run the resulting executable.
|
|
99
|
+
- [ ] Verify final UX: Compile the application using `dart compile exe` and run the resulting executable to verify the target user experience (e.g., `./bin/cli <command>`).
|
|
80
100
|
|
|
81
101
|
### Task Progress: Compile and Release Native Executable
|
|
82
102
|
|
|
@@ -84,6 +104,7 @@ Select the appropriate compilation target based on your distribution requirement
|
|
|
84
104
|
- [ ] Run validator -> Execute `dart analyze` to ensure no static analysis errors.
|
|
85
105
|
- [ ] Run validator -> Execute `dart test` to pass all integration tests.
|
|
86
106
|
- [ ] Compile for host OS: `dart compile exe bin/cli.dart -o build/cli-host`
|
|
107
|
+
- [ ] Compile for Linux (if host is macOS/Windows): `dart compile exe --target-os=linux --target-arch=x64 bin/cli.dart -o build/cli-linux-x64`
|
|
87
108
|
|
|
88
109
|
## Examples
|
|
89
110
|
|
|
@@ -121,7 +142,7 @@ void main(List<String> args) {
|
|
|
121
142
|
if (error is UsageException) {
|
|
122
143
|
stderr.writeln(error.message);
|
|
123
144
|
stderr.writeln(error.usage);
|
|
124
|
-
exit(64);
|
|
145
|
+
exit(64); // ExitCode.usage.code
|
|
125
146
|
} else {
|
|
126
147
|
stderr.writeln('Fatal error: $error');
|
|
127
148
|
stderr.writeln(chain.terse);
|
|
@@ -140,18 +161,24 @@ import 'package:test_descriptor/test_descriptor.dart' as d;
|
|
|
140
161
|
|
|
141
162
|
void main() {
|
|
142
163
|
test('CLI formats output correctly and modifies filesystem', () async {
|
|
164
|
+
// 1. Setup mock filesystem
|
|
143
165
|
await d.dir('project', [
|
|
144
166
|
d.file('config.json', '{"key": "value"}')
|
|
145
167
|
]).create();
|
|
146
168
|
|
|
169
|
+
// 2. Spawn the CLI process
|
|
147
170
|
final process = await TestProcess.start(
|
|
148
171
|
'dart',
|
|
149
172
|
['run', 'bin/cli.dart', 'process', '--path', '${d.sandbox}/project']
|
|
150
173
|
);
|
|
151
174
|
|
|
175
|
+
// 3. Validate stdout stream
|
|
152
176
|
await expectLater(process.stdout, emitsThrough('Processing complete.'));
|
|
177
|
+
|
|
178
|
+
// 4. Validate exit code
|
|
153
179
|
await process.shouldExit(0);
|
|
154
180
|
|
|
181
|
+
// 5. Validate filesystem mutations
|
|
155
182
|
await d.dir('project', [
|
|
156
183
|
d.file('config.json', '{"key": "value"}'),
|
|
157
184
|
d.file('output.log', 'Success')
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: build-responsive-layout
|
|
3
3
|
description: Use `LayoutBuilder`, `MediaQuery`, or `Expanded/Flexible` to create a layout that adapts to different screen sizes. Use when you need the UI to look good on both mobile and tablet/desktop form factors.
|
|
4
|
+
last_modified: Tue, 21 Apr 2026 20:17:40 GMT
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Implementing Adaptive Layouts
|
|
@@ -16,49 +17,64 @@ description: Use `LayoutBuilder`, `MediaQuery`, or `Expanded/Flexible` to create
|
|
|
16
17
|
|
|
17
18
|
## Space Measurement Guidelines
|
|
18
19
|
|
|
20
|
+
Determine the available space accurately to ensure layouts adapt to the app window, not just the physical device.
|
|
21
|
+
|
|
19
22
|
- **Use `MediaQuery.sizeOf(context)`** to get the size of the entire app window.
|
|
20
|
-
- **Use `LayoutBuilder`** to make layout decisions based on the parent widget's allocated space.
|
|
21
|
-
- **Do not use `MediaQuery.orientationOf` or `OrientationBuilder`** near the top of the widget tree. Device orientation does not accurately reflect available space.
|
|
22
|
-
- **Do not check for hardware types** ("phone" vs. "tablet"). Base all decisions strictly on available window space.
|
|
23
|
+
- **Use `LayoutBuilder`** to make layout decisions based on the parent widget's allocated space. Evaluate `constraints.maxWidth` to determine the appropriate widget tree to return.
|
|
24
|
+
- **Do not use `MediaQuery.orientationOf` or `OrientationBuilder`** near the top of the widget tree to switch layouts. Device orientation does not accurately reflect the available app window space.
|
|
25
|
+
- **Do not check for hardware types** (e.g., "phone" vs. "tablet"). Flutter apps run in resizable windows, multi-window modes, and picture-in-picture. Base all layout decisions strictly on available window space.
|
|
23
26
|
|
|
24
27
|
## Widget Sizing and Constraints
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
- **
|
|
29
|
-
-
|
|
29
|
+
Understand and apply Flutter's core layout rule: **Constraints go down. Sizes go up. Parent sets position.**
|
|
30
|
+
|
|
31
|
+
- **Distribute Space:** Use `Expanded` and `Flexible` within `Row`, `Column`, or `Flex` widgets.
|
|
32
|
+
- Use `Expanded` to force a child to fill all remaining available space (equivalent to `Flexible` with `fit: FlexFit.tight` and a `flex` factor of 1.0).
|
|
33
|
+
- Use `Flexible` to allow a child to size itself up to a specific limit while still expanding/contracting. Use the `flex` factor to define the ratio of space consumption among siblings.
|
|
34
|
+
- **Constrain Width:** Prevent widgets from consuming all horizontal space on large screens. Wrap widgets like `GridView` or `ListView` in a `ConstrainedBox` or `Container` and define a `maxWidth` in the `BoxConstraints`.
|
|
35
|
+
- **Lazy Rendering:** Always use `ListView.builder` or `GridView.builder` when rendering lists with an unknown or large number of items.
|
|
30
36
|
|
|
31
37
|
## Device and Orientation Behaviors
|
|
32
38
|
|
|
33
|
-
|
|
34
|
-
|
|
39
|
+
Ensure the app behaves correctly across all device form factors and input methods.
|
|
40
|
+
|
|
41
|
+
- **Do not lock screen orientation.** Locking orientation causes severe layout issues on foldable devices, often resulting in letterboxing (the app centered with black borders). Android large format tiers require both portrait and landscape support.
|
|
42
|
+
- **Fallback for Locked Orientation:** If business requirements strictly mandate a locked orientation, use the `Display API` to retrieve physical screen dimensions instead of `MediaQuery`. `MediaQuery` fails to receive the larger window size in compatibility modes.
|
|
43
|
+
- **Support Multiple Inputs:** Implement support for basic mice, trackpads, and keyboard shortcuts. Ensure touch targets are appropriately sized and keyboard navigation is accessible.
|
|
35
44
|
|
|
36
45
|
## Workflow: Constructing an Adaptive Layout
|
|
37
46
|
|
|
47
|
+
Follow this workflow to implement a layout that adapts to the available `BoxConstraints`.
|
|
48
|
+
|
|
38
49
|
**Task Progress:**
|
|
39
50
|
|
|
40
|
-
- [ ] Identify the target widget.
|
|
41
|
-
- [ ] Wrap in a `LayoutBuilder`.
|
|
42
|
-
- [ ] Extract `constraints.maxWidth
|
|
43
|
-
- [ ] Define breakpoint (e.g., `600`).
|
|
44
|
-
- [ ] If `maxWidth >
|
|
45
|
-
- [ ] If `maxWidth <=
|
|
46
|
-
- [ ]
|
|
51
|
+
- [ ] Identify the target widget that requires adaptive behavior.
|
|
52
|
+
- [ ] Wrap the widget tree in a `LayoutBuilder`.
|
|
53
|
+
- [ ] Extract the `constraints.maxWidth` from the builder callback.
|
|
54
|
+
- [ ] Define an adaptive breakpoint (e.g., `largeScreenMinWidth = 600`).
|
|
55
|
+
- [ ] **If `maxWidth > largeScreenMinWidth`:** Return a large-screen layout (e.g., a `Row` placing a navigation sidebar and content area side-by-side).
|
|
56
|
+
- [ ] **If `maxWidth <= largeScreenMinWidth`:** Return a small-screen layout (e.g., a `Column` or standard navigation-style approach).
|
|
57
|
+
- [ ] Run validator -> resize the application window -> review layout transitions -> fix overflow errors.
|
|
47
58
|
|
|
48
59
|
## Workflow: Optimizing for Large Screens
|
|
49
60
|
|
|
61
|
+
Follow this workflow to prevent UI elements from stretching unnaturally on large displays.
|
|
62
|
+
|
|
50
63
|
**Task Progress:**
|
|
51
64
|
|
|
52
|
-
- [ ] Identify full-width components.
|
|
53
|
-
- [ ] **
|
|
54
|
-
- [ ] **
|
|
55
|
-
- [ ]
|
|
56
|
-
- [ ]
|
|
65
|
+
- [ ] Identify full-width components (e.g., `ListView`, text blocks, forms).
|
|
66
|
+
- [ ] **If optimizing a list:** Convert `ListView.builder` to `GridView.builder` using `SliverGridDelegateWithMaxCrossAxisExtent` to automatically adjust column counts based on window size.
|
|
67
|
+
- [ ] **If optimizing a form or text block:** Wrap the component in a `ConstrainedBox`.
|
|
68
|
+
- [ ] Apply `BoxConstraints(maxWidth: [optimal_width])` to the `ConstrainedBox`.
|
|
69
|
+
- [ ] Wrap the `ConstrainedBox` in a `Center` widget to keep the constrained content centered on large screens.
|
|
70
|
+
- [ ] Run validator -> test on desktop/tablet target -> review horizontal stretching -> adjust `maxWidth` or grid extents.
|
|
57
71
|
|
|
58
72
|
## Examples
|
|
59
73
|
|
|
60
74
|
### Adaptive Layout using LayoutBuilder
|
|
61
75
|
|
|
76
|
+
Demonstrates switching between a mobile and desktop layout based on available width.
|
|
77
|
+
|
|
62
78
|
```dart
|
|
63
79
|
import 'package:flutter/material.dart';
|
|
64
80
|
|
|
@@ -72,25 +88,37 @@ class AdaptiveLayout extends StatelessWidget {
|
|
|
72
88
|
return LayoutBuilder(
|
|
73
89
|
builder: (context, constraints) {
|
|
74
90
|
if (constraints.maxWidth > largeScreenMinWidth) {
|
|
75
|
-
return
|
|
76
|
-
children: [
|
|
77
|
-
const SizedBox(width: 250, child: Placeholder(color: Colors.blue)),
|
|
78
|
-
const VerticalDivider(width: 1),
|
|
79
|
-
Expanded(child: const Placeholder(color: Colors.green)),
|
|
80
|
-
],
|
|
81
|
-
);
|
|
91
|
+
return _buildLargeScreenLayout();
|
|
82
92
|
} else {
|
|
83
|
-
return
|
|
93
|
+
return _buildSmallScreenLayout();
|
|
84
94
|
}
|
|
85
95
|
},
|
|
86
96
|
);
|
|
87
97
|
}
|
|
98
|
+
|
|
99
|
+
Widget _buildLargeScreenLayout() {
|
|
100
|
+
return Row(
|
|
101
|
+
children: [
|
|
102
|
+
const SizedBox(width: 250, child: Placeholder(color: Colors.blue)),
|
|
103
|
+
const VerticalDivider(width: 1),
|
|
104
|
+
Expanded(child: const Placeholder(color: Colors.green)),
|
|
105
|
+
],
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
Widget _buildSmallScreenLayout() {
|
|
110
|
+
return const Placeholder(color: Colors.green);
|
|
111
|
+
}
|
|
88
112
|
}
|
|
89
113
|
```
|
|
90
114
|
|
|
91
115
|
### Constraining Width on Large Screens
|
|
92
116
|
|
|
117
|
+
Demonstrates preventing a widget from consuming all horizontal space.
|
|
118
|
+
|
|
93
119
|
```dart
|
|
120
|
+
import 'package:flutter/material.dart';
|
|
121
|
+
|
|
94
122
|
class ConstrainedContent extends StatelessWidget {
|
|
95
123
|
const ConstrainedContent({super.key});
|
|
96
124
|
|
|
@@ -99,10 +127,16 @@ class ConstrainedContent extends StatelessWidget {
|
|
|
99
127
|
return Scaffold(
|
|
100
128
|
body: Center(
|
|
101
129
|
child: ConstrainedBox(
|
|
102
|
-
constraints: const BoxConstraints(
|
|
130
|
+
constraints: const BoxConstraints(
|
|
131
|
+
maxWidth: 800.0, // Maximum width for readability
|
|
132
|
+
),
|
|
103
133
|
child: ListView.builder(
|
|
104
134
|
itemCount: 50,
|
|
105
|
-
itemBuilder: (context, index)
|
|
135
|
+
itemBuilder: (context, index) {
|
|
136
|
+
return ListTile(
|
|
137
|
+
title: Text('Item $index'),
|
|
138
|
+
);
|
|
139
|
+
},
|
|
106
140
|
),
|
|
107
141
|
),
|
|
108
142
|
),
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: collect-coverage
|
|
3
|
-
description: Collect coverage using the coverage
|
|
3
|
+
description: Collect coverage using the coverage packge and create an LCOV report
|
|
4
|
+
last_modified: Fri, 24 Apr 2026 15:14:32 GMT
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Implementing Dart and Flutter Test Coverage
|
|
@@ -63,8 +64,12 @@ Use the bundled `test_with_coverage` script. This script automatically runs all
|
|
|
63
64
|
dart run coverage:test_with_coverage
|
|
64
65
|
```
|
|
65
66
|
|
|
67
|
+
_Note: If working within a Dart workspace (monorepo), specify the test directories explicitly (e.g., `dart run coverage:test_with_coverage -- pkgs/foo/test pkgs/bar/test`)._
|
|
68
|
+
|
|
66
69
|
### 3. Feedback Loop: Validate Output
|
|
67
70
|
|
|
71
|
+
**Run validator -> review errors -> fix:**
|
|
72
|
+
|
|
68
73
|
1. Verify that the `coverage/` directory was created in the project root.
|
|
69
74
|
2. Ensure `coverage/coverage.json` (raw data) and `coverage/lcov.info` (formatted report) exist.
|
|
70
75
|
3. If coverage is missing for specific files, ensure they are imported and executed by your test files, or add `// coverage:ignore-file` if they are intentionally excluded.
|
|
@@ -95,6 +100,8 @@ Extract the coverage data from the running VM service and output it to a JSON fi
|
|
|
95
100
|
dart run coverage:collect_coverage --wait-paused --uri=http://127.0.0.1:8181/ -o coverage/coverage.json --resume-isolates
|
|
96
101
|
```
|
|
97
102
|
|
|
103
|
+
_Optional: Append `--function-coverage` and `--branch-coverage` to gather deeper metrics (requires Dart VM 2.17.0+)._
|
|
104
|
+
|
|
98
105
|
### 3. Format to LCOV
|
|
99
106
|
|
|
100
107
|
Convert the raw JSON data into the standard LCOV format.
|
|
@@ -107,6 +114,8 @@ dart run coverage:format_coverage --packages=.dart_tool/package_config.json --lc
|
|
|
107
114
|
|
|
108
115
|
### Example: `pubspec.yaml` Configuration
|
|
109
116
|
|
|
117
|
+
Ensure your `pubspec.yaml` reflects the `coverage` package strictly under `dev_dependencies`.
|
|
118
|
+
|
|
110
119
|
```yaml
|
|
111
120
|
name: my_dart_app
|
|
112
121
|
environment:
|
|
@@ -122,6 +131,8 @@ dev_dependencies:
|
|
|
122
131
|
|
|
123
132
|
### Example: Applying Ignore Directives
|
|
124
133
|
|
|
134
|
+
Use ignore directives to prevent generated code or untestable edge cases from lowering coverage scores.
|
|
135
|
+
|
|
125
136
|
```dart
|
|
126
137
|
// coverage:ignore-file
|
|
127
138
|
import 'package:meta/meta.dart';
|
package/skills/debug/SKILL.md
CHANGED
|
@@ -96,7 +96,24 @@ Collect live evidence before diagnosing. Propose code fixes only after inspectin
|
|
|
96
96
|
- `mcp__plugin_flutter_flutter-ultra-runtime__count_widget_tree_nodes` to gauge tree complexity.
|
|
97
97
|
- `mcp__plugin_flutter_flutter-ultra-runtime__start_tail_logs` + `mcp__plugin_flutter_flutter-ultra-runtime__poll_tail_logs` for live log streaming during reproduction.
|
|
98
98
|
|
|
99
|
-
### 5.
|
|
99
|
+
### 5. Advanced VM service access
|
|
100
|
+
|
|
101
|
+
For low-level inspection beyond the standard helpers, use `mcp__plugin_flutter_flutter-ultra-runtime__call_vm_service_method` to call raw VM Service protocol methods directly:
|
|
102
|
+
|
|
103
|
+
- `getStack` — full isolate stack frames at any moment (useful when `get_runtime_errors` doesn't capture a synchronous deadlock).
|
|
104
|
+
- `getObject` — inspect any live Dart object by its VM object ID retrieved from `evaluate`.
|
|
105
|
+
- `evaluate` with full params — pass `disableBreakpoints: true` or `scope` overrides that the wrapper `evaluate` tool does not expose.
|
|
106
|
+
|
|
107
|
+
```
|
|
108
|
+
call_vm_service_method(
|
|
109
|
+
method: "getStack",
|
|
110
|
+
params: { "isolateId": "<id>", "limit": 20 }
|
|
111
|
+
)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Use this when `evaluate` and `get_runtime_errors` are insufficient to explain observed behaviour.
|
|
115
|
+
|
|
116
|
+
### 6. Evaluate in-app expressions
|
|
100
117
|
|
|
101
118
|
`mcp__plugin_flutter_flutter-ultra-runtime__evaluate` freely to inspect live objects:
|
|
102
119
|
|
|
@@ -104,7 +121,7 @@ Collect live evidence before diagnosing. Propose code fixes only after inspectin
|
|
|
104
121
|
- `context.read<MyBloc>().state.toString()`
|
|
105
122
|
- `ProviderScope.containerOf(context).read(myProvider).toString()`
|
|
106
123
|
|
|
107
|
-
###
|
|
124
|
+
### 7. Test the fix
|
|
108
125
|
|
|
109
126
|
1. Apply the code change.
|
|
110
127
|
2. `mcp__plugin_flutter_flutter-ultra-runtime__hot_reload` (or `mcp__plugin_flutter_flutter-ultra-runtime__hot_restart` if `initState` changed).
|
package/skills/drive/SKILL.md
CHANGED
|
@@ -30,18 +30,22 @@ Break the user's request into discrete, verifiable steps. Announce the plan befo
|
|
|
30
30
|
|
|
31
31
|
**Perform the action:**
|
|
32
32
|
|
|
33
|
-
| Action | Tool
|
|
34
|
-
| ------------------------- |
|
|
35
|
-
| Tap by key/text/coords | `mcp__plugin_flutter_flutter-ultra-gesture__tap`
|
|
36
|
-
| Double tap | `mcp__plugin_flutter_flutter-ultra-gesture__double_tap`
|
|
37
|
-
| Long press | `mcp__plugin_flutter_flutter-ultra-gesture__long_press`
|
|
38
|
-
| Enter text | `mcp__plugin_flutter_flutter-ultra-gesture__enter_text`
|
|
39
|
-
| Clear text field | `mcp__plugin_flutter_flutter-ultra-gesture__clear_text`
|
|
40
|
-
| Scroll to element | `mcp__plugin_flutter_flutter-ultra-gesture__scroll_to`
|
|
41
|
-
| Scroll until visible | `mcp__plugin_flutter_flutter-ultra-gesture__scroll_until_visible`
|
|
42
|
-
| Swipe gesture | `mcp__plugin_flutter_flutter-ultra-gesture__swipe`
|
|
43
|
-
| Pinch zoom | `mcp__plugin_flutter_flutter-ultra-gesture__pinch_zoom`
|
|
44
|
-
|
|
|
33
|
+
| Action | Tool |
|
|
34
|
+
| ------------------------- | ---------------------------------------------------------------------- |
|
|
35
|
+
| Tap by key/text/coords | `mcp__plugin_flutter_flutter-ultra-gesture__tap` |
|
|
36
|
+
| Double tap | `mcp__plugin_flutter_flutter-ultra-gesture__double_tap` |
|
|
37
|
+
| Long press | `mcp__plugin_flutter_flutter-ultra-gesture__long_press` |
|
|
38
|
+
| Enter text | `mcp__plugin_flutter_flutter-ultra-gesture__enter_text` |
|
|
39
|
+
| Clear text field | `mcp__plugin_flutter_flutter-ultra-gesture__clear_text` |
|
|
40
|
+
| Scroll to element | `mcp__plugin_flutter_flutter-ultra-gesture__scroll_to` |
|
|
41
|
+
| Scroll until visible | `mcp__plugin_flutter_flutter-ultra-gesture__scroll_until_visible` |
|
|
42
|
+
| Swipe gesture | `mcp__plugin_flutter_flutter-ultra-gesture__swipe` |
|
|
43
|
+
| Pinch zoom | `mcp__plugin_flutter_flutter-ultra-gesture__pinch_zoom` |
|
|
44
|
+
| Drag and drop | `mcp__plugin_flutter_flutter-ultra-browser__drag` |
|
|
45
|
+
| Multi-touch gesture | `mcp__plugin_flutter_flutter-ultra-gesture__perform_actions` |
|
|
46
|
+
| Open deep link | `mcp__plugin_flutter_flutter-ultra-native-mobile__dispatch_deep_link` |
|
|
47
|
+
| Set location | `mcp__plugin_flutter_flutter-ultra-native-mobile__set_device_location` |
|
|
48
|
+
| Navigate programmatically | `mcp__plugin_flutter_flutter-ultra-runtime__evaluate` |
|
|
45
49
|
|
|
46
50
|
**Wait for the UI to settle:**
|
|
47
51
|
|
|
@@ -77,8 +81,19 @@ When a flow involves an external OAuth consent screen:
|
|
|
77
81
|
3. `mcp__plugin_flutter_flutter-ultra-browser__fill` for username/password fields.
|
|
78
82
|
4. `mcp__plugin_flutter_flutter-ultra-browser__click` on the submit button.
|
|
79
83
|
5. `mcp__plugin_flutter_flutter-ultra-browser__wait_for_url` matching the app's redirect URI.
|
|
84
|
+
6. Use `mcp__plugin_flutter_flutter-ultra-browser__mock_network_route` to stub API responses during the flow when deterministic data is needed.
|
|
85
|
+
7. Use `mcp__plugin_flutter_flutter-ultra-browser__network_state_set` to simulate offline/slow-network conditions and verify the app handles connectivity loss gracefully.
|
|
80
86
|
|
|
81
|
-
### 6. Native
|
|
87
|
+
### 6. Native OS interactions (mobile targets)
|
|
88
|
+
|
|
89
|
+
For flows that leave the Flutter layer and interact with the host OS:
|
|
90
|
+
|
|
91
|
+
- **Share sheet**: `mcp__plugin_flutter_flutter-ultra-native-mobile__handle_share_sheet` to detect the OS share sheet after triggering a share action, then select an app or dismiss.
|
|
92
|
+
- **Notification flows**: `mcp__plugin_flutter_flutter-ultra-native-mobile__open_notification_tray` to expand the shade, `mcp__plugin_flutter_flutter-ultra-native-mobile__list_notifications` to find the target notification, `mcp__plugin_flutter_flutter-ultra-native-mobile__tap_notification` to open it and return to the app.
|
|
93
|
+
- **Chrome Custom Tab / SFSafariViewController flows**: `mcp__plugin_flutter_flutter-ultra-native-mobile__detect_in_app_browser` to confirm the CCT/SVC is open, `mcp__plugin_flutter_flutter-ultra-native-mobile__interact_in_app_browser` to type/tap within it (e.g., OAuth consent), then wait for the app to resume.
|
|
94
|
+
- **File picker**: `mcp__plugin_flutter_flutter-ultra-native-mobile__pick_file_native` to select a file from the OS file picker dialog that Flutter cannot drive with gesture tools.
|
|
95
|
+
|
|
96
|
+
### 7. Native mobile system dialogs (mobile targets)
|
|
82
97
|
|
|
83
98
|
- `mcp__plugin_flutter_flutter-ultra-native-mobile__wait_for_native_element` to detect OS dialogs.
|
|
84
99
|
- `mcp__plugin_flutter_flutter-ultra-native-mobile__native_permission_grant` or `mcp__plugin_flutter_flutter-ultra-native-mobile__native_permission_deny`.
|
|
@@ -87,7 +102,7 @@ When a flow involves an external OAuth consent screen:
|
|
|
87
102
|
- `mcp__plugin_flutter_flutter-ultra-native-mobile__native_home` to press Home.
|
|
88
103
|
- `mcp__plugin_flutter_flutter-ultra-native-mobile__solve_oauth_cct` for Chrome Custom Tab OAuth flows.
|
|
89
104
|
|
|
90
|
-
###
|
|
105
|
+
### 8. Native desktop dialogs (desktop targets)
|
|
91
106
|
|
|
92
107
|
- `mcp__plugin_flutter_flutter-ultra-native-desktop__wait_for_window` to detect native dialogs.
|
|
93
108
|
- `mcp__plugin_flutter_flutter-ultra-native-desktop__desktop_click` to interact with dialog buttons.
|
|
@@ -95,7 +110,7 @@ When a flow involves an external OAuth consent screen:
|
|
|
95
110
|
- `mcp__plugin_flutter_flutter-ultra-native-desktop__select_file_in_dialog` for file picker dialogs.
|
|
96
111
|
- `mcp__plugin_flutter_flutter-ultra-native-desktop__confirm_dialog` to accept/dismiss confirmation dialogs.
|
|
97
112
|
|
|
98
|
-
###
|
|
113
|
+
### 9. Report the flow result
|
|
99
114
|
|
|
100
115
|
Produce a numbered summary: steps taken, pass/fail status per step, screenshot paths, and any errors from `mcp__plugin_flutter_flutter-ultra-runtime__get_runtime_errors`.
|
|
101
116
|
|