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,206 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flutter-managing-state
|
|
3
|
+
description: Manages application and ephemeral state in a Flutter app. Use when sharing data between widgets or handling complex UI state transitions.
|
|
4
|
+
metadata:
|
|
5
|
+
model: models/gemini-3.1-pro-preview
|
|
6
|
+
last_modified: Thu, 12 Mar 2026 22:18:06 GMT
|
|
7
|
+
intents: [architecture]
|
|
8
|
+
stacks: [flutter]
|
|
9
|
+
source: https://github.com/flutter/skills
|
|
10
|
+
license: BSD-3-Clause
|
|
11
|
+
---
|
|
12
|
+
# Managing State in Flutter
|
|
13
|
+
|
|
14
|
+
## Contents
|
|
15
|
+
- [Core Concepts](#core-concepts)
|
|
16
|
+
- [Architecture and Data Flow](#architecture-and-data-flow)
|
|
17
|
+
- [Workflow: Selecting a State Management Approach](#workflow-selecting-a-state-management-approach)
|
|
18
|
+
- [Workflow: Implementing MVVM with Provider](#workflow-implementing-mvvm-with-provider)
|
|
19
|
+
- [Examples](#examples)
|
|
20
|
+
|
|
21
|
+
## Core Concepts
|
|
22
|
+
|
|
23
|
+
Flutter's UI is declarative; it is built to reflect the current state of the app (`UI = f(state)`). When state changes, trigger a rebuild of the UI that depends on that state.
|
|
24
|
+
|
|
25
|
+
Distinguish between two primary types of state to determine your management strategy:
|
|
26
|
+
* **Ephemeral State (Local State):** State contained neatly within a single widget (e.g., current page in a `PageView`, current selected tab, animation progress). Manage this using a `StatefulWidget` and `setState()`.
|
|
27
|
+
* **App State (Shared State):** State shared across multiple parts of the app and maintained between user sessions (e.g., user preferences, login info, shopping cart contents). Manage this using advanced approaches like `InheritedWidget`, the `provider` package, and the MVVM architecture.
|
|
28
|
+
|
|
29
|
+
## Architecture and Data Flow
|
|
30
|
+
|
|
31
|
+
Implement the **Model-View-ViewModel (MVVM)** design pattern combined with **Unidirectional Data Flow (UDF)** for scalable app state management.
|
|
32
|
+
|
|
33
|
+
* **Unidirectional Data Flow (UDF):** Enforce a strict flow where state flows *down* from the data layer, through the logic layer, to the UI layer. Events from user interactions flow *up* from the UI layer, to the logic layer, to the data layer.
|
|
34
|
+
* **Single Source of Truth (SSOT):** Ensure data changes always happen in the data layer (Repositories). The SSOT class must be the only class capable of modifying its respective data.
|
|
35
|
+
* **Model (Data Layer):** Handle low-level tasks like HTTP requests, data caching, and system resources using Repository classes.
|
|
36
|
+
* **ViewModel (Logic Layer):** Manage the UI state. Convert app data from the Model into UI State. Extend `ChangeNotifier` and call `notifyListeners()` to trigger UI rebuilds when data changes.
|
|
37
|
+
* **View (UI Layer):** Display the state provided by the ViewModel. Keep views lean; they should contain minimal logic (only routing, animations, or simple UI conditionals).
|
|
38
|
+
|
|
39
|
+
## Workflow: Selecting a State Management Approach
|
|
40
|
+
|
|
41
|
+
Evaluate the scope of the state to determine the correct implementation strategy.
|
|
42
|
+
|
|
43
|
+
* **If managing Ephemeral State (single widget scope):**
|
|
44
|
+
1. Subclass `StatefulWidget` and `State`.
|
|
45
|
+
2. Store mutable state as private fields within the `State` class.
|
|
46
|
+
3. Mutate state exclusively inside a `setState()` callback to mark the widget as dirty and schedule a rebuild.
|
|
47
|
+
* **If managing App State (shared across widgets):**
|
|
48
|
+
1. Implement the MVVM pattern.
|
|
49
|
+
2. Use the `provider` package (a wrapper around `InheritedWidget`) to inject state into the widget tree.
|
|
50
|
+
3. Use `ChangeNotifier` to emit state updates.
|
|
51
|
+
|
|
52
|
+
## Workflow: Implementing MVVM with Provider
|
|
53
|
+
|
|
54
|
+
Follow this sequential workflow to implement app-level state management using MVVM and `provider`.
|
|
55
|
+
|
|
56
|
+
**Task Progress:**
|
|
57
|
+
- [ ] 1. Define the Model (Repository).
|
|
58
|
+
- [ ] 2. Create the ViewModel (`ChangeNotifier`).
|
|
59
|
+
- [ ] 3. Inject the ViewModel into the Widget Tree.
|
|
60
|
+
- [ ] 4. Consume the State in the View.
|
|
61
|
+
- [ ] 5. Validate the implementation.
|
|
62
|
+
|
|
63
|
+
### 1. Define the Model (Repository)
|
|
64
|
+
Create a repository class to act as the Single Source of Truth (SSOT) for the specific data domain. Handle all external API calls or database queries here.
|
|
65
|
+
|
|
66
|
+
### 2. Create the ViewModel (`ChangeNotifier`)
|
|
67
|
+
Create a ViewModel class that extends `ChangeNotifier`.
|
|
68
|
+
* Pass the Repository into the ViewModel via dependency injection.
|
|
69
|
+
* Define properties for the UI state (e.g., `isLoading`, `data`, `errorMessage`).
|
|
70
|
+
* Implement methods to handle UI events. Inside these methods, mutate the state and call `notifyListeners()` to trigger UI rebuilds.
|
|
71
|
+
|
|
72
|
+
### 3. Inject the ViewModel into the Widget Tree
|
|
73
|
+
Use `ChangeNotifierProvider` from the `provider` package to provide the ViewModel to the widget subtree that requires it. Place the provider as low in the widget tree as possible to avoid polluting the scope.
|
|
74
|
+
|
|
75
|
+
### 4. Consume the State in the View
|
|
76
|
+
Access the ViewModel in your `StatelessWidget` or `StatefulWidget`.
|
|
77
|
+
* Use `Consumer<MyViewModel>` to rebuild specific parts of the UI when `notifyListeners()` is called.
|
|
78
|
+
* Use `context.read<MyViewModel>()` (or `Provider.of<MyViewModel>(context, listen: false)`) inside event handlers (like `onPressed`) to call ViewModel methods without triggering a rebuild of the calling widget.
|
|
79
|
+
|
|
80
|
+
### 5. Validate the implementation
|
|
81
|
+
Run the following feedback loop to ensure data flows correctly:
|
|
82
|
+
1. Trigger a user action in the View.
|
|
83
|
+
2. Verify the ViewModel receives the event and calls the Repository.
|
|
84
|
+
3. Verify the Repository updates the SSOT and returns data.
|
|
85
|
+
4. Verify the ViewModel updates its state and calls `notifyListeners()`.
|
|
86
|
+
5. Verify the View rebuilds with the new state.
|
|
87
|
+
*Run validator -> review errors -> fix missing `notifyListeners()` calls or incorrect `Provider` scopes.*
|
|
88
|
+
|
|
89
|
+
## Examples
|
|
90
|
+
|
|
91
|
+
### Ephemeral State Implementation (`setState`)
|
|
92
|
+
|
|
93
|
+
Use this pattern strictly for local, UI-only state.
|
|
94
|
+
|
|
95
|
+
```dart
|
|
96
|
+
class EphemeralCounter extends StatefulWidget {
|
|
97
|
+
const EphemeralCounter({super.key});
|
|
98
|
+
|
|
99
|
+
@override
|
|
100
|
+
State<EphemeralCounter> createState() => _EphemeralCounterState();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
class _EphemeralCounterState extends State<EphemeralCounter> {
|
|
104
|
+
int _counter = 0; // Local state
|
|
105
|
+
|
|
106
|
+
void _increment() {
|
|
107
|
+
setState(() {
|
|
108
|
+
_counter++; // Mutate state and schedule rebuild
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
@override
|
|
113
|
+
Widget build(BuildContext context) {
|
|
114
|
+
return ElevatedButton(
|
|
115
|
+
onPressed: _increment,
|
|
116
|
+
child: Text('Count: $_counter'),
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### App State Implementation (MVVM + Provider)
|
|
123
|
+
|
|
124
|
+
Use this pattern for shared data and complex business logic.
|
|
125
|
+
|
|
126
|
+
```dart
|
|
127
|
+
// 1. Model (Repository)
|
|
128
|
+
class CartRepository {
|
|
129
|
+
Future<void> saveItemToCart(String item) async {
|
|
130
|
+
// Simulate network/database call
|
|
131
|
+
await Future.delayed(const Duration(milliseconds: 500));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// 2. ViewModel (ChangeNotifier)
|
|
136
|
+
class CartViewModel extends ChangeNotifier {
|
|
137
|
+
final CartRepository repository;
|
|
138
|
+
|
|
139
|
+
CartViewModel({required this.repository});
|
|
140
|
+
|
|
141
|
+
final List<String> _items = [];
|
|
142
|
+
bool isLoading = false;
|
|
143
|
+
String? errorMessage;
|
|
144
|
+
|
|
145
|
+
List<String> get items => List.unmodifiable(_items);
|
|
146
|
+
|
|
147
|
+
Future<void> addItem(String item) async {
|
|
148
|
+
isLoading = true;
|
|
149
|
+
errorMessage = null;
|
|
150
|
+
notifyListeners(); // Trigger loading UI
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
await repository.saveItemToCart(item);
|
|
154
|
+
_items.add(item);
|
|
155
|
+
} catch (e) {
|
|
156
|
+
errorMessage = 'Failed to add item';
|
|
157
|
+
} finally {
|
|
158
|
+
isLoading = false;
|
|
159
|
+
notifyListeners(); // Trigger success/error UI
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 3. Injection & 4. View (UI)
|
|
165
|
+
class CartApp extends StatelessWidget {
|
|
166
|
+
const CartApp({super.key});
|
|
167
|
+
|
|
168
|
+
@override
|
|
169
|
+
Widget build(BuildContext context) {
|
|
170
|
+
// Inject ViewModel
|
|
171
|
+
return ChangeNotifierProvider(
|
|
172
|
+
create: (_) => CartViewModel(repository: CartRepository()),
|
|
173
|
+
child: const CartScreen(),
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
class CartScreen extends StatelessWidget {
|
|
179
|
+
const CartScreen({super.key});
|
|
180
|
+
|
|
181
|
+
@override
|
|
182
|
+
Widget build(BuildContext context) {
|
|
183
|
+
return Scaffold(
|
|
184
|
+
body: Consumer<CartViewModel>(
|
|
185
|
+
builder: (context, viewModel, child) {
|
|
186
|
+
if (viewModel.isLoading) {
|
|
187
|
+
return const CircularProgressIndicator();
|
|
188
|
+
}
|
|
189
|
+
if (viewModel.errorMessage != null) {
|
|
190
|
+
return Text(viewModel.errorMessage!);
|
|
191
|
+
}
|
|
192
|
+
return ListView.builder(
|
|
193
|
+
itemCount: viewModel.items.length,
|
|
194
|
+
itemBuilder: (_, index) => Text(viewModel.items[index]),
|
|
195
|
+
);
|
|
196
|
+
},
|
|
197
|
+
),
|
|
198
|
+
floatingActionButton: FloatingActionButton(
|
|
199
|
+
// Use read() to access methods without listening for rebuilds
|
|
200
|
+
onPressed: () => context.read<CartViewModel>().addItem('New Item'),
|
|
201
|
+
child: const Icon(Icons.add),
|
|
202
|
+
),
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flutter-project-audit
|
|
3
|
+
description: Audits the health of a Flutter project as a whole — pubspec constraints and lock file, analysis_options and lints, SDK and platform configuration (minSdk, iOS deployment target), flavors and environments, generated code, folder structure and test setup. Use when reviewing or auditing a Flutter app or plugin, before a release, or when the user asks whether a Flutter project is well configured.
|
|
4
|
+
intents: [review, architecture, dependency]
|
|
5
|
+
stacks: [flutter]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Flutter Project Audit
|
|
9
|
+
|
|
10
|
+
Project health checklist. Every item must be confirmed by reading the cited file; do not report what you could not verify.
|
|
11
|
+
|
|
12
|
+
## 1. `pubspec.yaml` and dependencies
|
|
13
|
+
|
|
14
|
+
- [ ] `environment.sdk` with a lower bound compatible with the features used (records, patterns and sealed classes require Dart 3).
|
|
15
|
+
- [ ] Dependencies with caret constraints (`^x.y.z`). `any` or versions without an upper bound are a finding.
|
|
16
|
+
- [ ] `dependency_overrides` present without a comment justifying it: finding (it masks conflicts and tends to be forgotten).
|
|
17
|
+
- [ ] Dev packages (`build_runner`, `mocktail`, `flutter_lints`/`very_good_analysis`) in `dev_dependencies`, not in `dependencies`.
|
|
18
|
+
- [ ] Dependencies via `git:` or `path:` in a published app: confirm it is intentional and that the `ref` is pinned.
|
|
19
|
+
- [ ] `pubspec.lock` committed in **apps** (guarantees reproducible builds). In **packages/plugins**, committing it is optional.
|
|
20
|
+
- [ ] Discontinued or replaced packages: only report with evidence (`pub` warning, package README). Do not claim it from memory.
|
|
21
|
+
|
|
22
|
+
Useful command, if Flutter is available: `flutter pub outdated`.
|
|
23
|
+
|
|
24
|
+
## 2. Static analysis
|
|
25
|
+
|
|
26
|
+
- [ ] `analysis_options.yaml` exists and includes a lint set (`package:flutter_lints/flutter.yaml`, `package:lints/recommended.yaml` or `very_good_analysis`).
|
|
27
|
+
- [ ] Rules disabled in bulk (`ignore:` at the top of files, `// ignore_for_file:`) without justification.
|
|
28
|
+
- [ ] Generated files (`*.g.dart`, `*.freezed.dart`, `*.mocks.dart`) excluded from analysis and consistent with the annotations. Stale generated code is a build bug.
|
|
29
|
+
|
|
30
|
+
Useful command: `flutter analyze`.
|
|
31
|
+
|
|
32
|
+
## 3. Native configuration
|
|
33
|
+
|
|
34
|
+
**Android** (`android/app/build.gradle` or `build.gradle.kts`):
|
|
35
|
+
- [ ] `applicationId` is not the default `com.example.*` (the Play Store rejects it).
|
|
36
|
+
- [ ] `minSdk`/`targetSdk`/`compileSdk`: explicit values or `flutter.*`. An outdated `targetSdk` blocks publishing; confirm the current Play Store requirement before citing a number.
|
|
37
|
+
- [ ] The release `signingConfig` does not use the debug key; passwords come from `key.properties` outside version control.
|
|
38
|
+
- [ ] `minifyEnabled`/`shrinkResources` and ProGuard/R8 rules consistent with plugins that use reflection.
|
|
39
|
+
|
|
40
|
+
**iOS** (`ios/Runner.xcodeproj`, `ios/Podfile`, `ios/Runner/Info.plist`):
|
|
41
|
+
- [ ] `PRODUCT_BUNDLE_IDENTIFIER` is not `com.example.*`.
|
|
42
|
+
- [ ] The Podfile `platform :ios` aligned with the project's `IPHONEOS_DEPLOYMENT_TARGET`.
|
|
43
|
+
- [ ] Every permission used has its `NS*UsageDescription` with real text (empty or generic text causes App Store rejection).
|
|
44
|
+
|
|
45
|
+
## 4. Environments and flavors
|
|
46
|
+
|
|
47
|
+
- [ ] Environment URLs and keys are not hardcoded in `lib/`. The standard is `--dart-define`/`--dart-define-from-file` or flavors.
|
|
48
|
+
- [ ] Values passed via `--dart-define` **are not secret**: they end up in the binary. A private API key must never be in the app.
|
|
49
|
+
- [ ] Production `.env`, `key.properties`, `*.jks`, `*.keystore`, `google-services.json` and `GoogleService-Info.plist` files: check `.gitignore` and `git ls-files`.
|
|
50
|
+
|
|
51
|
+
## 5. Structure and architecture
|
|
52
|
+
|
|
53
|
+
- [ ] Consistent organization (feature-first or by layers). Mixing both without criteria is a maintainability finding.
|
|
54
|
+
- [ ] The UI does not call HTTP, the database or platform plugins directly; there is a data/repository layer.
|
|
55
|
+
- [ ] A single predominant state management approach (BLoC, Riverpod, Provider...). Several without reason is a finding.
|
|
56
|
+
- [ ] Centralized dependency injection (constructors, `get_it`, providers), without global singletons scattered around.
|
|
57
|
+
|
|
58
|
+
## 6. Tests and CI
|
|
59
|
+
|
|
60
|
+
- [ ] There is a `test/` folder with tests matching the business logic (not just the template's default `widget_test.dart`).
|
|
61
|
+
- [ ] If there is an `integration_test/`, it runs in CI.
|
|
62
|
+
- [ ] The pipeline (`.github/workflows`, `codemagic.yaml`, `bitrise.yml`...) runs at least `flutter analyze` and `flutter test`.
|
|
63
|
+
|
|
64
|
+
## Output
|
|
65
|
+
|
|
66
|
+
Report each finding in the matching section of the `mobile-code-reviewer` report (Architecture, Security, Maintainability...), with `file:line` and the proposed fix.
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flutter-reducing-app-size
|
|
3
|
+
description: Measures and optimizes the size of Flutter application bundles for deployment. Use when minimizing download size or meeting app store package constraints.
|
|
4
|
+
metadata:
|
|
5
|
+
model: models/gemini-3.1-pro-preview
|
|
6
|
+
last_modified: Thu, 12 Mar 2026 22:22:44 GMT
|
|
7
|
+
intents: [performance]
|
|
8
|
+
stacks: [flutter]
|
|
9
|
+
source: https://github.com/flutter/skills
|
|
10
|
+
license: BSD-3-Clause
|
|
11
|
+
---
|
|
12
|
+
# Reducing Flutter App Size
|
|
13
|
+
|
|
14
|
+
## Contents
|
|
15
|
+
- [Core Concepts](#core-concepts)
|
|
16
|
+
- [Workflow: Generating Size Analysis Files](#workflow-generating-size-analysis-files)
|
|
17
|
+
- [Workflow: Analyzing Size Data in DevTools](#workflow-analyzing-size-data-in-devtools)
|
|
18
|
+
- [Workflow: Estimating iOS Download Size](#workflow-estimating-ios-download-size)
|
|
19
|
+
- [Workflow: Implementing Size Reduction Strategies](#workflow-implementing-size-reduction-strategies)
|
|
20
|
+
- [Examples](#examples)
|
|
21
|
+
|
|
22
|
+
## Core Concepts
|
|
23
|
+
- **Debug vs. Release:** Never use debug builds to measure app size. Debug builds include VM overhead and lack Ahead-Of-Time (AOT) compilation and tree-shaking.
|
|
24
|
+
- **Upload vs. Download Size:** The size of an upload package (APK, AAB, IPA) does not represent the end-user download size. App stores filter redundant native library architectures and asset densities based on the target device.
|
|
25
|
+
- **AOT Tree-Shaking:** The Dart AOT compiler automatically removes unused or unreachable code in profile and release modes.
|
|
26
|
+
- **Size Analysis JSON:** The `--analyze-size` flag generates a `*-code-size-analysis_*.json` file detailing the byte size of packages, libraries, classes, and functions.
|
|
27
|
+
|
|
28
|
+
## Workflow: Generating Size Analysis Files
|
|
29
|
+
|
|
30
|
+
Use this workflow to generate the raw data required for size analysis.
|
|
31
|
+
|
|
32
|
+
**Task Progress:**
|
|
33
|
+
- [ ] Determine the target platform (apk, appbundle, ios, linux, macos, windows).
|
|
34
|
+
- [ ] Run the Flutter build command with the `--analyze-size` flag.
|
|
35
|
+
- [ ] Locate the generated `*-code-size-analysis_*.json` file in the `build/` directory.
|
|
36
|
+
|
|
37
|
+
**Conditional Logic:**
|
|
38
|
+
- **If targeting Android:** Run `flutter build apk --analyze-size` or `flutter build appbundle --analyze-size`.
|
|
39
|
+
- **If targeting iOS:** Run `flutter build ios --analyze-size`. *Note: This creates a `.app` file useful for relative content sizing, but not for estimating final App Store download size. Use the [Estimating iOS Download Size](#workflow-estimating-ios-download-size) workflow for accurate iOS metrics.*
|
|
40
|
+
- **If targeting Desktop:** Run `flutter build [windows|macos|linux] --analyze-size`.
|
|
41
|
+
|
|
42
|
+
## Workflow: Analyzing Size Data in DevTools
|
|
43
|
+
|
|
44
|
+
Use this workflow to visualize and drill down into the Size Analysis JSON.
|
|
45
|
+
|
|
46
|
+
**Task Progress:**
|
|
47
|
+
- [ ] Launch DevTools by running `dart devtools` in the terminal.
|
|
48
|
+
- [ ] Select "Open app size tool" from the DevTools landing page.
|
|
49
|
+
- [ ] Upload the generated `*-code-size-analysis_*.json` file.
|
|
50
|
+
- [ ] Inspect the treemap or tree view to identify large packages, libraries, or assets.
|
|
51
|
+
- [ ] **Feedback Loop:**
|
|
52
|
+
1. Identify the largest contributors to app size.
|
|
53
|
+
2. Determine if the dependency or asset is strictly necessary.
|
|
54
|
+
3. Remove, replace, or optimize the identified component.
|
|
55
|
+
4. Regenerate the Size Analysis JSON and compare the new build against the old build using the DevTools "Diff" tab.
|
|
56
|
+
|
|
57
|
+
## Workflow: Estimating iOS Download Size
|
|
58
|
+
|
|
59
|
+
Use this workflow to get an accurate projection of iOS download and installation sizes across different devices.
|
|
60
|
+
|
|
61
|
+
**Task Progress:**
|
|
62
|
+
- [ ] Configure the app version and build number in `pubspec.yaml`.
|
|
63
|
+
- [ ] Generate an Xcode archive by running `flutter build ipa --export-method development`.
|
|
64
|
+
- [ ] Open the generated archive (`build/ios/archive/*.xcarchive`) in Xcode.
|
|
65
|
+
- [ ] Click **Distribute App** and select **Development**.
|
|
66
|
+
- [ ] In the App Thinning configuration, select **All compatible device variants**.
|
|
67
|
+
- [ ] Check the option to **Strip Swift symbols**.
|
|
68
|
+
- [ ] Sign and export the IPA.
|
|
69
|
+
- [ ] Open the exported directory and review the `App Thinning Size Report.txt` file to evaluate projected sizes per device.
|
|
70
|
+
|
|
71
|
+
## Workflow: Implementing Size Reduction Strategies
|
|
72
|
+
|
|
73
|
+
Apply these strategies to actively reduce the compiled footprint of the application.
|
|
74
|
+
|
|
75
|
+
**Task Progress:**
|
|
76
|
+
- [ ] **Split Debug Info:** Strip debug symbols from the compiled binary and store them in separate files.
|
|
77
|
+
- [ ] **Remove Unused Resources:** Audit the `pubspec.yaml` and `assets/` directory. Delete any images, fonts, or files not actively referenced in the codebase.
|
|
78
|
+
- [ ] **Minimize Library Resources:** Review third-party packages. If a package imports massive resource files (e.g., large icon sets or localization files) but only a fraction is used, consider alternative packages or custom implementations.
|
|
79
|
+
- [ ] **Compress Media:** Compress all PNG and JPEG assets using tools like `pngquant`, `imageoptim`, or WebP conversion before bundling them into the app.
|
|
80
|
+
|
|
81
|
+
## Examples
|
|
82
|
+
|
|
83
|
+
### Generating Size Analysis (Android)
|
|
84
|
+
```bash
|
|
85
|
+
# Generate the size analysis JSON for an Android App Bundle
|
|
86
|
+
flutter build appbundle --analyze-size --target-platform=android-arm64
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Splitting Debug Info (Release Build)
|
|
90
|
+
```bash
|
|
91
|
+
# Build an APK while stripping debug info to reduce binary size
|
|
92
|
+
flutter build apk --obfuscate --split-debug-info=build/app/outputs/symbols
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Reading the iOS App Thinning Size Report
|
|
96
|
+
When reviewing `App Thinning Size Report.txt`, look for the specific target device to understand the true impact on the user:
|
|
97
|
+
```text
|
|
98
|
+
Variant: Runner-7433FC8E-1DF4-4299-A7E8-E00768671BEB.ipa
|
|
99
|
+
Supported variant descriptors: [device: iPhone12,1, os-version: 13.0]
|
|
100
|
+
App + On Demand Resources size: 5.4 MB compressed, 13.7 MB uncompressed
|
|
101
|
+
App size: 5.4 MB compressed, 13.7 MB uncompressed
|
|
102
|
+
```
|
|
103
|
+
*Interpretation: The end-user download size (compressed) is 5.4 MB, and the on-device footprint (uncompressed) is 13.7 MB.*
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flutter-secure-token-store
|
|
3
|
+
description: "Stores JWT access tokens on Flutter with platform secure storage (Keychain on iOS, Keystore-backed storage on Android) via flutter_secure_storage. Use when persisting, reading or clearing auth tokens, or when replacing shared_preferences token storage."
|
|
4
|
+
intents: [security]
|
|
5
|
+
stacks: [flutter]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# flutter-secure-token-store
|
|
9
|
+
|
|
10
|
+
## Description
|
|
11
|
+
Store JWT tokens securely on Flutter using platform secure storage.
|
|
12
|
+
|
|
13
|
+
## Language
|
|
14
|
+
Dart
|
|
15
|
+
|
|
16
|
+
## Inputs
|
|
17
|
+
- `token: String`
|
|
18
|
+
|
|
19
|
+
## Returns
|
|
20
|
+
- `Future<void>`
|
|
21
|
+
- `Future<String?>`
|
|
22
|
+
|
|
23
|
+
## Example
|
|
24
|
+
```dart
|
|
25
|
+
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
|
26
|
+
|
|
27
|
+
class TokenStorage {
|
|
28
|
+
final FlutterSecureStorage _storage = const FlutterSecureStorage();
|
|
29
|
+
static const _key = 'access_token';
|
|
30
|
+
|
|
31
|
+
Future<void> save(String token) => _storage.write(key: _key, value: token);
|
|
32
|
+
Future<String?> read() => _storage.read(key: _key);
|
|
33
|
+
Future<void> clear() => _storage.delete(key: _key);
|
|
34
|
+
}
|
|
35
|
+
```
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flutter-setting-up-on-macos
|
|
3
|
+
description: Sets up a macOS environment for Flutter development. Use when configuring a macOS machine to run, build, or deploy Flutter applications for iOS or macOS.
|
|
4
|
+
metadata:
|
|
5
|
+
model: models/gemini-3.1-pro-preview
|
|
6
|
+
last_modified: Thu, 12 Mar 2026 22:12:50 GMT
|
|
7
|
+
routing: manual
|
|
8
|
+
stacks: [flutter]
|
|
9
|
+
source: https://github.com/flutter/skills
|
|
10
|
+
license: BSD-3-Clause
|
|
11
|
+
---
|
|
12
|
+
# Setting Up a macOS Environment for Flutter Development
|
|
13
|
+
|
|
14
|
+
## Contents
|
|
15
|
+
- [Prerequisites](#prerequisites)
|
|
16
|
+
- [Tooling Configuration](#tooling-configuration)
|
|
17
|
+
- [Workflow: Configuring macOS Tooling](#workflow-configuring-macos-tooling)
|
|
18
|
+
- [Workflow: Validating the Environment](#workflow-validating-the-environment)
|
|
19
|
+
- [Troubleshooting](#troubleshooting)
|
|
20
|
+
|
|
21
|
+
## Prerequisites
|
|
22
|
+
Ensure the following baseline requirements are met before configuring the macOS-specific toolchain:
|
|
23
|
+
- macOS operating system.
|
|
24
|
+
- Flutter SDK installed and added to the system `PATH`.
|
|
25
|
+
- Active internet connection for downloading toolchains and dependencies.
|
|
26
|
+
|
|
27
|
+
## Tooling Configuration
|
|
28
|
+
macOS desktop development requires specific Apple toolchains to compile and debug native Swift and Objective-C code.
|
|
29
|
+
- **Xcode**: Required for compiling macOS desktop applications.
|
|
30
|
+
- **CocoaPods**: Required for managing native dependencies used by Flutter plugins.
|
|
31
|
+
|
|
32
|
+
## Workflow: Configuring macOS Tooling
|
|
33
|
+
|
|
34
|
+
Copy and follow this checklist to configure the macOS build environment.
|
|
35
|
+
|
|
36
|
+
- [ ] **Install Xcode**: Install the latest version of Xcode from the Mac App Store or the Apple Developer portal.
|
|
37
|
+
- [ ] **Configure Command-Line Tools**: Link the Xcode command-line tools to the installed Xcode version. Run the following command in the terminal:
|
|
38
|
+
```bash
|
|
39
|
+
sudo sh -c 'xcode-select -s /Applications/Xcode.app/Contents/Developer && xcodebuild -runFirstLaunch'
|
|
40
|
+
```
|
|
41
|
+
*Conditional*: If Xcode is installed in a custom directory, replace `/Applications/Xcode.app` with the correct absolute path.
|
|
42
|
+
- [ ] **Accept Xcode Licenses**: Accept the required developer licenses by running:
|
|
43
|
+
```bash
|
|
44
|
+
sudo xcodebuild -license
|
|
45
|
+
```
|
|
46
|
+
Read and agree to the prompts.
|
|
47
|
+
- [ ] **Install CocoaPods**: Install CocoaPods to handle native macOS plugin dependencies.
|
|
48
|
+
```bash
|
|
49
|
+
sudo gem install cocoapods
|
|
50
|
+
```
|
|
51
|
+
*Conditional*: If CocoaPods is already installed, ensure it is updated to the latest version (`sudo gem update cocoapods`).
|
|
52
|
+
|
|
53
|
+
## Workflow: Validating the Environment
|
|
54
|
+
|
|
55
|
+
Execute this feedback loop to ensure the environment is correctly configured for macOS desktop development.
|
|
56
|
+
|
|
57
|
+
- [ ] **Run Validator**: Execute the Flutter diagnostic tool with verbose output:
|
|
58
|
+
```bash
|
|
59
|
+
flutter doctor -v
|
|
60
|
+
```
|
|
61
|
+
- [ ] **Review Errors**: Check the `Xcode` section in the output.
|
|
62
|
+
- [ ] **Fix & Retry**: If errors or missing components are reported under the Xcode section, resolve them according to the output instructions, then re-run `flutter doctor -v` until the Xcode section passes.
|
|
63
|
+
- [ ] **Verify Device Availability**: Confirm that Flutter recognizes the macOS desktop as a valid deployment target:
|
|
64
|
+
```bash
|
|
65
|
+
flutter devices
|
|
66
|
+
```
|
|
67
|
+
*Success Criteria*: The output must contain at least one entry with `macos` listed as the platform.
|
|
68
|
+
|
|
69
|
+
## Troubleshooting
|
|
70
|
+
|
|
71
|
+
If the validation workflow fails, apply the following resolutions:
|
|
72
|
+
|
|
73
|
+
- **Missing Command-Line Tools**: If `flutter doctor` reports missing tools, ensure the `xcode-select` command was run with `sudo` and points to the correct `.app` directory.
|
|
74
|
+
- **CocoaPods Not Found**: If CocoaPods is installed but not detected, verify that your Ruby `gem` binary path is included in your shell's `PATH` environment variable.
|
|
75
|
+
- **Device Not Listed**: If `flutter devices` does not list `macos`, ensure desktop support is enabled in your Flutter configuration:
|
|
76
|
+
```bash
|
|
77
|
+
flutter config --enable-macos-desktop
|
|
78
|
+
```
|