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.
Files changed (105) hide show
  1. package/.claude-plugin/marketplace.json +24 -0
  2. package/.claude-plugin/plugin.json +12 -0
  3. package/CHANGELOG.md +103 -0
  4. package/LICENSE +21 -0
  5. package/README.md +631 -0
  6. package/THIRD_PARTY_NOTICES.md +105 -0
  7. package/agents/flutter-architect.md +92 -0
  8. package/agents/flutter-performance-engineer.md +103 -0
  9. package/agents/flutter-test-engineer.md +100 -0
  10. package/agents/lead-mobile.md +107 -0
  11. package/agents/mobile-code-reviewer.md +66 -0
  12. package/agents/mobile-staff-engineer.md +85 -0
  13. package/agents/plugin-native-expert.md +97 -0
  14. package/bin/reis-mobile.mjs +256 -0
  15. package/commands/debate.md +135 -0
  16. package/commands/doctor.md +23 -0
  17. package/commands/reis-mobile.md +50 -0
  18. package/commands/review.md +53 -0
  19. package/core/config/language.mjs +83 -0
  20. package/core/context/context-engine.mjs +113 -0
  21. package/core/detection/project-probe.mjs +104 -0
  22. package/core/detection/stack-detector.mjs +204 -0
  23. package/core/diagnostics/doctor.mjs +181 -0
  24. package/core/install/claude-plugin.mjs +76 -0
  25. package/core/paths.mjs +4 -0
  26. package/core/registry/frontmatter.mjs +81 -0
  27. package/core/registry/registry.mjs +140 -0
  28. package/core/router/intent-detector.mjs +75 -0
  29. package/core/router/intents.mjs +91 -0
  30. package/core/router/router.mjs +83 -0
  31. package/core/security/redact.mjs +24 -0
  32. package/package.json +35 -0
  33. package/skills/architecture-feature-first/SKILL.md +116 -0
  34. package/skills/bloc/SKILL.md +354 -0
  35. package/skills/code-review/SKILL.md +89 -0
  36. package/skills/dart-3-updates/SKILL.md +223 -0
  37. package/skills/dart-add-unit-test/SKILL.md +126 -0
  38. package/skills/dart-collect-coverage/SKILL.md +145 -0
  39. package/skills/dart-fix-runtime-errors/SKILL.md +170 -0
  40. package/skills/dart-generate-test-mocks/SKILL.md +159 -0
  41. package/skills/dart-migrate-to-checks-package/SKILL.md +532 -0
  42. package/skills/dart-resolve-package-conflicts/SKILL.md +120 -0
  43. package/skills/dart-run-static-analysis/SKILL.md +108 -0
  44. package/skills/dart-use-pattern-matching/SKILL.md +240 -0
  45. package/skills/dart-use-pattern-matching/examples/json_patterns.dart +85 -0
  46. package/skills/detect-mobile-stack/SKILL.md +33 -0
  47. package/skills/effective-dart/SKILL.md +187 -0
  48. package/skills/firebase-ai/SKILL.md +93 -0
  49. package/skills/firebase-analytics/SKILL.md +129 -0
  50. package/skills/firebase-app-check/SKILL.md +124 -0
  51. package/skills/firebase-auth/SKILL.md +224 -0
  52. package/skills/firebase-cloud-firestore/SKILL.md +165 -0
  53. package/skills/firebase-cloud-functions/SKILL.md +148 -0
  54. package/skills/firebase-crashlytics/SKILL.md +182 -0
  55. package/skills/firebase-data-connect/SKILL.md +80 -0
  56. package/skills/firebase-database/SKILL.md +224 -0
  57. package/skills/firebase-in-app-messaging/SKILL.md +111 -0
  58. package/skills/firebase-messaging/SKILL.md +201 -0
  59. package/skills/firebase-remote-config/SKILL.md +131 -0
  60. package/skills/firebase-storage/SKILL.md +158 -0
  61. package/skills/flutter-add-integration-test/SKILL.md +167 -0
  62. package/skills/flutter-add-widget-preview/SKILL.md +149 -0
  63. package/skills/flutter-add-widget-test/SKILL.md +158 -0
  64. package/skills/flutter-adding-home-screen-widgets/SKILL.md +205 -0
  65. package/skills/flutter-animating-apps/SKILL.md +185 -0
  66. package/skills/flutter-app-architecture/SKILL.md +80 -0
  67. package/skills/flutter-apply-architecture-best-practices/SKILL.md +166 -0
  68. package/skills/flutter-build-responsive-layout/SKILL.md +143 -0
  69. package/skills/flutter-building-forms/SKILL.md +125 -0
  70. package/skills/flutter-building-layouts/SKILL.md +131 -0
  71. package/skills/flutter-building-plugins/SKILL.md +209 -0
  72. package/skills/flutter-caching-data/SKILL.md +171 -0
  73. package/skills/flutter-change-notifier/SKILL.md +138 -0
  74. package/skills/flutter-embedding-native-views/SKILL.md +200 -0
  75. package/skills/flutter-errors/SKILL.md +126 -0
  76. package/skills/flutter-fix-layout-issues/SKILL.md +134 -0
  77. package/skills/flutter-handling-concurrency/SKILL.md +182 -0
  78. package/skills/flutter-implement-json-serialization/SKILL.md +157 -0
  79. package/skills/flutter-improving-accessibility/SKILL.md +113 -0
  80. package/skills/flutter-interoperating-with-native-apis/SKILL.md +130 -0
  81. package/skills/flutter-login-usecase/SKILL.md +38 -0
  82. package/skills/flutter-managing-state/SKILL.md +206 -0
  83. package/skills/flutter-project-audit/SKILL.md +66 -0
  84. package/skills/flutter-reducing-app-size/SKILL.md +103 -0
  85. package/skills/flutter-secure-token-store/SKILL.md +35 -0
  86. package/skills/flutter-setting-up-on-macos/SKILL.md +78 -0
  87. package/skills/flutter-setup-declarative-routing/SKILL.md +259 -0
  88. package/skills/flutter-setup-localization/SKILL.md +214 -0
  89. package/skills/flutter-theming-apps/SKILL.md +163 -0
  90. package/skills/flutter-use-http-package/SKILL.md +178 -0
  91. package/skills/flutter-widget-review/SKILL.md +54 -0
  92. package/skills/flutter-working-with-databases/SKILL.md +203 -0
  93. package/skills/flutterfire-configure/SKILL.md +143 -0
  94. package/skills/mobile-security-audit/SKILL.md +101 -0
  95. package/skills/mockito/SKILL.md +149 -0
  96. package/skills/mocktail/SKILL.md +122 -0
  97. package/skills/patrol-e2e-testing/SKILL.md +224 -0
  98. package/skills/provider/SKILL.md +139 -0
  99. package/skills/riverpod/SKILL.md +246 -0
  100. package/skills/testing/SKILL.md +50 -0
  101. package/stacks/android/stack.json +8 -0
  102. package/stacks/flutter/stack.json +8 -0
  103. package/stacks/ios/stack.json +8 -0
  104. package/stacks/kotlin-multiplatform/stack.json +8 -0
  105. package/stacks/react-native/stack.json +8 -0
@@ -0,0 +1,178 @@
1
+ ---
2
+ name: flutter-use-http-package
3
+ description: Use the `http` package to execute GET, POST, PUT, or DELETE requests. Use when you need to fetch from or send data to a REST API.
4
+ metadata:
5
+ model: models/gemini-3.1-pro-preview
6
+ last_modified: Tue, 21 Apr 2026 21:36:42 GMT
7
+ routing: manual
8
+ stacks: [flutter]
9
+ source: https://github.com/flutter/skills
10
+ license: BSD-3-Clause
11
+ ---
12
+ # Implementing Flutter Networking
13
+
14
+ ## Contents
15
+ - [Configuration & Permissions](#configuration--permissions)
16
+ - [Request Execution & Response Handling](#request-execution--response-handling)
17
+ - [Background Parsing](#background-parsing)
18
+ - [Workflow: Executing Network Operations](#workflow-executing-network-operations)
19
+ - [Examples](#examples)
20
+
21
+ ## Configuration & Permissions
22
+
23
+ Configure the environment and platform-specific permissions required for network access.
24
+
25
+ 1. Add the `http` package dependency via the terminal:
26
+ ```bash
27
+ flutter pub add http
28
+ ```
29
+ 2. Import the package in your Dart files:
30
+ ```dart
31
+ import 'package:http/http.dart' as http;
32
+ ```
33
+ 3. Configure Android permissions by adding the Internet permission to `android/app/src/main/AndroidManifest.xml`:
34
+ ```xml
35
+ <uses-permission android:name="android.permission.INTERNET" />
36
+ ```
37
+ 4. Configure macOS entitlements by adding the network client key to both `macos/Runner/DebugProfile.entitlements` and `macos/Runner/Release.entitlements`:
38
+ ```xml
39
+ <key>com.apple.security.network.client</key>
40
+ <true/>
41
+ ```
42
+
43
+ ## Request Execution & Response Handling
44
+
45
+ Execute HTTP operations and map responses to strongly typed Dart objects.
46
+
47
+ * **URIs:** Always parse URL strings using `Uri.parse('your_url')`.
48
+ * **Headers:** Inject authorization and content-type headers via the `headers` parameter map. Use `HttpHeaders.authorizationHeader` for auth tokens.
49
+ * **Payloads:** For POST and PUT requests, encode the body using `jsonEncode()` from `dart:convert`.
50
+ * **Status Validation:** Evaluate `response.statusCode`. Treat `200 OK` (GET/PUT/DELETE) and `201 CREATED` (POST) as success.
51
+ * **Error Handling:** Throw explicit exceptions for non-success status codes. Never return `null` on failure, as this prevents `FutureBuilder` from triggering its error state and causes infinite loading indicators.
52
+ * **Deserialization:** Parse the raw string using `jsonDecode(response.body)` and map it to a custom Dart object using a factory constructor (e.g., `fromJson`).
53
+
54
+ ## Background Parsing
55
+
56
+ Offload expensive JSON parsing to a separate Isolate to prevent UI jank (frame drops).
57
+
58
+ * Import `package:flutter/foundation.dart`.
59
+ * Use the `compute()` function to run the parsing logic in a background isolate.
60
+ * Ensure the parsing function passed to `compute()` is a top-level function or a static method, as closures or instance methods cannot be passed across isolates.
61
+
62
+ ## Workflow: Executing Network Operations
63
+
64
+ Use the following checklist to implement and validate network operations.
65
+
66
+ **Task Progress:**
67
+ - [ ] 1. Define the strongly typed Dart model with a `fromJson` factory constructor.
68
+ - [ ] 2. Implement the network request method returning a `Future<Model>`.
69
+ - [ ] 3. Apply conditional logic based on the operation type:
70
+ - **If fetching data (GET):** Append query parameters to the URI.
71
+ - **If mutating data (POST/PUT):** Set `'Content-Type': 'application/json; charset=UTF-8'` and attach the `jsonEncode` body.
72
+ - **If deleting data (DELETE):** Return an empty model instance on success (`200 OK`).
73
+ - [ ] 4. Validate the `statusCode` and throw an `Exception` on failure.
74
+ - [ ] 5. Integrate the `Future` into the UI using `FutureBuilder`.
75
+ - [ ] 6. Handle `snapshot.hasData`, `snapshot.hasError`, and default to a `CircularProgressIndicator`.
76
+ - [ ] 7. **Feedback Loop:** Run the app -> trigger the network request -> review console for unhandled exceptions -> fix parsing or permission errors.
77
+
78
+ ## Examples
79
+
80
+ ### High-Fidelity Implementation: Fetching and Parsing in the Background
81
+
82
+ ```dart
83
+ import 'dart:async';
84
+ import 'dart:convert';
85
+ import 'dart:io';
86
+ import 'package:flutter/foundation.dart';
87
+ import 'package:flutter/material.dart';
88
+ import 'package:http/http.dart' as http;
89
+
90
+ // 1. Top-level parsing function for Isolate
91
+ List<Photo> parsePhotos(String responseBody) {
92
+ final parsed = (jsonDecode(responseBody) as List<Object?>)
93
+ .cast<Map<String, Object?>>();
94
+ return parsed.map<Photo>(Photo.fromJson).toList();
95
+ }
96
+
97
+ // 2. Network execution with background parsing
98
+ Future<List<Photo>> fetchPhotos() async {
99
+ final response = await http.get(
100
+ Uri.parse('https://jsonplaceholder.typicode.com/photos'),
101
+ headers: {
102
+ HttpHeaders.authorizationHeader: 'Bearer your_token_here',
103
+ HttpHeaders.acceptHeader: 'application/json',
104
+ },
105
+ );
106
+
107
+ if (response.statusCode == 200) {
108
+ // Offload heavy parsing to a background isolate
109
+ return compute(parsePhotos, response.body);
110
+ } else {
111
+ throw Exception('Failed to load photos. Status: ${response.statusCode}');
112
+ }
113
+ }
114
+
115
+ // 3. Strongly typed model
116
+ class Photo {
117
+ final int id;
118
+ final String title;
119
+ final String thumbnailUrl;
120
+
121
+ const Photo({
122
+ required this.id,
123
+ required this.title,
124
+ required this.thumbnailUrl,
125
+ });
126
+
127
+ factory Photo.fromJson(Map<String, dynamic> json) {
128
+ return Photo(
129
+ id: json['id'] as int,
130
+ title: json['title'] as String,
131
+ thumbnailUrl: json['thumbnailUrl'] as String,
132
+ );
133
+ }
134
+ }
135
+
136
+ // 4. UI Integration
137
+ class PhotoGallery extends StatefulWidget {
138
+ const PhotoGallery({super.key});
139
+
140
+ @override
141
+ State<PhotoGallery> createState() => _PhotoGalleryState();
142
+ }
143
+
144
+ class _PhotoGalleryState extends State<PhotoGallery> {
145
+ late Future<List<Photo>> _futurePhotos;
146
+
147
+ @override
148
+ void initState() {
149
+ super.initState();
150
+ // Initialize Future once to prevent re-fetching on rebuilds
151
+ _futurePhotos = fetchPhotos();
152
+ }
153
+
154
+ @override
155
+ Widget build(BuildContext context) {
156
+ return FutureBuilder<List<Photo>>(
157
+ future: _futurePhotos,
158
+ builder: (context, snapshot) {
159
+ if (snapshot.hasData) {
160
+ final photos = snapshot.data!;
161
+ return ListView.builder(
162
+ itemCount: photos.length,
163
+ itemBuilder: (context, index) => ListTile(
164
+ leading: Image.network(photos[index].thumbnailUrl),
165
+ title: Text(photos[index].title),
166
+ ),
167
+ );
168
+ } else if (snapshot.hasError) {
169
+ return Center(child: Text('Error: ${snapshot.error}'));
170
+ }
171
+
172
+ // Default loading state
173
+ return const Center(child: CircularProgressIndicator());
174
+ },
175
+ );
176
+ }
177
+ }
178
+ ```
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: flutter-widget-review
3
+ description: Reviews Flutter widget code for lifecycle bugs, BuildContext misuse across async gaps, missing dispose, side effects in build, unnecessary rebuilds, list and image performance, layout overflow risks and accessibility. Use when reviewing Dart files under lib/ that contain widgets, screens or pages, or when the user asks to review Flutter UI code.
4
+ intents: [review, performance]
5
+ stacks: [flutter]
6
+ ---
7
+
8
+ # Flutter Widget Review
9
+
10
+ Apply to Dart files with widgets (`StatelessWidget`, `StatefulWidget`, `ConsumerWidget`, `HookWidget`...). Items are ordered by impact: the first ones cause crashes or visible bugs.
11
+
12
+ ## 1. Lifecycle and async (crash / bug)
13
+
14
+ - [ ] **`BuildContext` after `await`.** Using `context` (`Navigator.of`, `ScaffoldMessenger.of`, `Theme.of`) after an `await` without checking `if (!context.mounted) return;` (or `mounted` in a `State`). The `use_build_context_synchronously` lint covers some of the cases.
15
+ - [ ] **`setState` after `dispose`.** `Future`, `Stream` or `Timer` callbacks that call `setState` without checking `mounted`.
16
+ - [ ] **Resources without `dispose`.** `TextEditingController`, `AnimationController`, `ScrollController`, `PageController`, `FocusNode`, `StreamSubscription`, `Timer` and `ChangeNotifier` created in the `State` must be released in `dispose()`.
17
+ - [ ] **Controller created inside `build`.** Recreates the controller on every rebuild and loses state (typed text, scroll position).
18
+ - [ ] **`Future` created inside `build`.** `FutureBuilder(future: api.fetch())` repeats the request on every rebuild. The `Future` should be created in `initState`, in the state management layer or memoized.
19
+ - [ ] **Side effects in `build`.** Navigation, `showDialog`, analytics or network calls inside `build`. They belong in listeners (`BlocListener`, `ref.listen`) or in `addPostFrameCallback`.
20
+
21
+ ## 2. State and identity
22
+
23
+ - [ ] Reorderable or removable list items without a stable `Key` (`ValueKey(item.id)`): state ends up on the wrong item.
24
+ - [ ] `GlobalKey` created inside `build`.
25
+ - [ ] Business state kept in a `StatefulWidget` when the project already uses a state manager.
26
+
27
+ ## 3. Rendering performance
28
+
29
+ - [ ] Missing `const` constructors in static subtrees (only report in widgets that rebuild often).
30
+ - [ ] Long lists with `ListView(children: [...])` / `Column` inside `SingleChildScrollView`: use `ListView.builder` or `SliverList`.
31
+ - [ ] `shrinkWrap: true` on a large list inside another scroll view: forces layout of every item.
32
+ - [ ] Rebuild scope too large: `setState`, `BlocBuilder` or `Consumer` at the top of the screen when only one part changes. Use `buildWhen`, `select` or extract the widget.
33
+ - [ ] `MediaQuery.of(context)` used only for the size: `MediaQuery.sizeOf(context)` rebuilds less. Confirm the Flutter version in `pubspec.lock` before suggesting it.
34
+ - [ ] Network images without `cacheWidth`/`cacheHeight` or without caching, decoded at full resolution to display thumbnails.
35
+ - [ ] Animated `Opacity` or `ClipRRect`/`BackdropFilter` in list items: prefer `FadeTransition`/`AnimatedOpacity` and avoid unnecessary clipping.
36
+ - [ ] Heavy work (parsing large JSON, cryptography, image processing) on the UI thread: use `compute`/`Isolate.run`.
37
+
38
+ ## 4. Layout
39
+
40
+ - [ ] `Row` with `Text` without `Expanded`/`Flexible`: overflow with long text or a larger system font.
41
+ - [ ] Fixed height or width for text content: breaks with a high `textScaler`.
42
+ - [ ] Missing `SafeArea` on screens without `Scaffold`/`AppBar`.
43
+ - [ ] Keyboard covering fields: form outside a scroll view.
44
+
45
+ ## 5. Accessibility and i18n
46
+
47
+ - [ ] `IconButton` and `GestureDetector` without `tooltip` or `Semantics(label: ...)`.
48
+ - [ ] Touch target smaller than 48x48 dp.
49
+ - [ ] Hardcoded visible strings when the project already uses `intl`/`AppLocalizations`.
50
+ - [ ] Information conveyed by color alone.
51
+
52
+ ## Output
53
+
54
+ Report in the appropriate section of the report (Bugs for section 1, Performance for section 3...), always with `file:line` and the corrected snippet when the fix is short.
@@ -0,0 +1,203 @@
1
+ ---
2
+ name: flutter-working-with-databases
3
+ description: Manages local data persistence using SQLite or other database solutions. Use when a Flutter app needs to store, query, or synchronize large amounts of structured data on the device.
4
+ metadata:
5
+ model: models/gemini-3.1-pro-preview
6
+ last_modified: Thu, 12 Mar 2026 22:19:15 GMT
7
+ routing: manual
8
+ stacks: [flutter]
9
+ source: https://github.com/flutter/skills
10
+ license: BSD-3-Clause
11
+ ---
12
+ # Architecting the Data Layer
13
+
14
+ ## Contents
15
+ - [Core Architecture](#core-architecture)
16
+ - [Services Implementation](#services-implementation)
17
+ - [Repository Implementation](#repository-implementation)
18
+ - [Caching Strategies](#caching-strategies)
19
+ - [Workflows](#workflows)
20
+ - [Examples](#examples)
21
+
22
+ ## Core Architecture
23
+
24
+ Construct the data layer as the Single Source of Truth (SSOT) for all application data. In an MVVM architecture, the data layer represents the Model. Never update application data outside of this layer.
25
+
26
+ Separate the data layer into two distinct components: **Repositories** and **Services**.
27
+
28
+ ### Repositories
29
+ * Act as the SSOT for a specific domain entity.
30
+ * Contain business logic for data mutation, polling, caching, and offline synchronization.
31
+ * Transform raw data models (API/DB models) into Domain Models (clean data classes containing only what the UI needs).
32
+ * Inject Services as private members to prevent the UI layer from bypassing the repository.
33
+
34
+ ### Services
35
+ * Act as stateless wrappers around external data sources (HTTP clients, SQLite databases, platform plugins).
36
+ * Perform no business logic or data transformation beyond basic JSON serialization.
37
+ * Return raw data models or `Result` wrappers to the calling repository.
38
+
39
+ ## Services Implementation
40
+
41
+ ### Database Services (SQLite)
42
+ Use databases to persist and query large amounts of structured data locally.
43
+ * Add `sqflite` and `path` packages to `pubspec.yaml`.
44
+ * Use the `path` package to define the storage location on disk safely across platforms.
45
+ * Define table schemas using constants to prevent typos.
46
+ * Use `id` as the primary key with `AUTOINCREMENT` to improve query and update times.
47
+ * Always use `whereArgs` in SQL queries to prevent SQL injection (e.g., `where: 'id = ?', whereArgs: [id]`).
48
+
49
+ ### API Services
50
+ * Wrap HTTP calls (e.g., using the `http` package) in dedicated client classes.
51
+ * Return asynchronous response objects (`Future` or `Stream`).
52
+ * Handle raw JSON serialization at this level, returning API-specific data models.
53
+
54
+ ## Repository Implementation
55
+
56
+ ### Domain Models
57
+ * Define immutable data classes (using `freezed` or `built_value`) for Domain Models.
58
+ * Strip out backend-specific fields (like metadata or pagination tokens) that the UI does not need.
59
+
60
+ ### Offline-First Synchronization
61
+ Combine local and remote data sources within the repository to provide seamless offline support.
62
+
63
+ * **If reading data:** Return a `Stream` that immediately yields the cached local data from the Database Service, performs the network request via the API Service, updates the Database Service, and then yields the fresh data.
64
+ * **If writing data (Online-only):** Attempt the API Service mutation first. If successful, update the Database Service.
65
+ * **If writing data (Offline-first):** Update the Database Service immediately. Attempt the API Service mutation. If the network fails, flag the local database record as `synchronized: false` and queue a background synchronization task.
66
+
67
+ ## Caching Strategies
68
+
69
+ Select the appropriate caching strategy based on the data payload:
70
+ * **Small Key-Value Data:** Use `shared_preferences` for simple app configurations, theme settings, or user preferences.
71
+ * **Large Datasets:** Use relational (`sqflite`, `drift`) or non-relational (`hive_ce`, `isar_community`) on-device databases.
72
+ * **Images:** Use the `cached_network_image` package to automatically cache remote images to the device's file system.
73
+ * **API Responses:** Implement lightweight remote caching within the API Service or Repository using in-memory maps or temporary file storage.
74
+
75
+ ## Workflows
76
+
77
+ ### Workflow: Implementing a New Data Feature
78
+ Copy and track this checklist when adding a new data entity to the application.
79
+
80
+ - [ ] **Task Progress**
81
+ - [ ] Define the Domain Model (immutable, UI-focused).
82
+ - [ ] Define the API/DB Models (raw data structures).
83
+ - [ ] Create or update the Service(s) to handle raw data fetching/storage.
84
+ - [ ] Create the Repository interface (abstract class).
85
+ - [ ] Implement the Repository, injecting the required Service(s) as private dependencies.
86
+ - [ ] Map raw Service models to the Domain Model within the Repository.
87
+ - [ ] Expose Repository methods to the View Model.
88
+ - [ ] Run validator -> review errors -> fix.
89
+
90
+ ### Workflow: Implementing SQLite Persistence
91
+ Follow this sequence to add a new SQLite table and integrate it.
92
+
93
+ - [ ] **Task Progress**
94
+ - [ ] Add `sqflite` and `path` dependencies.
95
+ - [ ] Define table name and column constants.
96
+ - [ ] Update the `onCreate` or `onUpgrade` method in the Database Service to execute the `CREATE TABLE` statement.
97
+ - [ ] Implement `insert`, `query`, `update`, and `delete` methods in the Database Service.
98
+ - [ ] Inject the Database Service into the target Repository.
99
+ - [ ] Ensure the Repository calls `database.open()` before executing queries.
100
+
101
+ ## Examples
102
+
103
+ ### Offline-First Repository Implementation
104
+ This example demonstrates a Repository coordinating between a Database Service and an API Service using a Stream for offline-first reads.
105
+
106
+ ```dart
107
+ import 'dart:async';
108
+
109
+ class TodoRepository {
110
+ TodoRepository({
111
+ required DatabaseService databaseService,
112
+ required ApiClientService apiClientService,
113
+ }) : _databaseService = databaseService,
114
+ _apiClientService = apiClientService;
115
+
116
+ final DatabaseService _databaseService;
117
+ final ApiClientService _apiClientService;
118
+
119
+ /// Yields local data immediately, then fetches remote data, updates local, and yields fresh data.
120
+ Stream<List<Todo>> observeTodos() async* {
121
+ // 1. Yield local cached data first
122
+ final localTodos = await _databaseService.getAllTodos();
123
+ if (localTodos.isNotEmpty) {
124
+ yield localTodos.map((model) => Todo.fromDbModel(model)).toList();
125
+ }
126
+
127
+ try {
128
+ // 2. Fetch fresh data from API
129
+ final remoteTodos = await _apiClientService.fetchTodos();
130
+
131
+ // 3. Update local database
132
+ await _databaseService.replaceAllTodos(remoteTodos);
133
+
134
+ // 4. Yield fresh data
135
+ yield remoteTodos.map((model) => Todo.fromApiModel(model)).toList();
136
+ } on Exception catch (e) {
137
+ // Handle network errors (UI will still have local data)
138
+ // Log error or yield a specific error state if required
139
+ }
140
+ }
141
+
142
+ /// Offline-first write: Save locally, then attempt remote sync.
143
+ Future<void> createTodo(Todo todo) async {
144
+ final dbModel = todo.toDbModel().copyWith(isSynced: false);
145
+
146
+ // 1. Save locally immediately
147
+ await _databaseService.insertTodo(dbModel);
148
+
149
+ try {
150
+ // 2. Attempt remote sync
151
+ final apiModel = await _apiClientService.postTodo(todo.toApiModel());
152
+
153
+ // 3. Mark as synced locally
154
+ await _databaseService.updateTodo(
155
+ dbModel.copyWith(id: apiModel.id, isSynced: true)
156
+ );
157
+ } on Exception catch (_) {
158
+ // Leave as isSynced: false for background sync task to pick up later
159
+ }
160
+ }
161
+ }
162
+ ```
163
+
164
+ ### SQLite Database Service Implementation
165
+ Demonstrates safe query construction using `whereArgs`.
166
+
167
+ ```dart
168
+ class DatabaseService {
169
+ static const String _tableName = 'todos';
170
+ static const String _colId = 'id';
171
+ static const String _colTask = 'task';
172
+ static const String _colIsSynced = 'is_synced';
173
+
174
+ Database? _database;
175
+
176
+ Future<void> open() async {
177
+ if (_database != null) return;
178
+
179
+ final dbPath = join(await getDatabasesPath(), 'app_database.db');
180
+ _database = await openDatabase(
181
+ dbPath,
182
+ version: 1,
183
+ onCreate: (db, version) {
184
+ return db.execute(
185
+ 'CREATE TABLE $_tableName('
186
+ '$_colId INTEGER PRIMARY KEY AUTOINCREMENT, '
187
+ '$_colTask TEXT, '
188
+ '$_colIsSynced INTEGER)'
189
+ );
190
+ },
191
+ );
192
+ }
193
+
194
+ Future<void> updateTodo(TodoDbModel todo) async {
195
+ await _database!.update(
196
+ _tableName,
197
+ todo.toMap(),
198
+ where: '$_colId = ?',
199
+ whereArgs: [todo.id], // Prevents SQL injection
200
+ );
201
+ }
202
+ }
203
+ ```
@@ -0,0 +1,143 @@
1
+ ---
2
+ name: flutterfire-configure
3
+ description: Sets up Firebase for Flutter apps using FlutterFire CLI. Use when initializing a Firebase project, running flutterfire configure, initializing Firebase in main.dart, or configuring multiple app flavors.
4
+ routing: manual
5
+ stacks: [flutter]
6
+ source: https://github.com/evanca/flutter-ai-rules
7
+ license: MIT
8
+ ---
9
+
10
+ # FlutterFire Configure Skill
11
+
12
+ This skill defines how to correctly set up and configure Firebase for Flutter applications.
13
+
14
+ ## When to Use
15
+
16
+ Use this skill when:
17
+
18
+ * Adding Firebase to a Flutter project for the first time.
19
+ * Running `flutterfire configure` after adding a new Firebase service or platform.
20
+ * Initializing Firebase in `main.dart`.
21
+ * Setting up separate Firebase projects for multiple app flavors.
22
+
23
+ ---
24
+
25
+ ## 1. Prerequisites
26
+
27
+ Install the required tools:
28
+
29
+ ```bash
30
+ npm install -g firebase-tools
31
+ firebase login
32
+ dart pub global activate flutterfire_cli
33
+ ```
34
+
35
+ **Minimum platform requirements:**
36
+ - Android: API level 19 (KitKat) or higher
37
+ - Apple: iOS 11 or higher
38
+
39
+ ---
40
+
41
+ ## 2. Setup and Configuration
42
+
43
+ ```bash
44
+ # From your Flutter project directory:
45
+ flutterfire configure
46
+
47
+ # Add the core Firebase package:
48
+ flutter pub add firebase_core
49
+ ```
50
+
51
+ - Re-run `flutterfire configure` any time you **add support for a new platform** or **start using a new Firebase service**.
52
+ - For Android-specific services (Crashlytics, Performance Monitoring), the FlutterFire CLI automatically adds the required Gradle plugins.
53
+ - Rebuild with `flutter run` after adding new Firebase plugins.
54
+
55
+ ---
56
+
57
+ ## 3. Firebase Initialization
58
+
59
+ ```dart
60
+ import 'package:firebase_core/firebase_core.dart';
61
+ import 'firebase_options.dart';
62
+
63
+ void main() async {
64
+ WidgetsFlutterBinding.ensureInitialized();
65
+ await Firebase.initializeApp(
66
+ options: DefaultFirebaseOptions.currentPlatform,
67
+ );
68
+ runApp(const MyApp());
69
+ }
70
+ ```
71
+
72
+ - Call `WidgetsFlutterBinding.ensureInitialized()` **before** Firebase initialization.
73
+ - Place Firebase initialization **before** any other Firebase service calls.
74
+ - **Never modify** `firebase_options.dart` manually — it is auto-generated.
75
+ - **Commit** `firebase_options.dart` to version control — it contains non-secret configuration identifiers.
76
+ - For Firebase Emulator Suite: `await Firebase.initializeApp(demoProjectId: "demo-project-id")`.
77
+
78
+ ---
79
+
80
+ ## 4. Best Practices
81
+
82
+ - Enable **Firebase Analytics** for optimal experience with Crashlytics, Remote Config, and other products.
83
+ - Use a **consistent Firebase project** across all platforms for data consistency.
84
+ - For iOS/macOS apps using certain Firebase services, add the **Keychain Sharing** capability in Xcode.
85
+ - Test your Firebase configuration with **both debug and release** builds.
86
+ - Check **version compatibility** between Flutter plugins and the underlying Firebase SDK.
87
+
88
+ ---
89
+
90
+ ## 5. Multiple App Flavors
91
+
92
+ Create separate Firebase projects per environment (development, staging, production):
93
+
94
+ ```bash
95
+ flutterfire config \
96
+ --project=flutter-app-dev \
97
+ --out=lib/firebase_options_dev.dart \
98
+ --ios-bundle-id=com.example.flutterApp.dev \
99
+ --ios-out=ios/flavors/dev/GoogleService-Info.plist \
100
+ --android-package-name=com.example.flutter_app.dev \
101
+ --android-out=android/app/src/dev/google-services.json
102
+ ```
103
+
104
+ **Centralize Firebase initialization by flavor:**
105
+
106
+ ```dart
107
+ // firebase.dart
108
+ import 'package:firebase_core/firebase_core.dart';
109
+ import 'package:flutter/services.dart';
110
+ import 'package:flutter_app/firebase_options_prod.dart' as prod;
111
+ import 'package:flutter_app/firebase_options_stg.dart' as stg;
112
+ import 'package:flutter_app/firebase_options_dev.dart' as dev;
113
+
114
+ Future<void> initializeFirebaseApp() async {
115
+ final firebaseOptions = switch (appFlavor) {
116
+ 'prod' => prod.DefaultFirebaseOptions.currentPlatform,
117
+ 'stg' => stg.DefaultFirebaseOptions.currentPlatform,
118
+ 'dev' => dev.DefaultFirebaseOptions.currentPlatform,
119
+ _ => throw UnsupportedError('Invalid flavor: $appFlavor'),
120
+ };
121
+ await Firebase.initializeApp(options: firebaseOptions);
122
+ }
123
+ ```
124
+
125
+ ```dart
126
+ // main.dart
127
+ void main() async {
128
+ WidgetsFlutterBinding.ensureInitialized();
129
+ await initializeFirebaseApp();
130
+ runApp(const MainApp());
131
+ }
132
+ ```
133
+
134
+ - Use `appFlavor` or environment variables to select the configuration at runtime.
135
+ - Import each flavor's config with **namespace aliases** (e.g., `as dev`).
136
+ - Use a helper script to automate multi-flavor configuration.
137
+
138
+ ---
139
+
140
+ ## References
141
+
142
+ - [Add Firebase to your Flutter app](https://firebase.google.com/docs/flutter/setup)
143
+ - [FlutterFire CLI](https://firebase.flutter.dev/docs/cli)
@@ -0,0 +1,101 @@
1
+ ---
2
+ name: mobile-security-audit
3
+ description: Security audit for mobile apps based on the OWASP MASVS categories — insecure token storage, hardcoded secrets, cleartext traffic, TLS validation bypass, exported components, WebView exposure, sensitive logging and release hardening — with concrete checks for Flutter, Android, iOS and React Native. Use when reviewing mobile code, auditing app security, handling authentication or tokens, or preparing a release.
4
+ intents: [review, security, release]
5
+ stacks: ["*"]
6
+ ---
7
+
8
+ # Mobile Security Audit
9
+
10
+ Organized by the OWASP MASVS categories. Apply the general section and then the one for the detected stack (in cross-platform apps, also apply Android and iOS to the native folders).
11
+
12
+ Every finding needs evidence in the code or configuration. Severity:
13
+
14
+ - **Critical:** exposed credential or sensitive data, TLS validation disabled, exported component that performs a privileged action.
15
+ - **High:** token in insecure storage, cleartext traffic to the API, WebView with a JavaScript bridge exposed to remote content.
16
+ - **Medium/Low:** missing hardening, verbose logs, backup enabled without need.
17
+
18
+ ## General (all stacks)
19
+
20
+ **MASVS-STORAGE**
21
+ - [ ] Access token, refresh token and personal data kept in insecure storage (preferences, files, unencrypted database).
22
+ - [ ] Sensitive data in logs, analytics or crash reports.
23
+
24
+ **MASVS-CRYPTO**
25
+ - [ ] Private API keys, client secrets, signing keys or passwords in source code, assets or bundled configuration files. Everything shipped in the app can be extracted; a real secret stays on the backend.
26
+ - [ ] Homemade cryptography, fixed IV/key, MD5/SHA-1 for passwords.
27
+
28
+ **MASVS-AUTH**
29
+ - [ ] Authorization decided only on the client (local `isAdmin` flag, hiding a button as access control).
30
+ - [ ] Session without expiration or refresh token never invalidated on logout.
31
+ - [ ] Biometrics used only as a local boolean, without binding to a cryptographic key.
32
+
33
+ **MASVS-NETWORK**
34
+ - [ ] `http://` URLs for APIs.
35
+ - [ ] Certificate validation disabled.
36
+ - [ ] Pinning, if present, without a rotation strategy (single pin with no backup).
37
+
38
+ **MASVS-PLATFORM**
39
+ - [ ] Deep links and universal links that perform actions without validating parameters or without authentication.
40
+ - [ ] WebView loading remote content with JavaScript and a native bridge enabled.
41
+ - [ ] Sensitive data on the clipboard or visible in the app switcher snapshot.
42
+
43
+ **MASVS-CODE / RESILIENCE**
44
+ - [ ] Release build without obfuscation/minification when the app has sensitive logic.
45
+ - [ ] Debug code or endpoints reachable in release.
46
+
47
+ **MASVS-PRIVACY**
48
+ - [ ] Permissions requested without real use in the code.
49
+ - [ ] Third-party SDKs collecting undeclared data (Privacy Manifest on iOS, Data safety on the Play Store).
50
+
51
+ ## Flutter
52
+
53
+ - [ ] Tokens in `shared_preferences`, `hive` or `sqflite` without encryption: use `flutter_secure_storage` (Keychain/Keystore).
54
+ - [ ] `HttpClient.badCertificateCallback` returning `true` or `HttpOverrides.global` disabling validation: Critical if it reaches the release build.
55
+ - [ ] `print`/`debugPrint`/`log` with a token, `Authorization` header or login payload. `dio` interceptors with `LogInterceptor` active in release.
56
+ - [ ] Secrets passed via `--dart-define` and treated as safe: they end up in the binary.
57
+ - [ ] Release build without `--obfuscate --split-debug-info` when the app needs obfuscation.
58
+ - [ ] `webview_flutter` with `JavaScriptMode.unrestricted` and `addJavaScriptChannel` loading an uncontrolled URL.
59
+
60
+ ## Android (Kotlin/Java, or `android/` in cross-platform apps)
61
+
62
+ - [ ] `AndroidManifest.xml`: `android:usesCleartextTraffic="true"` or `network_security_config` with `cleartextTrafficPermitted="true"` for production domains.
63
+ - [ ] `network_security_config` trusting user certificates (`<certificates src="user"/>`) in release.
64
+ - [ ] `android:allowBackup="true"` (or absent) in an app with sensitive data, without exclusion rules.
65
+ - [ ] `activity`/`service`/`receiver`/`provider` with `android:exported="true"` without a permission and without validating the `Intent`.
66
+ - [ ] `android:debuggable="true"` hardcoded in the manifest.
67
+ - [ ] Tokens in `SharedPreferences` without encryption.
68
+ - [ ] `WebView` with `setJavaScriptEnabled(true)` + `addJavascriptInterface` or `setAllowFileAccess(true)`.
69
+ - [ ] Custom `TrustManager`/`HostnameVerifier` that accepts everything.
70
+ - [ ] `Log.d`/`Log.v` with sensitive data not stripped in release (R8 rules or a log wrapper).
71
+ - [ ] Keystore passwords in a committed `build.gradle` instead of `key.properties`/environment variables.
72
+
73
+ ## iOS (Swift/Objective-C, or `ios/` in cross-platform apps)
74
+
75
+ - [ ] `Info.plist`: `NSAppTransportSecurity` with `NSAllowsArbitraryLoads = true` without a justified per-domain exception.
76
+ - [ ] Tokens in `UserDefaults` or files instead of the Keychain.
77
+ - [ ] Keychain items with permissive accessibility (`kSecAttrAccessibleAlways*`, already deprecated) instead of `...WhenUnlocked`/`...AfterFirstUnlock`.
78
+ - [ ] `URLSessionDelegate` accepting any `serverTrust` without evaluating it.
79
+ - [ ] Custom URL schemes performing actions without validation (prefer universal links).
80
+ - [ ] `NSLog`/`print` with sensitive data.
81
+ - [ ] Screen with sensitive data without snapshot protection when going to background.
82
+
83
+ ## React Native
84
+
85
+ - [ ] Tokens in `AsyncStorage`: use Keychain/Keystore (`react-native-keychain`, `expo-secure-store`).
86
+ - [ ] `.env` via `react-native-config`/`expo-constants` with secrets: they go into the JS bundle, readable in the APK/IPA.
87
+ - [ ] `react-native-webview` with `originWhitelist={['*']}` and `onMessage` performing actions.
88
+ - [ ] `console.log` with sensitive data not stripped in release (for example, `babel-plugin-transform-remove-console`).
89
+ - [ ] Deep links handled in `Linking` without validating origin and parameters.
90
+
91
+ ## Useful commands (read-only)
92
+
93
+ ```bash
94
+ git ls-files | grep -Ei '\.(jks|keystore|p12|p8|mobileprovision|env)$|key\.properties|google-services\.json|GoogleService-Info\.plist'
95
+ grep -rn --include='*.xml' -E 'usesCleartextTraffic|allowBackup|exported="true"|debuggable' android app 2>/dev/null
96
+ grep -rn -E 'NSAllowsArbitraryLoads' --include='*.plist' . 2>/dev/null
97
+ ```
98
+
99
+ ## Output
100
+
101
+ Report under **Security** (or **Critical**, when the severity is Critical), with the MASVS category, `file:line`, impact and fix.