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,201 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: firebase-messaging
|
|
3
|
+
description: Integrates Firebase Cloud Messaging (FCM) into Flutter apps. Use when setting up push notifications, handling foreground/background messages, managing permissions, working with FCM tokens, or configuring platform-specific notification behavior.
|
|
4
|
+
routing: manual
|
|
5
|
+
stacks: [flutter]
|
|
6
|
+
source: https://github.com/evanca/flutter-ai-rules
|
|
7
|
+
license: MIT
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Firebase Cloud Messaging Skill
|
|
11
|
+
|
|
12
|
+
This skill defines how to correctly use Firebase Cloud Messaging (FCM) in Flutter applications.
|
|
13
|
+
|
|
14
|
+
## When to Use
|
|
15
|
+
|
|
16
|
+
Use this skill when:
|
|
17
|
+
|
|
18
|
+
* Setting up push notifications with FCM in a Flutter project.
|
|
19
|
+
* Handling messages in foreground, background, and terminated states.
|
|
20
|
+
* Managing notification permissions and FCM tokens.
|
|
21
|
+
* Configuring platform-specific notification display behavior.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1. Setup and Configuration
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
flutter pub add firebase_messaging
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**iOS:**
|
|
32
|
+
- Enable **Push Notifications** and **Background Modes** in Xcode.
|
|
33
|
+
- Upload your **APNs authentication key** to Firebase before using FCM.
|
|
34
|
+
- Do **not** disable method swizzling — it is required for FCM token handling.
|
|
35
|
+
- Ensure the bundle ID for your APNs certificate matches your app's bundle ID.
|
|
36
|
+
|
|
37
|
+
**Android:**
|
|
38
|
+
- Devices must run **Android 4.4+** with Google Play services installed.
|
|
39
|
+
- Check for Google Play services compatibility in both `onCreate()` and `onResume()`.
|
|
40
|
+
|
|
41
|
+
**Web:**
|
|
42
|
+
- Create and register a service worker file named `firebase-messaging-sw.js` in your `web/` directory:
|
|
43
|
+
|
|
44
|
+
```js
|
|
45
|
+
importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-app-compat.js");
|
|
46
|
+
importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-messaging-compat.js");
|
|
47
|
+
|
|
48
|
+
firebase.initializeApp({ /* your config */ });
|
|
49
|
+
|
|
50
|
+
const messaging = firebase.messaging();
|
|
51
|
+
|
|
52
|
+
messaging.onBackgroundMessage((message) => {
|
|
53
|
+
console.log("onBackgroundMessage", message);
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## 2. Message Handling
|
|
60
|
+
|
|
61
|
+
**Foreground messages:**
|
|
62
|
+
|
|
63
|
+
```dart
|
|
64
|
+
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
|
|
65
|
+
print('Foreground message data: ${message.data}');
|
|
66
|
+
if (message.notification != null) {
|
|
67
|
+
print('Notification: ${message.notification}');
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**Background messages:**
|
|
73
|
+
|
|
74
|
+
```dart
|
|
75
|
+
@pragma('vm:entry-point')
|
|
76
|
+
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
|
77
|
+
// Initialize Firebase before using other Firebase services in background
|
|
78
|
+
await Firebase.initializeApp();
|
|
79
|
+
print("Background message: ${message.messageId}");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
void main() {
|
|
83
|
+
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
|
84
|
+
runApp(MyApp());
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Background handler rules:
|
|
89
|
+
- Must be a **top-level function** (not anonymous, not a class method).
|
|
90
|
+
- Annotate with `@pragma('vm:entry-point')` (Flutter 3.3.0+) to prevent removal during tree shaking in release mode.
|
|
91
|
+
- Cannot update app state or execute UI-impacting logic — runs in a separate isolate.
|
|
92
|
+
- Call `Firebase.initializeApp()` before using any other Firebase services.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 3. Permissions
|
|
97
|
+
|
|
98
|
+
```dart
|
|
99
|
+
NotificationSettings settings = await FirebaseMessaging.instance.requestPermission(
|
|
100
|
+
alert: true,
|
|
101
|
+
badge: true,
|
|
102
|
+
sound: true,
|
|
103
|
+
announcement: false,
|
|
104
|
+
carPlay: false,
|
|
105
|
+
criticalAlert: false,
|
|
106
|
+
provisional: false,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
print('Authorization status: ${settings.authorizationStatus}');
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
- **iOS / macOS / Web / Android 13+:** Must request permission before receiving FCM payloads.
|
|
113
|
+
- **Android < 13:** `authorizationStatus` returns `authorized` if the user has not disabled notifications in OS settings.
|
|
114
|
+
- **Android 13+:** Track permission requests in your app — there's no way to determine if the user chose to grant/deny.
|
|
115
|
+
- Use **provisional permissions** on iOS (`provisional: true`) to let users choose notification types after receiving their first notification.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 4. Token Management
|
|
120
|
+
|
|
121
|
+
**Get FCM registration token (use to send messages to a specific device):**
|
|
122
|
+
|
|
123
|
+
```dart
|
|
124
|
+
final fcmToken = await FirebaseMessaging.instance.getToken();
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Web — provide VAPID key:**
|
|
128
|
+
|
|
129
|
+
```dart
|
|
130
|
+
final fcmToken = await FirebaseMessaging.instance.getToken(
|
|
131
|
+
vapidKey: "BKagOny0KF_2pCJQ3m....moL0ewzQ8rZu"
|
|
132
|
+
);
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Listen for token refresh:**
|
|
136
|
+
|
|
137
|
+
```dart
|
|
138
|
+
FirebaseMessaging.instance.onTokenRefresh.listen((fcmToken) {
|
|
139
|
+
// Send updated token to your application server
|
|
140
|
+
}).onError((err) {
|
|
141
|
+
// Handle error
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Apple platforms — ensure APNS token is available before FCM calls:**
|
|
146
|
+
|
|
147
|
+
```dart
|
|
148
|
+
final apnsToken = await FirebaseMessaging.instance.getAPNSToken();
|
|
149
|
+
if (apnsToken != null) {
|
|
150
|
+
// Safe to make FCM plugin API requests
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## 5. Platform-Specific Behavior
|
|
157
|
+
|
|
158
|
+
- **iOS:** If the user swipes away the app from the app switcher, it must be **manually reopened** for background messages to work again.
|
|
159
|
+
- **Android:** If the user force-quits from device settings, the app must be **manually reopened**.
|
|
160
|
+
- **Android foreground notifications:** Create a **"High Priority"** notification channel to display notifications while the app is in the foreground.
|
|
161
|
+
- **iOS foreground notifications:** Update presentation options to display notifications while the app is in the foreground.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## 6. Auto-Initialization Control
|
|
166
|
+
|
|
167
|
+
**Disable auto-init — iOS** (`Info.plist`):
|
|
168
|
+
```
|
|
169
|
+
FirebaseMessagingAutoInitEnabled = NO
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
**Disable auto-init — Android** (`AndroidManifest.xml`):
|
|
173
|
+
```xml
|
|
174
|
+
<meta-data android:name="firebase_messaging_auto_init_enabled" android:value="false" />
|
|
175
|
+
<meta-data android:name="firebase_analytics_collection_enabled" android:value="false" />
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
**Re-enable at runtime:**
|
|
179
|
+
```dart
|
|
180
|
+
await FirebaseMessaging.instance.setAutoInitEnabled(true);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
- The auto-init setting **persists across app restarts** once set.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 7. iOS Image Notifications
|
|
188
|
+
|
|
189
|
+
> **Important:** The iOS simulator does **not** display images in push notifications. Test on a physical device.
|
|
190
|
+
|
|
191
|
+
- Add a **Notification Service Extension** in Xcode.
|
|
192
|
+
- Use `Messaging.serviceExtension().populateNotificationContent()` in the extension for image handling.
|
|
193
|
+
- Swift: add the `FirebaseMessaging` Swift package to your extension target.
|
|
194
|
+
- Objective-C: add the `Firebase/Messaging` pod to your Podfile.
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## References
|
|
199
|
+
|
|
200
|
+
- [Firebase Cloud Messaging Flutter documentation](https://firebase.google.com/docs/cloud-messaging/flutter/client)
|
|
201
|
+
- [Receive messages in Flutter](https://firebase.google.com/docs/cloud-messaging/flutter/receive)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: firebase-remote-config
|
|
3
|
+
description: Integrates Firebase Remote Config into Flutter apps. Use when setting up Remote Config, managing parameter defaults, fetching and activating values, implementing real-time updates, or handling throttling and testing.
|
|
4
|
+
routing: manual
|
|
5
|
+
stacks: [flutter]
|
|
6
|
+
source: https://github.com/evanca/flutter-ai-rules
|
|
7
|
+
license: MIT
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Firebase Remote Config Skill
|
|
11
|
+
|
|
12
|
+
This skill defines how to correctly use Firebase Remote Config in Flutter applications.
|
|
13
|
+
|
|
14
|
+
## When to Use
|
|
15
|
+
|
|
16
|
+
Use this skill when:
|
|
17
|
+
|
|
18
|
+
* Setting up Remote Config to configure your app without deploying updates.
|
|
19
|
+
* Managing parameter defaults and remote values.
|
|
20
|
+
* Fetching, activating, and listening to config updates.
|
|
21
|
+
* Applying throttling, A/B testing, or conditional targeting.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1. Setup and Configuration
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
flutter pub add firebase_remote_config
|
|
29
|
+
flutter pub add firebase_analytics # required for conditional targeting of app instances
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```dart
|
|
33
|
+
import 'package:firebase_remote_config/firebase_remote_config.dart';
|
|
34
|
+
|
|
35
|
+
final remoteConfig = FirebaseRemoteConfig.instance;
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
- Enable **Google Analytics** in your Firebase project for user property and audience targeting.
|
|
39
|
+
- Ensure the **Remote Config REST API** is not disabled — the SDK depends on it.
|
|
40
|
+
- For **macOS**, enable Keychain Sharing in Xcode.
|
|
41
|
+
|
|
42
|
+
**Configure settings:**
|
|
43
|
+
|
|
44
|
+
```dart
|
|
45
|
+
await remoteConfig.setConfigSettings(RemoteConfigSettings(
|
|
46
|
+
fetchTimeout: const Duration(minutes: 1),
|
|
47
|
+
minimumFetchInterval: const Duration(hours: 1),
|
|
48
|
+
));
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## 2. Parameter Management
|
|
54
|
+
|
|
55
|
+
**Set in-app defaults** (ensures your app behaves as intended before connecting to the backend):
|
|
56
|
+
|
|
57
|
+
```dart
|
|
58
|
+
await remoteConfig.setDefaults(const {
|
|
59
|
+
"example_param_1": 42,
|
|
60
|
+
"example_param_2": 3.14159,
|
|
61
|
+
"example_param_3": true,
|
|
62
|
+
"example_param_4": "Hello, world!",
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
- **Never** store confidential data in Remote Config keys or values — they can be accessed by end users.
|
|
67
|
+
- Use type-specific getters: `getBool()`, `getDouble()`, `getInt()`, `getString()`.
|
|
68
|
+
- Define parameters with the **same names** in the Firebase console as those in your app.
|
|
69
|
+
- Group related parameters with common prefixes (e.g., `login_timeout`, `login_attempts_max`).
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## 3. Fetching and Activating
|
|
74
|
+
|
|
75
|
+
```dart
|
|
76
|
+
await remoteConfig.fetchAndActivate();
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
- Use `fetchAndActivate()` to fetch and apply values in a single call.
|
|
80
|
+
- Alternatively, call `fetch()` then `activate()` separately to control when values take effect.
|
|
81
|
+
- Activate fetched values at appropriate times (e.g., app start) for a smooth user experience.
|
|
82
|
+
- Check `remoteConfig.lastFetchStatus` to determine if the fetch was successful, failed, or throttled.
|
|
83
|
+
- Handle fetch failures gracefully by falling back to default values.
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## 4. Real-time Updates
|
|
88
|
+
|
|
89
|
+
```dart
|
|
90
|
+
remoteConfig.onConfigUpdated.listen((event) async {
|
|
91
|
+
await remoteConfig.activate();
|
|
92
|
+
// Use the new config values here
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- Real-time Remote Config is **not available for Web**.
|
|
97
|
+
- Update your UI state when new configuration values are activated.
|
|
98
|
+
- Ensure real-time updates don't disrupt the user experience.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## 5. Throttling and Performance
|
|
103
|
+
|
|
104
|
+
- Fetch calls are **throttled** if an app fetches too frequently.
|
|
105
|
+
- Default minimum fetch interval in production: **12 hours**.
|
|
106
|
+
- For development, use a shorter interval — but only in debug builds:
|
|
107
|
+
|
|
108
|
+
```dart
|
|
109
|
+
await remoteConfig.setConfigSettings(RemoteConfigSettings(
|
|
110
|
+
fetchTimeout: const Duration(minutes: 1),
|
|
111
|
+
minimumFetchInterval: const Duration(minutes: 5),
|
|
112
|
+
));
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
- Be mindful of **service-side quota limits** with a large user base.
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## 6. Testing and Debugging
|
|
120
|
+
|
|
121
|
+
- Use **conditional values** in the Firebase console to test configurations without new app deployments.
|
|
122
|
+
- Implement **A/B testing** with different parameter values for different user segments.
|
|
123
|
+
- Test your app with both default and remote values.
|
|
124
|
+
- Verify graceful handling of configuration changes at runtime.
|
|
125
|
+
- Test **offline behavior** to ensure proper fallback to defaults.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
|
129
|
+
## References
|
|
130
|
+
|
|
131
|
+
- [Firebase Remote Config Flutter documentation](https://firebase.google.com/docs/remote-config/get-started?platform=flutter)
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: firebase-storage
|
|
3
|
+
description: Integrates Firebase Cloud Storage into Flutter apps. Use when setting up Storage, uploading or downloading files, managing metadata, handling errors, or applying security rules.
|
|
4
|
+
routing: manual
|
|
5
|
+
stacks: [flutter]
|
|
6
|
+
source: https://github.com/evanca/flutter-ai-rules
|
|
7
|
+
license: MIT
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# Firebase Cloud Storage Skill
|
|
11
|
+
|
|
12
|
+
This skill defines how to correctly use Firebase Cloud Storage in Flutter applications.
|
|
13
|
+
|
|
14
|
+
## When to Use
|
|
15
|
+
|
|
16
|
+
Use this skill when:
|
|
17
|
+
|
|
18
|
+
* Setting up Cloud Storage in a Flutter project.
|
|
19
|
+
* Uploading or downloading files.
|
|
20
|
+
* Managing file metadata.
|
|
21
|
+
* Handling Storage errors and monitoring upload progress.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## 1. Setup and Configuration
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
flutter pub add firebase_storage
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```dart
|
|
32
|
+
import 'package:firebase_storage/firebase_storage.dart';
|
|
33
|
+
|
|
34
|
+
final storage = FirebaseStorage.instance;
|
|
35
|
+
// Or specify a bucket explicitly:
|
|
36
|
+
// final storage = FirebaseStorage.instanceFor(bucket: "gs://BUCKET_NAME");
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
- Firebase Storage requires the **Blaze (pay-as-you-go) plan**.
|
|
40
|
+
- Run `flutterfire configure` to update your Firebase config with the default Storage bucket name.
|
|
41
|
+
|
|
42
|
+
> **Security note:** By default, a Cloud Storage bucket requires Firebase Authentication for any action. Configuring public access may also make App Engine files publicly accessible — restrict access again when you set up Authentication.
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 2. File Operations
|
|
47
|
+
|
|
48
|
+
**Create a reference:**
|
|
49
|
+
|
|
50
|
+
```dart
|
|
51
|
+
final storageRef = FirebaseStorage.instance.ref();
|
|
52
|
+
final fileRef = storageRef.child("uploads/file.jpg");
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Upload:**
|
|
56
|
+
|
|
57
|
+
```dart
|
|
58
|
+
final uploadTask = fileRef.putFile(file);
|
|
59
|
+
final snapshot = await uploadTask;
|
|
60
|
+
final downloadUrl = await snapshot.ref.getDownloadURL();
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Download (in-memory):**
|
|
64
|
+
|
|
65
|
+
```dart
|
|
66
|
+
final data = await fileRef.getData();
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Download URL:**
|
|
70
|
+
|
|
71
|
+
```dart
|
|
72
|
+
final downloadUrl = await fileRef.getDownloadURL();
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Delete:**
|
|
76
|
+
|
|
77
|
+
```dart
|
|
78
|
+
await fileRef.delete();
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## 3. Metadata Management
|
|
84
|
+
|
|
85
|
+
**Get metadata:**
|
|
86
|
+
|
|
87
|
+
```dart
|
|
88
|
+
final metadata = await fileRef.getMetadata();
|
|
89
|
+
print('Content type: ${metadata.contentType}');
|
|
90
|
+
print('Size: ${metadata.size}');
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Update metadata:**
|
|
94
|
+
|
|
95
|
+
```dart
|
|
96
|
+
final newMetadata = SettableMetadata(
|
|
97
|
+
contentType: "image/jpeg",
|
|
98
|
+
customMetadata: {'uploaded_by': 'user123'},
|
|
99
|
+
);
|
|
100
|
+
await fileRef.updateMetadata(newMetadata);
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Use **custom metadata** to store additional key/value pairs with your files.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 4. Error Handling
|
|
108
|
+
|
|
109
|
+
Use `try-catch` with `FirebaseException`. Key error codes:
|
|
110
|
+
|
|
111
|
+
| Code | Meaning |
|
|
112
|
+
|---|---|
|
|
113
|
+
| `storage/object-not-found` | File doesn't exist at the reference |
|
|
114
|
+
| `storage/bucket-not-found` | No bucket configured for Cloud Storage |
|
|
115
|
+
| `storage/project-not-found` | No project configured for Cloud Storage |
|
|
116
|
+
| `storage/quota-exceeded` | Storage quota exceeded |
|
|
117
|
+
| `storage/unauthenticated` | User needs to authenticate |
|
|
118
|
+
| `storage/unauthorized` | User lacks permission |
|
|
119
|
+
| `storage/retry-limit-exceeded` | Operation timeout exceeded |
|
|
120
|
+
|
|
121
|
+
- For `quota-exceeded` on the Spark plan, upgrade to Blaze.
|
|
122
|
+
- Implement retry logic for network-related errors and timeouts.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 5. Performance and Best Practices
|
|
127
|
+
|
|
128
|
+
**Monitor upload progress:**
|
|
129
|
+
|
|
130
|
+
```dart
|
|
131
|
+
final uploadTask = fileRef.putFile(file);
|
|
132
|
+
uploadTask.snapshotEvents.listen((TaskSnapshot snapshot) {
|
|
133
|
+
print('Progress: ${(snapshot.bytesTransferred / snapshot.totalBytes) * 100}%');
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- **Cancel** uploads with `uploadTask.cancel()`.
|
|
138
|
+
- **Pause / resume** with `uploadTask.pause()` and `uploadTask.resume()`.
|
|
139
|
+
- Optimize file sizes before upload to reduce costs and improve performance.
|
|
140
|
+
- Use Cloud Storage with Firestore for comprehensive data management.
|
|
141
|
+
- Use the **Firebase Local Emulator Suite** for local development and testing.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 6. Security
|
|
146
|
+
|
|
147
|
+
- Use **Firebase Security Rules** to control access to files.
|
|
148
|
+
- Combine Storage rules with **Firebase Authentication** for user-based access control.
|
|
149
|
+
- Test security rules thoroughly before deploying to production.
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## References
|
|
154
|
+
|
|
155
|
+
- [Firebase Cloud Storage Flutter documentation](https://firebase.google.com/docs/storage/flutter/start)
|
|
156
|
+
- [Upload files](https://firebase.google.com/docs/storage/flutter/upload-files)
|
|
157
|
+
- [Download files](https://firebase.google.com/docs/storage/flutter/download-files)
|
|
158
|
+
- [Handle errors](https://firebase.google.com/docs/storage/flutter/handle-errors)
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flutter-add-integration-test
|
|
3
|
+
description: Configures Flutter Driver for app interaction and converts MCP actions into permanent integration tests. Use when adding integration testing to a project, exploring UI components via MCP, or automating user flows with the integration_test package.
|
|
4
|
+
metadata:
|
|
5
|
+
model: models/gemini-3.1-pro-preview
|
|
6
|
+
last_modified: Tue, 21 Apr 2026 18:29:20 GMT
|
|
7
|
+
intents: [test]
|
|
8
|
+
stacks: [flutter]
|
|
9
|
+
source: https://github.com/flutter/skills
|
|
10
|
+
license: BSD-3-Clause
|
|
11
|
+
---
|
|
12
|
+
# Implementing Flutter Integration Tests
|
|
13
|
+
|
|
14
|
+
## Contents
|
|
15
|
+
- [Project Setup and Dependencies](#project-setup-and-dependencies)
|
|
16
|
+
- [Interactive Exploration via MCP](#interactive-exploration-via-mcp)
|
|
17
|
+
- [Test Authoring Guidelines](#test-authoring-guidelines)
|
|
18
|
+
- [Execution and Profiling](#execution-and-profiling)
|
|
19
|
+
- [Workflow: End-to-End Integration Testing](#workflow-end-to-end-integration-testing)
|
|
20
|
+
- [Examples](#examples)
|
|
21
|
+
|
|
22
|
+
## Project Setup and Dependencies
|
|
23
|
+
|
|
24
|
+
Configure the project to support integration testing and Flutter Driver extensions.
|
|
25
|
+
|
|
26
|
+
1. Add required development dependencies to `pubspec.yaml`:
|
|
27
|
+
```bash
|
|
28
|
+
flutter pub add 'dev:integration_test:{"sdk":"flutter"}'
|
|
29
|
+
flutter pub add 'dev:flutter_test:{"sdk":"flutter"}'
|
|
30
|
+
```
|
|
31
|
+
2. Enable the Flutter Driver extension in your application entry point (typically `lib/main.dart` or a dedicated `lib/main_test.dart`):
|
|
32
|
+
- Import `package:flutter_driver/driver_extension.dart`.
|
|
33
|
+
- Call `enableFlutterDriverExtension();` before `runApp()`.
|
|
34
|
+
3. Add `Key` parameters (e.g., `ValueKey('login_button')`) to critical widgets in the application code to ensure reliable targeting during tests.
|
|
35
|
+
|
|
36
|
+
## Interactive Exploration via MCP
|
|
37
|
+
|
|
38
|
+
Use the Dart/Flutter MCP server tools to interactively explore and manipulate the application state before writing static tests.
|
|
39
|
+
|
|
40
|
+
- **Launch**: Execute `launch_app` with `target: "lib/main_test.dart"` to start the application and acquire the DTD URI.
|
|
41
|
+
- **Inspect**: Execute `get_widget_tree` to discover available `Key`s, `Text` nodes, and widget `Type`s.
|
|
42
|
+
- **Interact**: Execute `tap`, `enter_text`, and `scroll` to simulate user flows.
|
|
43
|
+
- **Wait**: Always execute `waitFor` or verify state with `get_health` when navigating or triggering animations.
|
|
44
|
+
- **Troubleshoot Unmounted Widgets**: If a widget is not found in the tree, it may be lazily loaded in a `SliverList` or `ListView`. Execute `scroll` or `scrollIntoView` to force the widget to mount before interacting with it.
|
|
45
|
+
|
|
46
|
+
## Test Authoring Guidelines
|
|
47
|
+
|
|
48
|
+
Structure integration tests using the `flutter_test` API paradigm.
|
|
49
|
+
|
|
50
|
+
- Create a dedicated `integration_test/` directory at the project root.
|
|
51
|
+
- Name all test files using the `<name>_test.dart` convention.
|
|
52
|
+
- Initialize the binding by calling `IntegrationTestWidgetsFlutterBinding.ensureInitialized();` at the start of `main()`.
|
|
53
|
+
- Load the application UI using `await tester.pumpWidget(MyApp());`.
|
|
54
|
+
- Trigger frames and wait for animations to complete using `await tester.pumpAndSettle();` after interactions like `tester.tap()`.
|
|
55
|
+
- Assert widget visibility using `expect(find.byKey(ValueKey('foo')), findsOneWidget);` or `findsNothing`.
|
|
56
|
+
- Scroll to specific off-screen widgets using `await tester.scrollUntilVisible(itemFinder, 500.0, scrollable: listFinder);`.
|
|
57
|
+
|
|
58
|
+
**Conditional Logic for Legacy `flutter_driver`:**
|
|
59
|
+
- If maintaining or migrating legacy `flutter_driver` tests, use `driver.waitFor()`, `driver.waitForAbsent()`, `driver.tap()`, and `driver.scroll()` instead of the `WidgetTester` APIs.
|
|
60
|
+
|
|
61
|
+
## Execution and Profiling
|
|
62
|
+
|
|
63
|
+
Execute tests using the `flutter drive` command. Require a host driver script located in `test_driver/integration_test.dart` that calls `integrationDriver()`.
|
|
64
|
+
|
|
65
|
+
**Conditional Execution Targets:**
|
|
66
|
+
- **If testing on Chrome:** Launch `chromedriver --port=4444` in a separate terminal, then run:
|
|
67
|
+
`flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart -d chrome`
|
|
68
|
+
- **If testing headless web:** Run with `-d web-server`.
|
|
69
|
+
- **If testing on Android (Local):** Run `flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart`.
|
|
70
|
+
- **If testing on Firebase Test Lab (Android):**
|
|
71
|
+
1. Build debug APK: `flutter build apk --debug`
|
|
72
|
+
2. Build test APK: `./gradlew app:assembleAndroidTest`
|
|
73
|
+
3. Upload both APKs to the Firebase Test Lab console.
|
|
74
|
+
|
|
75
|
+
## Workflow: End-to-End Integration Testing
|
|
76
|
+
|
|
77
|
+
Copy and follow this checklist to implement and verify integration tests.
|
|
78
|
+
|
|
79
|
+
- [ ] **Task Progress: Setup**
|
|
80
|
+
- [ ] Add `integration_test` and `flutter_test` to `pubspec.yaml`.
|
|
81
|
+
- [ ] Inject `enableFlutterDriverExtension()` into the app entry point.
|
|
82
|
+
- [ ] Assign `ValueKey`s to target widgets.
|
|
83
|
+
- [ ] **Task Progress: Exploration**
|
|
84
|
+
- [ ] Run `launch_app` via MCP.
|
|
85
|
+
- [ ] Map the widget tree using `get_widget_tree`.
|
|
86
|
+
- [ ] Validate interaction paths using MCP tools (`tap`, `enter_text`).
|
|
87
|
+
- [ ] **Task Progress: Authoring**
|
|
88
|
+
- [ ] Create `integration_test/app_test.dart`.
|
|
89
|
+
- [ ] Write test cases using `WidgetTester` APIs.
|
|
90
|
+
- [ ] Create `test_driver/integration_test.dart` with `integrationDriver()`.
|
|
91
|
+
- [ ] **Task Progress: Execution & Feedback Loop**
|
|
92
|
+
- [ ] Run `flutter drive --driver=test_driver/integration_test.dart --target=integration_test/app_test.dart`.
|
|
93
|
+
- [ ] **Feedback Loop**: Review test output -> If `PumpAndSettleTimedOutException` occurs, check for infinite animations -> If widget not found, add `scrollUntilVisible` -> Re-run test until passing.
|
|
94
|
+
|
|
95
|
+
## Examples
|
|
96
|
+
|
|
97
|
+
### Standard Integration Test (`integration_test/app_test.dart`)
|
|
98
|
+
|
|
99
|
+
```dart
|
|
100
|
+
import 'package:flutter/material.dart';
|
|
101
|
+
import 'package:flutter_test/flutter_test.dart';
|
|
102
|
+
import 'package:integration_test/integration_test.dart';
|
|
103
|
+
import 'package:my_app/main.dart';
|
|
104
|
+
|
|
105
|
+
void main() {
|
|
106
|
+
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
|
107
|
+
|
|
108
|
+
group('End-to-end test', () {
|
|
109
|
+
testWidgets('tap on the floating action button, verify counter', (tester) async {
|
|
110
|
+
// Load app widget.
|
|
111
|
+
await tester.pumpWidget(const MyApp());
|
|
112
|
+
|
|
113
|
+
// Verify the counter starts at 0.
|
|
114
|
+
expect(find.text('0'), findsOneWidget);
|
|
115
|
+
|
|
116
|
+
// Find the floating action button to tap on.
|
|
117
|
+
final fab = find.byKey(const ValueKey('increment'));
|
|
118
|
+
|
|
119
|
+
// Emulate a tap on the floating action button.
|
|
120
|
+
await tester.tap(fab);
|
|
121
|
+
|
|
122
|
+
// Trigger a frame and wait for animations.
|
|
123
|
+
await tester.pumpAndSettle();
|
|
124
|
+
|
|
125
|
+
// Verify the counter increments by 1.
|
|
126
|
+
expect(find.text('1'), findsOneWidget);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Host Driver Script (`test_driver/integration_test.dart`)
|
|
133
|
+
|
|
134
|
+
```dart
|
|
135
|
+
import 'package:integration_test/integration_test_driver.dart';
|
|
136
|
+
|
|
137
|
+
Future<void> main() => integrationDriver();
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Performance Profiling Driver Script (`test_driver/perf_driver.dart`)
|
|
141
|
+
|
|
142
|
+
Use this driver script if you wrap your test actions in `binding.traceAction()` to capture performance metrics.
|
|
143
|
+
|
|
144
|
+
```dart
|
|
145
|
+
import 'package:flutter_driver/flutter_driver.dart' as driver;
|
|
146
|
+
import 'package:integration_test/integration_test_driver.dart';
|
|
147
|
+
|
|
148
|
+
Future<void> main() {
|
|
149
|
+
return integrationDriver(
|
|
150
|
+
responseDataCallback: (data) async {
|
|
151
|
+
if (data != null) {
|
|
152
|
+
final timeline = driver.Timeline.fromJson(
|
|
153
|
+
data['scrolling_timeline'] as Map<String, dynamic>,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
final summary = driver.TimelineSummary.summarize(timeline);
|
|
157
|
+
|
|
158
|
+
await summary.writeTimelineToFile(
|
|
159
|
+
'scrolling_timeline',
|
|
160
|
+
pretty: true,
|
|
161
|
+
includeSummary: true,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
);
|
|
166
|
+
}
|
|
167
|
+
```
|