reis-mobile 0.3.5
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 +24 -0
- package/.claude-plugin/plugin.json +12 -0
- package/CHANGELOG.md +103 -0
- package/LICENSE +21 -0
- package/README.md +631 -0
- package/THIRD_PARTY_NOTICES.md +105 -0
- package/agents/flutter-architect.md +92 -0
- package/agents/flutter-performance-engineer.md +103 -0
- package/agents/flutter-test-engineer.md +100 -0
- package/agents/lead-mobile.md +107 -0
- package/agents/mobile-code-reviewer.md +66 -0
- package/agents/mobile-staff-engineer.md +85 -0
- package/agents/plugin-native-expert.md +97 -0
- package/bin/reis-mobile.mjs +256 -0
- package/commands/debate.md +135 -0
- package/commands/doctor.md +23 -0
- package/commands/reis-mobile.md +50 -0
- package/commands/review.md +53 -0
- package/core/config/language.mjs +83 -0
- package/core/context/context-engine.mjs +113 -0
- package/core/detection/project-probe.mjs +104 -0
- package/core/detection/stack-detector.mjs +204 -0
- package/core/diagnostics/doctor.mjs +181 -0
- package/core/install/claude-plugin.mjs +76 -0
- package/core/paths.mjs +4 -0
- package/core/registry/frontmatter.mjs +81 -0
- package/core/registry/registry.mjs +140 -0
- package/core/router/intent-detector.mjs +75 -0
- package/core/router/intents.mjs +91 -0
- package/core/router/router.mjs +83 -0
- package/core/security/redact.mjs +24 -0
- package/package.json +35 -0
- package/skills/architecture-feature-first/SKILL.md +116 -0
- package/skills/bloc/SKILL.md +354 -0
- package/skills/code-review/SKILL.md +89 -0
- package/skills/dart-3-updates/SKILL.md +223 -0
- package/skills/dart-add-unit-test/SKILL.md +126 -0
- package/skills/dart-collect-coverage/SKILL.md +145 -0
- package/skills/dart-fix-runtime-errors/SKILL.md +170 -0
- package/skills/dart-generate-test-mocks/SKILL.md +159 -0
- package/skills/dart-migrate-to-checks-package/SKILL.md +532 -0
- package/skills/dart-resolve-package-conflicts/SKILL.md +120 -0
- package/skills/dart-run-static-analysis/SKILL.md +108 -0
- package/skills/dart-use-pattern-matching/SKILL.md +240 -0
- package/skills/dart-use-pattern-matching/examples/json_patterns.dart +85 -0
- package/skills/detect-mobile-stack/SKILL.md +33 -0
- package/skills/effective-dart/SKILL.md +187 -0
- package/skills/firebase-ai/SKILL.md +93 -0
- package/skills/firebase-analytics/SKILL.md +129 -0
- package/skills/firebase-app-check/SKILL.md +124 -0
- package/skills/firebase-auth/SKILL.md +224 -0
- package/skills/firebase-cloud-firestore/SKILL.md +165 -0
- package/skills/firebase-cloud-functions/SKILL.md +148 -0
- package/skills/firebase-crashlytics/SKILL.md +182 -0
- package/skills/firebase-data-connect/SKILL.md +80 -0
- package/skills/firebase-database/SKILL.md +224 -0
- package/skills/firebase-in-app-messaging/SKILL.md +111 -0
- package/skills/firebase-messaging/SKILL.md +201 -0
- package/skills/firebase-remote-config/SKILL.md +131 -0
- package/skills/firebase-storage/SKILL.md +158 -0
- package/skills/flutter-add-integration-test/SKILL.md +167 -0
- package/skills/flutter-add-widget-preview/SKILL.md +149 -0
- package/skills/flutter-add-widget-test/SKILL.md +158 -0
- package/skills/flutter-adding-home-screen-widgets/SKILL.md +205 -0
- package/skills/flutter-animating-apps/SKILL.md +185 -0
- package/skills/flutter-app-architecture/SKILL.md +80 -0
- package/skills/flutter-apply-architecture-best-practices/SKILL.md +166 -0
- package/skills/flutter-build-responsive-layout/SKILL.md +143 -0
- package/skills/flutter-building-forms/SKILL.md +125 -0
- package/skills/flutter-building-layouts/SKILL.md +131 -0
- package/skills/flutter-building-plugins/SKILL.md +209 -0
- package/skills/flutter-caching-data/SKILL.md +171 -0
- package/skills/flutter-change-notifier/SKILL.md +138 -0
- package/skills/flutter-embedding-native-views/SKILL.md +200 -0
- package/skills/flutter-errors/SKILL.md +126 -0
- package/skills/flutter-fix-layout-issues/SKILL.md +134 -0
- package/skills/flutter-handling-concurrency/SKILL.md +182 -0
- package/skills/flutter-implement-json-serialization/SKILL.md +157 -0
- package/skills/flutter-improving-accessibility/SKILL.md +113 -0
- package/skills/flutter-interoperating-with-native-apis/SKILL.md +130 -0
- package/skills/flutter-login-usecase/SKILL.md +38 -0
- package/skills/flutter-managing-state/SKILL.md +206 -0
- package/skills/flutter-project-audit/SKILL.md +66 -0
- package/skills/flutter-reducing-app-size/SKILL.md +103 -0
- package/skills/flutter-secure-token-store/SKILL.md +35 -0
- package/skills/flutter-setting-up-on-macos/SKILL.md +78 -0
- package/skills/flutter-setup-declarative-routing/SKILL.md +259 -0
- package/skills/flutter-setup-localization/SKILL.md +214 -0
- package/skills/flutter-theming-apps/SKILL.md +163 -0
- package/skills/flutter-use-http-package/SKILL.md +178 -0
- package/skills/flutter-widget-review/SKILL.md +54 -0
- package/skills/flutter-working-with-databases/SKILL.md +203 -0
- package/skills/flutterfire-configure/SKILL.md +143 -0
- package/skills/mobile-security-audit/SKILL.md +101 -0
- package/skills/mockito/SKILL.md +149 -0
- package/skills/mocktail/SKILL.md +122 -0
- package/skills/patrol-e2e-testing/SKILL.md +224 -0
- package/skills/provider/SKILL.md +139 -0
- package/skills/riverpod/SKILL.md +246 -0
- package/skills/testing/SKILL.md +50 -0
- package/stacks/android/stack.json +8 -0
- package/stacks/flutter/stack.json +8 -0
- package/stacks/ios/stack.json +8 -0
- package/stacks/kotlin-multiplatform/stack.json +8 -0
- package/stacks/react-native/stack.json +8 -0
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mockito
|
|
3
|
+
description: Uses the Mockito package for mocking in Flutter/Dart tests. Use when generating mocks, stubbing methods, verifying interactions, capturing arguments, or deciding between mocks, fakes, and real objects.
|
|
4
|
+
intents: [test]
|
|
5
|
+
stacks: [flutter]
|
|
6
|
+
source: https://github.com/evanca/flutter-ai-rules
|
|
7
|
+
license: MIT
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Mockito Skill
|
|
11
|
+
|
|
12
|
+
This skill defines how to correctly use the `mockito` package for mocking in Dart and Flutter tests.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 1. Mock vs. Fake vs. Real Object
|
|
17
|
+
|
|
18
|
+
| Use | When |
|
|
19
|
+
|---|---|
|
|
20
|
+
| **Real object** | Prefer over mocks when practical. |
|
|
21
|
+
| **Fake** (`extends Fake`) | Lightweight custom implementation; override only the methods you need. Prefer over mocks when you don't need interaction verification. |
|
|
22
|
+
| **Mock** (`extends Mock`) | Only when you need to **verify interactions** (call counts, arguments) or stub dynamic responses. |
|
|
23
|
+
|
|
24
|
+
- **Data models** should not be mocked if they can be constructed with stubbed data.
|
|
25
|
+
- Only use mocks if your test has `verify` assertions; otherwise prefer real or fake objects.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 2. Generating Mocks
|
|
30
|
+
|
|
31
|
+
```dart
|
|
32
|
+
@GenerateMocks([MyClass])
|
|
33
|
+
// or for nice mocks (return simple legal values for missing stubs):
|
|
34
|
+
@GenerateNiceMocks([MockSpec<MyClass>()])
|
|
35
|
+
void main() { ... }
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
dart run build_runner build
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
- Only annotate files under `test/` for mock generation by default.
|
|
43
|
+
- Use a `build.yaml` if you need to generate mocks outside of `test/`.
|
|
44
|
+
- Never add `@override` methods or implementations to a class extending `Mock`.
|
|
45
|
+
- Never stub responses in a mock's constructor or inside the mock class — always stub in your tests.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 3. Stubbing
|
|
50
|
+
|
|
51
|
+
```dart
|
|
52
|
+
final mock = MockCat();
|
|
53
|
+
|
|
54
|
+
// Return a value
|
|
55
|
+
when(mock.sound()).thenReturn('Meow');
|
|
56
|
+
|
|
57
|
+
// Throw an error
|
|
58
|
+
when(mock.sound()).thenThrow(Exception('No sound'));
|
|
59
|
+
|
|
60
|
+
// Calculate response at call time
|
|
61
|
+
when(mock.sound()).thenAnswer((_) => computedValue);
|
|
62
|
+
|
|
63
|
+
// Return values in sequence
|
|
64
|
+
when(mock.sound()).thenReturnInOrder(['Meow', 'Purr']);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
- Always stub methods/getters **before** using them if you need specific return values.
|
|
68
|
+
- Missing stub behavior: `@GenerateMocks` → throws; `@GenerateNiceMocks` → returns a simple legal value.
|
|
69
|
+
- Use `throwOnMissingStub(mock)` to throw on any unstubbed call.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 4. Verification
|
|
74
|
+
|
|
75
|
+
```dart
|
|
76
|
+
verify(mock.sound()); // called at least once
|
|
77
|
+
verifyNever(mock.eat(any)); // never called
|
|
78
|
+
verify(mock.sound()).called(2); // called exactly twice
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Async:**
|
|
82
|
+
|
|
83
|
+
```dart
|
|
84
|
+
await untilCalled(mock.sound()); // wait for the interaction
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 5. Argument Matchers
|
|
90
|
+
|
|
91
|
+
```dart
|
|
92
|
+
// Flexible stubbing
|
|
93
|
+
when(mock.eat(any)).thenReturn(true);
|
|
94
|
+
when(mock.eat(argThat(isNotNull))).thenReturn(true);
|
|
95
|
+
|
|
96
|
+
// Named arguments
|
|
97
|
+
when(mock.fetch(any, headers: any)).thenReturn(response);
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
- Do **not** use `null` as an argument adjacent to an argument matcher.
|
|
101
|
+
- For named arguments, use `any` or `argThat` as values, not as argument names.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 6. Capturing Arguments
|
|
106
|
+
|
|
107
|
+
```dart
|
|
108
|
+
final captured = verify(mock.eat(captureAny)).captured;
|
|
109
|
+
print(captured.last); // last captured argument
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Use `captureThat` for conditional capturing.
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 7. Resetting Mocks
|
|
117
|
+
|
|
118
|
+
```dart
|
|
119
|
+
reset(mock); // clear all stubs AND interactions
|
|
120
|
+
clearInteractions(mock); // clear only recorded interactions
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 8. Mocking Function Types
|
|
126
|
+
|
|
127
|
+
To mock a function type (e.g., a callback), define an abstract class with the required signature and generate mocks for it:
|
|
128
|
+
|
|
129
|
+
```dart
|
|
130
|
+
abstract class Callback {
|
|
131
|
+
void call(String value);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
@GenerateMocks([Callback])
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## 9. Debugging
|
|
140
|
+
|
|
141
|
+
```dart
|
|
142
|
+
logInvocations([mock1, mock2]); // print all collected invocations
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## References
|
|
148
|
+
|
|
149
|
+
- [Mockito GitHub Repository](https://github.com/dart-lang/mockito)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mocktail
|
|
3
|
+
description: Uses the Mocktail package for mocking in Flutter/Dart tests. Use when creating mocks, stubbing methods, verifying interactions, registering fallback values, or deciding between mocks, fakes, and real objects.
|
|
4
|
+
intents: [test]
|
|
5
|
+
stacks: [flutter]
|
|
6
|
+
source: https://github.com/evanca/flutter-ai-rules
|
|
7
|
+
license: MIT
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Mocktail Skill
|
|
11
|
+
|
|
12
|
+
This skill defines how to correctly use the `mocktail` package for mocking in Dart and Flutter tests.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 1. Mock vs. Fake vs. Real Object
|
|
17
|
+
|
|
18
|
+
| Use | When |
|
|
19
|
+
|---|---|
|
|
20
|
+
| **Real object** | Prefer over mocks when practical. |
|
|
21
|
+
| **Fake** (`extends Fake`) | Lightweight custom implementation; override only the methods you need. Prefer over mocks when you don't need interaction verification. |
|
|
22
|
+
| **Mock** (`extends Mock`) | Only when you need to **verify interactions** (call counts, arguments) or stub dynamic responses. |
|
|
23
|
+
|
|
24
|
+
- Never add `@override` methods or implementations to a class extending `Mock`.
|
|
25
|
+
- Only use mocks if your test has `verify` assertions; otherwise prefer real or fake objects.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## 2. Creating Mocks
|
|
30
|
+
|
|
31
|
+
```dart
|
|
32
|
+
class MockMyService extends Mock implements MyService {}
|
|
33
|
+
class FakeMyEvent extends Fake implements MyEvent {}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
No code generation required — unlike Mockito, Mocktail uses `noSuchMethod` at runtime.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
40
|
+
## 3. Registering Fallback Values
|
|
41
|
+
|
|
42
|
+
Register fallback values **before** using custom types with argument matchers. Do this in `setUpAll` or at the top of your test:
|
|
43
|
+
|
|
44
|
+
```dart
|
|
45
|
+
setUpAll(() {
|
|
46
|
+
registerFallbackValue(FakeMyEvent());
|
|
47
|
+
});
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
- Required for non-nullable custom types used with `any()`, `captureAny()`, or `captureThat()`.
|
|
51
|
+
- Register fallback values for **any** custom type used with argument matchers.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 4. Stubbing
|
|
56
|
+
|
|
57
|
+
```dart
|
|
58
|
+
final mock = MockMyService();
|
|
59
|
+
|
|
60
|
+
// Return a value
|
|
61
|
+
when(() => mock.fetchData()).thenReturn('result');
|
|
62
|
+
|
|
63
|
+
// Throw an error
|
|
64
|
+
when(() => mock.fetchData()).thenThrow(Exception('error'));
|
|
65
|
+
|
|
66
|
+
// Dynamic/async response
|
|
67
|
+
when(() => mock.fetchData()).thenAnswer((_) async => 'result');
|
|
68
|
+
|
|
69
|
+
// Future<void>
|
|
70
|
+
when(() => mock.doWork()).thenAnswer((_) async {});
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
- Always stub async methods (returning `Future` or `Future<void>`) with `thenAnswer`.
|
|
74
|
+
- Stub every method you expect to be called, even if it's not the focus of your test.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 5. Named Parameters
|
|
79
|
+
|
|
80
|
+
Always include **all named parameters** in both `when` and `verify` calls. Use `any(named: 'paramName')` for those you don't care about:
|
|
81
|
+
|
|
82
|
+
```dart
|
|
83
|
+
when(() => mock.fetch(
|
|
84
|
+
id: any(named: 'id'),
|
|
85
|
+
headers: any(named: 'headers'),
|
|
86
|
+
)).thenReturn(response);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
- If a method has default values for named parameters, Mocktail still expects them all to be matched.
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 6. Verification
|
|
94
|
+
|
|
95
|
+
```dart
|
|
96
|
+
verify(() => mock.fetchData()); // called at least once
|
|
97
|
+
verifyNever(() => mock.fetchData()); // never called
|
|
98
|
+
verify(() => mock.fetchData()).called(2); // called exactly twice
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 7. Argument Matchers
|
|
104
|
+
|
|
105
|
+
```dart
|
|
106
|
+
// Any positional argument
|
|
107
|
+
when(() => mock.process(any())).thenReturn(true);
|
|
108
|
+
|
|
109
|
+
// Capture arguments for later assertions
|
|
110
|
+
final captured = verify(() => mock.process(captureAny())).captured;
|
|
111
|
+
print(captured.last);
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
- Use `any()` for positional parameters when you don't care about the exact value.
|
|
115
|
+
- Use `captureThat()` for conditional capturing.
|
|
116
|
+
- When matching string output, be aware of what `.toString()` returns for the type.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
## References
|
|
121
|
+
|
|
122
|
+
- [Mocktail GitHub Repository](https://github.com/felangel/mocktail)
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: patrol-e2e-testing
|
|
3
|
+
description: Generates and maintains end-to-end tests for Flutter apps using Patrol. Use when adding E2E coverage for new features, regression tests for UI bugs, or testing native interactions (permissions, system dialogs, deep links)
|
|
4
|
+
intents: [test]
|
|
5
|
+
stacks: [flutter]
|
|
6
|
+
source: https://github.com/evanca/flutter-ai-rules
|
|
7
|
+
license: MIT
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Patrol E2E Testing Skill
|
|
11
|
+
|
|
12
|
+
This skill defines how to design, implement, and run end-to-end (E2E) tests using Patrol 4.x in Flutter projects.
|
|
13
|
+
|
|
14
|
+
It focuses on:
|
|
15
|
+
|
|
16
|
+
* Covering new feature flows with deterministic UI tests
|
|
17
|
+
* Converting reproducible UI bugs into regression tests
|
|
18
|
+
* Handling native OS interactions
|
|
19
|
+
* Running tests locally and in CI using correct Patrol CLI commands
|
|
20
|
+
|
|
21
|
+
## When to Use
|
|
22
|
+
|
|
23
|
+
Use this skill when:
|
|
24
|
+
|
|
25
|
+
* A new screen or user flow must be covered with E2E tests.
|
|
26
|
+
* A feature interacts with native components (permissions, notifications, system dialogs).
|
|
27
|
+
* A UI bug should be captured as a regression test.
|
|
28
|
+
* Cross-platform behavior (Android / iOS / Web) must be validated.
|
|
29
|
+
|
|
30
|
+
## Setup
|
|
31
|
+
|
|
32
|
+
For installation and project initialization, follow the official documentation:
|
|
33
|
+
|
|
34
|
+
[https://patrol.leancode.co/documentation#setup](https://patrol.leancode.co/documentation#setup)
|
|
35
|
+
|
|
36
|
+
Key Patrol conventions:
|
|
37
|
+
|
|
38
|
+
* `patrol` is added as a dev dependency.
|
|
39
|
+
* Tests live in `patrol_test/`.
|
|
40
|
+
* Test files must end with `_test.dart`.
|
|
41
|
+
* Tests are executed with `patrol test`.
|
|
42
|
+
|
|
43
|
+
## How to use this skill
|
|
44
|
+
|
|
45
|
+
Follow the steps below when implementing or updating Patrol tests.
|
|
46
|
+
|
|
47
|
+
### 1. Identify the user journey
|
|
48
|
+
|
|
49
|
+
Break the feature into:
|
|
50
|
+
|
|
51
|
+
* **Actions**: taps, scrolls, input, navigation, deep links.
|
|
52
|
+
* **Observable outcomes**: visible text, screen changes, enabled buttons, dialogs.
|
|
53
|
+
|
|
54
|
+
Rules:
|
|
55
|
+
|
|
56
|
+
* One test per primary (happy-path) journey.
|
|
57
|
+
* Separate tests for critical edge cases.
|
|
58
|
+
* Avoid combining unrelated flows in a single test.
|
|
59
|
+
|
|
60
|
+
### 2. Structure the Patrol test
|
|
61
|
+
|
|
62
|
+
Basic Patrol structure:
|
|
63
|
+
|
|
64
|
+
```dart
|
|
65
|
+
import 'package:flutter_test/flutter_test.dart';
|
|
66
|
+
import 'package:patrol/patrol.dart';
|
|
67
|
+
|
|
68
|
+
void main() {
|
|
69
|
+
patrolTest(
|
|
70
|
+
'user can log in successfully',
|
|
71
|
+
($) async {
|
|
72
|
+
await $.pumpWidgetAndSettle(const MyApp());
|
|
73
|
+
|
|
74
|
+
const email = String.fromEnvironment('E2E_EMAIL');
|
|
75
|
+
const password = String.fromEnvironment('E2E_PASSWORD');
|
|
76
|
+
|
|
77
|
+
await $(#emailField).enterText(email);
|
|
78
|
+
await $(#passwordField).enterText(password);
|
|
79
|
+
await $(#loginButton).tap();
|
|
80
|
+
|
|
81
|
+
await $.waitUntilVisible($(#homeScreenTitle));
|
|
82
|
+
|
|
83
|
+
expect($(#homeScreenTitle).text, equals('Welcome'));
|
|
84
|
+
},
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Key concepts:
|
|
90
|
+
|
|
91
|
+
* Use `patrolTest()` instead of `testWidgets()`.
|
|
92
|
+
* `$` is the Patrol tester.
|
|
93
|
+
* Use `$(#keyName)` to find widgets by `Key`.
|
|
94
|
+
* Use explicit wait conditions (e.g., `waitUntilVisible`).
|
|
95
|
+
|
|
96
|
+
### 3. Handle native dialogs
|
|
97
|
+
|
|
98
|
+
For OS-level permission dialogs:
|
|
99
|
+
|
|
100
|
+
```dart
|
|
101
|
+
patrolTest('grants camera permission', ($) async {
|
|
102
|
+
await $.pumpWidgetAndSettle(const MyApp());
|
|
103
|
+
|
|
104
|
+
await $(#openCameraButton).tap();
|
|
105
|
+
|
|
106
|
+
if (await $.native.isPermissionDialogVisible()) {
|
|
107
|
+
await $.native.grantPermission();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
await $.waitUntilVisible($(#cameraPreview));
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Use native automation only when required by the feature.
|
|
115
|
+
|
|
116
|
+
### 4. Selector & interaction quick reference
|
|
117
|
+
|
|
118
|
+
**Finding widgets:**
|
|
119
|
+
|
|
120
|
+
```dart
|
|
121
|
+
$('some text') // by text
|
|
122
|
+
$(TextField) // by type
|
|
123
|
+
$(Icons.arrow_back) // by icon
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Tapping:**
|
|
127
|
+
|
|
128
|
+
```dart
|
|
129
|
+
// Tap a widget containing a specific text label
|
|
130
|
+
await $(Container).$('click').tap();
|
|
131
|
+
|
|
132
|
+
// Tap a container that contains an ElevatedButton
|
|
133
|
+
await $(Container).containing(ElevatedButton).tap();
|
|
134
|
+
|
|
135
|
+
// Tap only the enabled ElevatedButton
|
|
136
|
+
await $(ElevatedButton)
|
|
137
|
+
.which<ElevatedButton>(
|
|
138
|
+
(b) => b.enabled,
|
|
139
|
+
)
|
|
140
|
+
.tap();
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**Entering text:**
|
|
144
|
+
|
|
145
|
+
```dart
|
|
146
|
+
// Enter text into the second TextField on screen
|
|
147
|
+
await $(TextField).at(1).enterText('your input');
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Scrolling:**
|
|
151
|
+
|
|
152
|
+
```dart
|
|
153
|
+
await $(widget_you_want_to_scroll_to).scrollTo();
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Native interactions:**
|
|
157
|
+
|
|
158
|
+
```dart
|
|
159
|
+
// Grant permission while app is in use
|
|
160
|
+
await $.native.grantPermissionWhenInUse();
|
|
161
|
+
|
|
162
|
+
// Open notification shade and tap a notification by text
|
|
163
|
+
await $.native.openNotifications();
|
|
164
|
+
await $.native.tapOnNotificationBySelector(
|
|
165
|
+
Selector(textContains: 'text'),
|
|
166
|
+
);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### 5. Running Patrol tests
|
|
170
|
+
|
|
171
|
+
Run all tests:
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
patrol test
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Run a specific file with live reload (development mode):
|
|
178
|
+
|
|
179
|
+
```bash
|
|
180
|
+
patrol develop -t integration_test/my_test.dart
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Run a specific file:
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
patrol test --target patrol_test/login_test.dart
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Run on web:
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
patrol test --device chrome
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
Headless web (CI):
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
patrol test --device chrome --web-headless true
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
Filter by tags:
|
|
202
|
+
|
|
203
|
+
```bash
|
|
204
|
+
patrol test --tags android
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
### Output requirements when this skill is used
|
|
208
|
+
|
|
209
|
+
When applied, this skill must produce:
|
|
210
|
+
|
|
211
|
+
1. Patrol test(s) covering the specified feature.
|
|
212
|
+
2. Any required widget key additions.
|
|
213
|
+
3. Exact `patrol test` command(s) to execute locally.
|
|
214
|
+
4. Notes explaining stabilization decisions.
|
|
215
|
+
|
|
216
|
+
### Quality bar
|
|
217
|
+
|
|
218
|
+
A valid Patrol test must be:
|
|
219
|
+
|
|
220
|
+
* Deterministic (no arbitrary delays)
|
|
221
|
+
* Readable
|
|
222
|
+
* Minimal but complete
|
|
223
|
+
* Secret-safe (no hardcoded credentials)
|
|
224
|
+
* CI-ready
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: provider
|
|
3
|
+
description: Uses the Provider package for dependency injection and state management in Flutter. Use when setting up providers, consuming state, optimizing rebuilds, using ProxyProvider, or migrating from deprecated providers.
|
|
4
|
+
routing: manual
|
|
5
|
+
stacks: [flutter]
|
|
6
|
+
source: https://github.com/evanca/flutter-ai-rules
|
|
7
|
+
license: MIT
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Provider Skill
|
|
11
|
+
|
|
12
|
+
This skill defines how to correctly use the `provider` package in Flutter applications.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## 1. Provider Types
|
|
17
|
+
|
|
18
|
+
| Provider | Use for |
|
|
19
|
+
|---|---|
|
|
20
|
+
| `Provider` | Exposing any immutable value |
|
|
21
|
+
| `ChangeNotifierProvider` | Mutable state with `ChangeNotifier` |
|
|
22
|
+
| `FutureProvider` | Exposing a `Future` result |
|
|
23
|
+
| `StreamProvider` | Exposing a `Stream` |
|
|
24
|
+
| `ProxyProvider` / `ChangeNotifierProxyProvider` | Objects that depend on other providers |
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 2. Setup
|
|
29
|
+
|
|
30
|
+
```dart
|
|
31
|
+
MultiProvider(
|
|
32
|
+
providers: [
|
|
33
|
+
Provider<Something>(create: (_) => Something()),
|
|
34
|
+
ChangeNotifierProvider(create: (_) => MyNotifier()),
|
|
35
|
+
FutureProvider<String>(create: (_) => fetchData(), initialData: ''),
|
|
36
|
+
],
|
|
37
|
+
child: MyApp(),
|
|
38
|
+
)
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- Use `MultiProvider` to group multiple providers and avoid deeply nested trees.
|
|
42
|
+
- `ChangeNotifierProvider` **automatically disposes** the model when it is no longer needed.
|
|
43
|
+
- **Never** create a provider's object from variables that can change over time — the object won't update when the variable changes. Use `ProxyProvider` instead.
|
|
44
|
+
- If you have 150+ providers, consider mounting them over time (e.g., during a splash screen) rather than all at once to avoid `StackOverflowError`.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## 3. Consuming State
|
|
49
|
+
|
|
50
|
+
Always specify the **generic type** for type safety:
|
|
51
|
+
|
|
52
|
+
```dart
|
|
53
|
+
// Listen and rebuild on change
|
|
54
|
+
final count = context.watch<MyModel>().count;
|
|
55
|
+
|
|
56
|
+
// Access without listening (use in callbacks)
|
|
57
|
+
context.read<MyModel>().increment();
|
|
58
|
+
|
|
59
|
+
// Listen to only part of the state
|
|
60
|
+
final count = context.select<MyModel, int>((m) => m.count);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Use `Consumer<T>` or `Selector<T, R>` widgets when you need fine-grained rebuilds and cannot access a descendant `BuildContext`:
|
|
64
|
+
|
|
65
|
+
```dart
|
|
66
|
+
Consumer<MyModel>(
|
|
67
|
+
builder: (context, model, child) => Text('${model.count}'),
|
|
68
|
+
child: const ExpensiveWidget(), // rebuilt only once
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
Selector<MyModel, int>(
|
|
72
|
+
selector: (_, model) => model.count,
|
|
73
|
+
builder: (_, count, __) => Text('$count'),
|
|
74
|
+
)
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 4. ProxyProvider
|
|
80
|
+
|
|
81
|
+
Use `ProxyProvider` or `ChangeNotifierProxyProvider` for objects that depend on other providers or values that can change:
|
|
82
|
+
|
|
83
|
+
```dart
|
|
84
|
+
MultiProvider(
|
|
85
|
+
providers: [
|
|
86
|
+
Provider<Auth>(create: (_) => Auth()),
|
|
87
|
+
ProxyProvider<Auth, Api>(
|
|
88
|
+
update: (_, auth, __) => Api(auth.token),
|
|
89
|
+
),
|
|
90
|
+
],
|
|
91
|
+
)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 5. Rules
|
|
97
|
+
|
|
98
|
+
- Do **not** access providers inside `initState` or constructors — use them in `build`, callbacks, or lifecycle methods where the widget is fully mounted.
|
|
99
|
+
- You can use **any object** as state, not just `ChangeNotifier`; use `Provider.value()` with a `StatefulWidget` if needed.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## 6. Debugging
|
|
104
|
+
|
|
105
|
+
Implement `toString` or `DiagnosticableTreeMixin` to improve how your objects appear in Flutter DevTools:
|
|
106
|
+
|
|
107
|
+
```dart
|
|
108
|
+
class MyModel with DiagnosticableTreeMixin {
|
|
109
|
+
final int count;
|
|
110
|
+
MyModel(this.count);
|
|
111
|
+
|
|
112
|
+
@override
|
|
113
|
+
String toString() => 'MyModel(count: $count)';
|
|
114
|
+
}
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 7. Migration: ValueListenableProvider
|
|
120
|
+
|
|
121
|
+
`ValueListenableProvider` is deprecated. Use `Provider` with `ValueListenableBuilder` instead:
|
|
122
|
+
|
|
123
|
+
```dart
|
|
124
|
+
ValueListenableBuilder<int>(
|
|
125
|
+
valueListenable: myValueListenable,
|
|
126
|
+
builder: (context, value, _) {
|
|
127
|
+
return Provider<int>.value(
|
|
128
|
+
value: value,
|
|
129
|
+
child: MyApp(),
|
|
130
|
+
);
|
|
131
|
+
},
|
|
132
|
+
)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
## References
|
|
138
|
+
|
|
139
|
+
- [Provider GitHub Repository](https://github.com/rrousselGit/provider)
|