ginskill-init 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.wrangler/cache/pages.json +4 -0
- package/.wrangler/cache/wrangler-account.json +6 -0
- package/DEVELOPMENT.md +510 -0
- package/README.md +104 -0
- package/agents/developer.md +56 -0
- package/agents/frontend-design.md +69 -0
- package/agents/mobile-reviewer.md +36 -0
- package/agents/review-code.md +49 -0
- package/agents/security-scanner.md +50 -0
- package/agents/tester.md +72 -0
- package/bin/cli.js +461 -0
- package/landing/ai-build-ai.png +0 -0
- package/landing/index.html +1495 -0
- package/landing/logo.png +0 -0
- package/package.json +37 -0
- package/skills/active-life-dev/SKILL.md +157 -0
- package/skills/active-life-dev/docs/auth.md +187 -0
- package/skills/active-life-dev/docs/customers.md +216 -0
- package/skills/active-life-dev/docs/integrations.md +209 -0
- package/skills/active-life-dev/docs/inventory.md +192 -0
- package/skills/active-life-dev/docs/modules.md +181 -0
- package/skills/active-life-dev/docs/orders.md +180 -0
- package/skills/active-life-dev/docs/patterns.md +319 -0
- package/skills/active-life-dev/docs/products.md +216 -0
- package/skills/active-life-dev/docs/schema.md +502 -0
- package/skills/active-life-dev/docs/setup.md +169 -0
- package/skills/active-life-dev/docs/vouchers.md +144 -0
- package/skills/ai-asset-generator/SKILL.md +247 -0
- package/skills/ai-asset-generator/docs/gen-image.md +274 -0
- package/skills/ai-asset-generator/docs/genvideo.md +341 -0
- package/skills/ai-asset-generator/docs/remove-background.md +19 -0
- package/skills/ai-asset-generator/lib/bg-remove.mjs +34 -0
- package/skills/ai-asset-generator/lib/env.mjs +48 -0
- package/skills/ai-asset-generator/lib/kie-client.mjs +100 -0
- package/skills/ai-build-ai/SKILL.md +127 -0
- package/skills/ai-build-ai/docs/agent-teams.md +293 -0
- package/skills/ai-build-ai/docs/checkpointing.md +161 -0
- package/skills/ai-build-ai/docs/create-agent.md +399 -0
- package/skills/ai-build-ai/docs/create-mcp.md +395 -0
- package/skills/ai-build-ai/docs/create-skill.md +299 -0
- package/skills/ai-build-ai/docs/headless-mode.md +614 -0
- package/skills/ai-build-ai/docs/hooks.md +578 -0
- package/skills/ai-build-ai/docs/memory-claude-md.md +375 -0
- package/skills/ai-build-ai/docs/output-styles.md +208 -0
- package/skills/ai-build-ai/docs/overview.md +162 -0
- package/skills/ai-build-ai/docs/permissions.md +391 -0
- package/skills/ai-build-ai/docs/plugins.md +396 -0
- package/skills/ai-build-ai/docs/sandbox.md +262 -0
- package/skills/ai-build-ai/docs/team-lead-workflow.md +648 -0
- package/skills/ant-design/SKILL.md +323 -0
- package/skills/ant-design/docs/components.md +160 -0
- package/skills/ant-design/docs/data-entry.md +406 -0
- package/skills/ant-design/docs/display.md +594 -0
- package/skills/ant-design/docs/feedback.md +451 -0
- package/skills/ant-design/docs/key-components.md +414 -0
- package/skills/ant-design/docs/navigation.md +310 -0
- package/skills/ant-design/docs/pro-components.md +543 -0
- package/skills/ant-design/docs/setup.md +213 -0
- package/skills/ant-design/docs/theme.md +265 -0
- package/skills/flutter-performance/SKILL.md +803 -0
- package/skills/flutter-performance/references/flutter-patterns.md +595 -0
- package/skills/icon-generator/SKILL.md +270 -0
- package/skills/mobile-app-review/SKILL.md +321 -0
- package/skills/mobile-app-review/references/apple-review.md +132 -0
- package/skills/mobile-app-review/references/google-play-review.md +203 -0
- package/skills/mongodb/SKILL.md +667 -0
- package/skills/mongodb/references/mongoose-patterns.md +368 -0
- package/skills/nestjs-architecture/SKILL.md +1086 -0
- package/skills/nestjs-architecture/references/advanced-patterns.md +590 -0
- package/skills/performance/SKILL.md +509 -0
- package/skills/react-fsd-architecture/SKILL.md +693 -0
- package/skills/react-fsd-architecture/references/fsd-patterns.md +747 -0
- package/skills/react-native-expo/SKILL.md +128 -0
- package/skills/react-native-expo/references/data-layer.md +252 -0
- package/skills/react-native-expo/references/design-system.md +252 -0
- package/skills/react-native-expo/references/navigation.md +199 -0
- package/skills/react-native-expo/references/performance.md +229 -0
- package/skills/react-native-expo/references/platform-services.md +179 -0
- package/skills/react-native-expo/references/state-management.md +209 -0
- package/skills/react-native-expo/references/ui-patterns.md +301 -0
- package/skills/react-query/SKILL.md +685 -0
- package/skills/react-query/references/query-patterns.md +365 -0
- package/skills/review-code/SKILL.md +374 -0
- package/skills/review-code/references/clean-code-principles.md +395 -0
- package/skills/review-code/references/frontend-patterns.md +136 -0
- package/skills/review-code/references/nestjs-patterns.md +184 -0
- package/skills/security-scanner/SKILL.md +366 -0
- package/skills/security-scanner/references/nestjs-security.md +260 -0
- package/skills/security-scanner/references/nextjs-security.md +201 -0
- package/skills/security-scanner/references/react-native-security.md +199 -0
- package/skills/traefik/SKILL.md +105 -0
- package/skills/traefik/docs/advanced-routing.md +186 -0
- package/skills/traefik/docs/auth-providers.md +137 -0
- package/skills/traefik/docs/cicd-devops.md +396 -0
- package/skills/traefik/docs/core-config.md +171 -0
- package/skills/traefik/docs/distributed-config.md +96 -0
- package/skills/traefik/docs/docker-compose.md +182 -0
- package/skills/traefik/docs/ha-performance.md +177 -0
- package/skills/traefik/docs/kubernetes.md +278 -0
- package/skills/traefik/docs/middleware.md +205 -0
- package/skills/traefik/docs/monitoring.md +357 -0
- package/skills/traefik/docs/security.md +391 -0
- package/skills/traefik/docs/tls-acme.md +155 -0
- package/skills/ui-ux-pro-max/SKILL.md +377 -0
- package/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/skills/ui-ux-pro-max/data/icons.csv +101 -0
- package/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/skills/ui-ux-pro-max/data/styles.csv +68 -0
- package/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
- package/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
|
@@ -0,0 +1,803 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: flutter-performance
|
|
3
|
+
description: |
|
|
4
|
+
**Flutter Performance Optimization**: Comprehensive guide for optimizing Flutter app performance — widget rebuilds, jank reduction, memory leaks, Dart isolates, image caching, state management perf, Impeller rendering, DevTools profiling. Targets 60/120fps production apps.
|
|
5
|
+
- MANDATORY TRIGGERS: flutter performance, flutter optimization, flutter jank, flutter fps, flutter rebuild, flutter memory leak, flutter isolate, flutter image cache, flutter profiling, flutter devtools, flutter impeller, flutter shader, flutter widget rebuild, flutter slow, flutter laggy, flutter frame drop, flutter startup time, flutter app size, flutter tree shaking, flutter repaint boundary, flutter const constructor, flutter state management performance, flutter listview performance, flutter animation performance, dart isolate, flutter scroll jank, flutter build method, flutter dispose
|
|
6
|
+
- Use this skill whenever the user is optimizing Flutter app performance, debugging jank or frame drops, profiling with DevTools, fixing memory leaks, or reviewing Flutter code for performance anti-patterns. Also trigger when discussing widget rebuild optimization, Impeller rendering, Dart isolate usage, or app size reduction — even casual mentions like "my Flutter app is slow" or "how do I improve FPS?".
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Flutter Performance Optimization
|
|
10
|
+
|
|
11
|
+
Analyze and optimize Flutter apps for consistent 60/120fps rendering. Covers widget rebuilds, jank elimination, memory management, Dart isolates, image optimization, Impeller rendering, and DevTools profiling. Targets production apps on both iOS and Android.
|
|
12
|
+
|
|
13
|
+
## Core Mental Model
|
|
14
|
+
|
|
15
|
+
**Flutter renders at 60fps (16ms per frame) or 120fps (8ms per frame).** Every frame goes through Build → Layout → Paint → Composite. If any phase exceeds the frame budget, you get jank — visible stuttering. The goal: keep the main isolate free for UI work, minimize widget rebuilds, and let Impeller handle GPU rendering efficiently.
|
|
16
|
+
|
|
17
|
+
Key principles:
|
|
18
|
+
- **Measure first** — use DevTools before optimizing blindly
|
|
19
|
+
- **Rebuild less** — const constructors, selective state, RepaintBoundary
|
|
20
|
+
- **Offload heavy work** — Dart isolates for CPU-intensive tasks
|
|
21
|
+
- **Cache aggressively** — images, computed values, widget subtrees
|
|
22
|
+
- **Let Impeller work** — avoid patterns that defeat its optimizations
|
|
23
|
+
|
|
24
|
+
## 1. Widget Rebuild Optimization
|
|
25
|
+
|
|
26
|
+
### const Constructors (Up to 70% Fewer Rebuilds)
|
|
27
|
+
|
|
28
|
+
```dart
|
|
29
|
+
// BAD — rebuilds every time parent rebuilds
|
|
30
|
+
Widget build(BuildContext context) {
|
|
31
|
+
return Column(
|
|
32
|
+
children: [
|
|
33
|
+
Text('Static Title'), // rebuilt unnecessarily
|
|
34
|
+
Icon(Icons.star), // rebuilt unnecessarily
|
|
35
|
+
MyDynamicWidget(data: _data),
|
|
36
|
+
],
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// GOOD — const widgets are skipped during rebuild
|
|
41
|
+
Widget build(BuildContext context) {
|
|
42
|
+
return Column(
|
|
43
|
+
children: [
|
|
44
|
+
const Text('Static Title'), // skipped — same instance
|
|
45
|
+
const Icon(Icons.star), // skipped — same instance
|
|
46
|
+
MyDynamicWidget(data: _data), // only this rebuilds
|
|
47
|
+
],
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Enable the `prefer_const_constructors` lint:
|
|
53
|
+
```yaml
|
|
54
|
+
# analysis_options.yaml
|
|
55
|
+
linter:
|
|
56
|
+
rules:
|
|
57
|
+
prefer_const_constructors: true
|
|
58
|
+
prefer_const_declarations: true
|
|
59
|
+
prefer_const_literals_to_create_immutables: true
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Decompose Large build() Methods
|
|
63
|
+
|
|
64
|
+
```dart
|
|
65
|
+
// BAD — 200+ line build method, entire tree rebuilds on any setState
|
|
66
|
+
Widget build(BuildContext context) {
|
|
67
|
+
return Scaffold(
|
|
68
|
+
appBar: AppBar(title: Text(widget.title)),
|
|
69
|
+
body: Column(children: [
|
|
70
|
+
// ...hundreds of lines of widgets...
|
|
71
|
+
]),
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// GOOD — extracted const/stateless sub-widgets
|
|
76
|
+
Widget build(BuildContext context) {
|
|
77
|
+
return Scaffold(
|
|
78
|
+
appBar: AppBar(title: Text(widget.title)),
|
|
79
|
+
body: Column(children: [
|
|
80
|
+
const HeaderSection(), // separate StatelessWidget
|
|
81
|
+
ContentSection(data: _data), // only rebuilds when _data changes
|
|
82
|
+
const FooterSection(), // separate StatelessWidget
|
|
83
|
+
]),
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Avoid Inline Closures That Cause Rebuilds
|
|
89
|
+
|
|
90
|
+
```dart
|
|
91
|
+
// BAD — new function instance every build → child always rebuilds
|
|
92
|
+
ListView.builder(
|
|
93
|
+
itemBuilder: (context, index) => ItemTile(
|
|
94
|
+
onTap: () => _handleTap(items[index]), // new closure each build
|
|
95
|
+
),
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
// GOOD — stable callback reference
|
|
99
|
+
ListView.builder(
|
|
100
|
+
itemBuilder: (context, index) => ItemTile(
|
|
101
|
+
item: items[index],
|
|
102
|
+
onTap: _handleTap, // stable reference, pass item via widget
|
|
103
|
+
),
|
|
104
|
+
)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Selective setState — Minimize Rebuild Scope
|
|
108
|
+
|
|
109
|
+
```dart
|
|
110
|
+
// BAD — setState at root rebuilds EVERYTHING
|
|
111
|
+
class _HomePageState extends State<HomePage> {
|
|
112
|
+
int _counter = 0;
|
|
113
|
+
void _increment() => setState(() => _counter++);
|
|
114
|
+
|
|
115
|
+
Widget build(BuildContext context) {
|
|
116
|
+
return Column(children: [
|
|
117
|
+
const HeavyHeader(), // rebuilds unnecessarily!
|
|
118
|
+
Text('$_counter'),
|
|
119
|
+
const HeavyFooter(), // rebuilds unnecessarily!
|
|
120
|
+
]);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// GOOD — isolate changing widget with a dedicated StatefulWidget or Builder
|
|
125
|
+
class _HomePageState extends State<HomePage> {
|
|
126
|
+
Widget build(BuildContext context) {
|
|
127
|
+
return Column(children: [
|
|
128
|
+
const HeavyHeader(),
|
|
129
|
+
const CounterWidget(), // only this subtree rebuilds
|
|
130
|
+
const HeavyFooter(),
|
|
131
|
+
]);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### RepaintBoundary — Isolate Expensive Paints
|
|
137
|
+
|
|
138
|
+
```dart
|
|
139
|
+
// Wrap frequently-animating or complex widgets
|
|
140
|
+
RepaintBoundary(
|
|
141
|
+
child: CustomPaint(
|
|
142
|
+
painter: ComplexChartPainter(data),
|
|
143
|
+
size: const Size(300, 200),
|
|
144
|
+
),
|
|
145
|
+
)
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Use when: animations, custom painters, frequently-updating regions. Don't overuse — each boundary has memory overhead for its own layer.
|
|
149
|
+
|
|
150
|
+
## 2. List & Scroll Performance
|
|
151
|
+
|
|
152
|
+
### ListView.builder (Lazy Construction)
|
|
153
|
+
|
|
154
|
+
```dart
|
|
155
|
+
// BAD — builds ALL items upfront, even offscreen
|
|
156
|
+
ListView(
|
|
157
|
+
children: items.map((item) => ItemTile(item: item)).toList(),
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
// GOOD — only builds visible items + cacheExtent
|
|
161
|
+
ListView.builder(
|
|
162
|
+
itemCount: items.length,
|
|
163
|
+
itemExtent: 72, // skip measurement pass — major win for fixed-height items
|
|
164
|
+
itemBuilder: (context, index) => ItemTile(item: items[index]),
|
|
165
|
+
)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### cacheExtent Tuning
|
|
169
|
+
|
|
170
|
+
```dart
|
|
171
|
+
ListView.builder(
|
|
172
|
+
cacheExtent: 500, // pixels to pre-build offscreen (default: 250)
|
|
173
|
+
// Higher = smoother fast scrolling, but more memory
|
|
174
|
+
// Lower = less memory, but may show blank frames during fast scroll
|
|
175
|
+
)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### SliverList for Mixed Scroll Content
|
|
179
|
+
|
|
180
|
+
```dart
|
|
181
|
+
// Instead of nesting ListView inside ScrollView:
|
|
182
|
+
CustomScrollView(
|
|
183
|
+
slivers: [
|
|
184
|
+
const SliverToBoxAdapter(child: HeaderWidget()),
|
|
185
|
+
SliverList.builder(
|
|
186
|
+
itemCount: items.length,
|
|
187
|
+
itemBuilder: (context, index) => ItemTile(item: items[index]),
|
|
188
|
+
),
|
|
189
|
+
const SliverToBoxAdapter(child: FooterWidget()),
|
|
190
|
+
],
|
|
191
|
+
)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### Keys for Stateful List Items
|
|
195
|
+
|
|
196
|
+
```dart
|
|
197
|
+
// BAD — index keys cause state mismatch on reorder/insert
|
|
198
|
+
ListView.builder(
|
|
199
|
+
itemBuilder: (context, index) => ItemTile(key: ValueKey(index)),
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
// GOOD — stable unique key preserves widget state
|
|
203
|
+
ListView.builder(
|
|
204
|
+
itemBuilder: (context, index) => ItemTile(key: ValueKey(items[index].id)),
|
|
205
|
+
)
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## 3. Image Optimization
|
|
209
|
+
|
|
210
|
+
### CachedNetworkImage (Disk + Memory Cache)
|
|
211
|
+
|
|
212
|
+
```dart
|
|
213
|
+
import 'package:cached_network_image/cached_network_image.dart';
|
|
214
|
+
|
|
215
|
+
CachedNetworkImage(
|
|
216
|
+
imageUrl: '$cdnBaseUrl/product/${product.id}.webp?w=400&q=75',
|
|
217
|
+
placeholder: (context, url) => const ShimmerPlaceholder(),
|
|
218
|
+
errorWidget: (context, url, error) => const Icon(Icons.error),
|
|
219
|
+
memCacheWidth: 400, // decode at display size, not full resolution
|
|
220
|
+
memCacheHeight: 400,
|
|
221
|
+
fadeInDuration: const Duration(milliseconds: 200),
|
|
222
|
+
)
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Precache Critical Images
|
|
226
|
+
|
|
227
|
+
```dart
|
|
228
|
+
@override
|
|
229
|
+
void didChangeDependencies() {
|
|
230
|
+
super.didChangeDependencies();
|
|
231
|
+
// Precache hero images before they're needed
|
|
232
|
+
precacheImage(
|
|
233
|
+
const AssetImage('assets/images/hero.webp'),
|
|
234
|
+
context,
|
|
235
|
+
);
|
|
236
|
+
precacheImage(
|
|
237
|
+
NetworkImage('$cdnUrl/banner.webp'),
|
|
238
|
+
context,
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Resolution-Aware Loading
|
|
244
|
+
|
|
245
|
+
```dart
|
|
246
|
+
// Request appropriately-sized images from CDN
|
|
247
|
+
final pixelRatio = MediaQuery.devicePixelRatioOf(context);
|
|
248
|
+
final width = (containerWidth * pixelRatio).toInt();
|
|
249
|
+
final imageUrl = '$cdnUrl/image.webp?w=$width&q=80&fm=webp';
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Image Memory Budget
|
|
253
|
+
|
|
254
|
+
| Device RAM | ImageCache Size | ImageCache Size Bytes |
|
|
255
|
+
|---|---|---|
|
|
256
|
+
| 1-2 GB | 50 images | 50 MB |
|
|
257
|
+
| 3-4 GB | 100 images | 100 MB |
|
|
258
|
+
| 6+ GB | 200 images | 200 MB |
|
|
259
|
+
|
|
260
|
+
```dart
|
|
261
|
+
// Tune ImageCache in main()
|
|
262
|
+
PaintingBinding.instance.imageCache.maximumSize = 100;
|
|
263
|
+
PaintingBinding.instance.imageCache.maximumSizeBytes = 100 << 20; // 100 MB
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
## 4. Memory Management
|
|
267
|
+
|
|
268
|
+
### Top Memory Leak Sources (90% of Leaks)
|
|
269
|
+
|
|
270
|
+
**1. Undisposed Controllers:**
|
|
271
|
+
```dart
|
|
272
|
+
// BAD — controller leaks
|
|
273
|
+
class _MyState extends State<MyWidget> {
|
|
274
|
+
final _controller = TextEditingController();
|
|
275
|
+
final _scrollController = ScrollController();
|
|
276
|
+
// ... no dispose()
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// GOOD — always dispose
|
|
280
|
+
@override
|
|
281
|
+
void dispose() {
|
|
282
|
+
_controller.dispose();
|
|
283
|
+
_scrollController.dispose();
|
|
284
|
+
super.dispose();
|
|
285
|
+
}
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
**2. Uncancelled Stream Subscriptions:**
|
|
289
|
+
```dart
|
|
290
|
+
// BAD — subscription leaks after widget unmounts
|
|
291
|
+
late StreamSubscription _sub;
|
|
292
|
+
|
|
293
|
+
@override
|
|
294
|
+
void initState() {
|
|
295
|
+
super.initState();
|
|
296
|
+
_sub = myStream.listen((data) => setState(() => _data = data));
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// GOOD — cancel in dispose
|
|
300
|
+
@override
|
|
301
|
+
void dispose() {
|
|
302
|
+
_sub.cancel();
|
|
303
|
+
super.dispose();
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
**3. AnimationController Leaks:**
|
|
308
|
+
```dart
|
|
309
|
+
// GOOD — dispose animation controllers
|
|
310
|
+
class _AnimState extends State<AnimWidget> with SingleTickerProviderStateMixin {
|
|
311
|
+
late final AnimationController _controller;
|
|
312
|
+
|
|
313
|
+
@override
|
|
314
|
+
void initState() {
|
|
315
|
+
super.initState();
|
|
316
|
+
_controller = AnimationController(vsync: this, duration: const Duration(milliseconds: 300));
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
@override
|
|
320
|
+
void dispose() {
|
|
321
|
+
_controller.dispose();
|
|
322
|
+
super.dispose();
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**4. Timer Leaks:**
|
|
328
|
+
```dart
|
|
329
|
+
Timer? _timer;
|
|
330
|
+
|
|
331
|
+
@override
|
|
332
|
+
void initState() {
|
|
333
|
+
super.initState();
|
|
334
|
+
_timer = Timer.periodic(const Duration(seconds: 5), (_) => _refresh());
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
@override
|
|
338
|
+
void dispose() {
|
|
339
|
+
_timer?.cancel();
|
|
340
|
+
super.dispose();
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
## 5. Dart Isolates — Offload Heavy Work
|
|
345
|
+
|
|
346
|
+
### When to Use Isolates
|
|
347
|
+
|
|
348
|
+
Any operation that takes > 16ms on the main isolate should be offloaded:
|
|
349
|
+
- JSON parsing of large payloads (> 50KB)
|
|
350
|
+
- Image processing / compression
|
|
351
|
+
- Encryption / hashing
|
|
352
|
+
- Complex data transformations
|
|
353
|
+
- Database-heavy operations
|
|
354
|
+
- File I/O on large files
|
|
355
|
+
|
|
356
|
+
### Isolate.run() (Dart 2.19+ / Dart 3.x)
|
|
357
|
+
|
|
358
|
+
```dart
|
|
359
|
+
// Simple one-shot computation
|
|
360
|
+
final result = await Isolate.run(() {
|
|
361
|
+
// Runs in a separate isolate — won't block UI
|
|
362
|
+
return heavyJsonParse(largeJsonString);
|
|
363
|
+
});
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### compute() Function
|
|
367
|
+
|
|
368
|
+
```dart
|
|
369
|
+
// Flutter's convenience wrapper — serializes input/output automatically
|
|
370
|
+
final parsed = await compute(parseProducts, jsonString);
|
|
371
|
+
|
|
372
|
+
// Top-level or static function (required for compute)
|
|
373
|
+
List<Product> parseProducts(String json) {
|
|
374
|
+
final decoded = jsonDecode(json) as List;
|
|
375
|
+
return decoded.map((e) => Product.fromJson(e)).toList();
|
|
376
|
+
}
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
### Long-Running Isolate with Ports
|
|
380
|
+
|
|
381
|
+
```dart
|
|
382
|
+
// For continuous background work (e.g., real-time data processing)
|
|
383
|
+
final receivePort = ReceivePort();
|
|
384
|
+
await Isolate.spawn(_backgroundWorker, receivePort.sendPort);
|
|
385
|
+
|
|
386
|
+
final sendPort = await receivePort.first as SendPort;
|
|
387
|
+
// Send work to the isolate
|
|
388
|
+
sendPort.send(workPayload);
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**Rule of thumb:** Use `Isolate.run()` for one-shot tasks, `compute()` for simple transformations, and `Isolate.spawn()` for long-running workers.
|
|
392
|
+
|
|
393
|
+
## 6. State Management Performance
|
|
394
|
+
|
|
395
|
+
### Performance Characteristics
|
|
396
|
+
|
|
397
|
+
| Library | Rebuild Granularity | Bundle Impact | Best For |
|
|
398
|
+
|---|---|---|---|
|
|
399
|
+
| **Riverpod 2.x** | select() per-field | ~15 KB | Enterprise, compile-time safe |
|
|
400
|
+
| **Provider** | Consumer/Selector | ~10 KB | Simple apps, official recommendation |
|
|
401
|
+
| **Bloc/Cubit** | BlocSelector | ~20 KB | Complex async, event-driven |
|
|
402
|
+
| **GetX** | Obx() per-observable | ~25 KB | Rapid prototyping (not recommended for prod) |
|
|
403
|
+
| setState | Widget-level | 0 KB | Ephemeral UI state only |
|
|
404
|
+
|
|
405
|
+
### Selective Rebuilds (Critical Pattern)
|
|
406
|
+
|
|
407
|
+
```dart
|
|
408
|
+
// BAD — entire widget rebuilds when ANY field in UserState changes
|
|
409
|
+
Widget build(BuildContext context) {
|
|
410
|
+
final user = ref.watch(userProvider);
|
|
411
|
+
return Text(user.name);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// GOOD — only rebuilds when name changes (Riverpod)
|
|
415
|
+
Widget build(BuildContext context) {
|
|
416
|
+
final name = ref.watch(userProvider.select((u) => u.name));
|
|
417
|
+
return Text(name);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// GOOD — Bloc equivalent
|
|
421
|
+
BlocSelector<UserCubit, UserState, String>(
|
|
422
|
+
selector: (state) => state.name,
|
|
423
|
+
builder: (context, name) => Text(name),
|
|
424
|
+
)
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
### Avoid Context.watch at Widget Root
|
|
428
|
+
|
|
429
|
+
```dart
|
|
430
|
+
// BAD — Provider: watching at Scaffold level rebuilds everything
|
|
431
|
+
Widget build(BuildContext context) {
|
|
432
|
+
final cart = context.watch<CartModel>();
|
|
433
|
+
return Scaffold(
|
|
434
|
+
appBar: AppBar(title: Text('Cart (${cart.itemCount})')),
|
|
435
|
+
body: /* expensive subtree */,
|
|
436
|
+
);
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// GOOD — isolate the watch with Consumer
|
|
440
|
+
Widget build(BuildContext context) {
|
|
441
|
+
return Scaffold(
|
|
442
|
+
appBar: AppBar(
|
|
443
|
+
title: Consumer<CartModel>(
|
|
444
|
+
builder: (context, cart, _) => Text('Cart (${cart.itemCount})'),
|
|
445
|
+
),
|
|
446
|
+
),
|
|
447
|
+
body: /* expensive subtree — NOT rebuilt */,
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
## 7. Impeller Rendering Engine
|
|
453
|
+
|
|
454
|
+
### Status (Flutter 3.27+)
|
|
455
|
+
|
|
456
|
+
- **iOS**: Default since Flutter 3.16 — stable, production-ready
|
|
457
|
+
- **Android**: Default on API 29+ (Vulkan) since Flutter 3.27
|
|
458
|
+
- **Fallback**: OpenGL on older Android devices without Vulkan
|
|
459
|
+
|
|
460
|
+
### What Impeller Solves
|
|
461
|
+
|
|
462
|
+
| Problem (Skia) | Solution (Impeller) |
|
|
463
|
+
|---|---|
|
|
464
|
+
| First-frame shader compilation jank | All shaders pre-compiled at build time |
|
|
465
|
+
| Runtime shader compilation stalls | Zero runtime shader compilation |
|
|
466
|
+
| Inconsistent frame times | Predictable frame rendering |
|
|
467
|
+
| GPU cache misses on new visuals | Tile-based rendering (256x256 tiles) |
|
|
468
|
+
|
|
469
|
+
### SkSL Warmup (Legacy — Only for Skia Fallback)
|
|
470
|
+
|
|
471
|
+
```bash
|
|
472
|
+
# Only needed if targeting Skia (Android API < 29)
|
|
473
|
+
# 1. Capture shaders
|
|
474
|
+
flutter run --profile --cache-sksl
|
|
475
|
+
|
|
476
|
+
# 2. Export shader bundle
|
|
477
|
+
flutter build apk --bundle-sksl-path flutter_01.sksl.json
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
**With Impeller (default), SkSL warmup is unnecessary.**
|
|
481
|
+
|
|
482
|
+
### Impeller-Specific Optimizations
|
|
483
|
+
|
|
484
|
+
```dart
|
|
485
|
+
// AVOID — complex clipping paths increase draw calls in Impeller
|
|
486
|
+
ClipPath(
|
|
487
|
+
clipper: ComplexCustomClipper(), // expensive with Impeller
|
|
488
|
+
child: /* ... */,
|
|
489
|
+
)
|
|
490
|
+
|
|
491
|
+
// PREFER — simple clip shapes
|
|
492
|
+
ClipRRect(
|
|
493
|
+
borderRadius: BorderRadius.circular(16),
|
|
494
|
+
child: /* ... */,
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
// AVOID — multiple overlapping opacity layers
|
|
498
|
+
Opacity(opacity: 0.5, child: /* ... */) // creates extra layer
|
|
499
|
+
|
|
500
|
+
// PREFER — use color opacity or AnimatedOpacity
|
|
501
|
+
Container(color: Colors.black.withOpacity(0.5))
|
|
502
|
+
AnimatedOpacity(opacity: 0.5, child: /* ... */)
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
## 8. Animation Performance
|
|
506
|
+
|
|
507
|
+
### Prefer Implicit Animations for Simple Cases
|
|
508
|
+
|
|
509
|
+
```dart
|
|
510
|
+
// GOOD — Flutter handles optimization automatically
|
|
511
|
+
AnimatedContainer(
|
|
512
|
+
duration: const Duration(milliseconds: 300),
|
|
513
|
+
curve: Curves.easeOut,
|
|
514
|
+
width: _expanded ? 200 : 100,
|
|
515
|
+
height: _expanded ? 200 : 100,
|
|
516
|
+
color: _expanded ? Colors.blue : Colors.red,
|
|
517
|
+
)
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
### AnimatedBuilder for Complex Animations
|
|
521
|
+
|
|
522
|
+
```dart
|
|
523
|
+
// GOOD — only the builder subtree rebuilds, not the entire widget
|
|
524
|
+
AnimatedBuilder(
|
|
525
|
+
animation: _controller,
|
|
526
|
+
builder: (context, child) {
|
|
527
|
+
return Transform.rotate(
|
|
528
|
+
angle: _controller.value * 2 * pi,
|
|
529
|
+
child: child, // child is cached — NOT rebuilt
|
|
530
|
+
);
|
|
531
|
+
},
|
|
532
|
+
child: const ExpensiveWidget(), // built once, reused
|
|
533
|
+
)
|
|
534
|
+
```
|
|
535
|
+
|
|
536
|
+
### Avoid Layout-Triggering Animations
|
|
537
|
+
|
|
538
|
+
```dart
|
|
539
|
+
// BAD — changes size → triggers layout every frame
|
|
540
|
+
AnimatedBuilder(
|
|
541
|
+
animation: _controller,
|
|
542
|
+
builder: (context, child) {
|
|
543
|
+
return SizedBox(
|
|
544
|
+
width: 100 * _controller.value, // layout change every frame!
|
|
545
|
+
child: child,
|
|
546
|
+
);
|
|
547
|
+
},
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
// GOOD — Transform doesn't trigger layout
|
|
551
|
+
AnimatedBuilder(
|
|
552
|
+
animation: _controller,
|
|
553
|
+
builder: (context, child) {
|
|
554
|
+
return Transform.scale(
|
|
555
|
+
scale: _controller.value, // paint-only — no layout
|
|
556
|
+
child: child,
|
|
557
|
+
);
|
|
558
|
+
},
|
|
559
|
+
)
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
## 9. Startup Optimization
|
|
563
|
+
|
|
564
|
+
### Cold Start Targets
|
|
565
|
+
|
|
566
|
+
| Platform | Target | Warning | Fail |
|
|
567
|
+
|---|---|---|---|
|
|
568
|
+
| iOS flagship | ≤ 1.0s | ≤ 1.5s | > 2.0s |
|
|
569
|
+
| Mid-tier Android | ≤ 1.5s | ≤ 2.5s | > 3.0s |
|
|
570
|
+
| Low-end Android | ≤ 2.0s | ≤ 3.0s | > 4.0s |
|
|
571
|
+
|
|
572
|
+
### Quick Wins
|
|
573
|
+
|
|
574
|
+
1. **Deferred initialization** — lazy-load services not needed at startup
|
|
575
|
+
2. **Deferred imports** — split rarely-used features into deferred loads
|
|
576
|
+
3. **Native splash** — use `flutter_native_splash` for instant visual
|
|
577
|
+
4. **Reduce main() work** — defer analytics, crash reporting, remote config
|
|
578
|
+
|
|
579
|
+
```dart
|
|
580
|
+
void main() async {
|
|
581
|
+
WidgetsFlutterBinding.ensureInitialized();
|
|
582
|
+
// Only essential init here
|
|
583
|
+
await _initCriticalServices(); // auth, storage
|
|
584
|
+
runApp(const MyApp());
|
|
585
|
+
// Defer non-critical init
|
|
586
|
+
_initDeferredServices(); // analytics, crash reporting, remote config
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
Future<void> _initDeferredServices() async {
|
|
590
|
+
await Future.delayed(const Duration(seconds: 1)); // after first frame
|
|
591
|
+
await Firebase.initializeApp();
|
|
592
|
+
await CrashReporting.init();
|
|
593
|
+
}
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
### Deferred Imports (Code Splitting)
|
|
597
|
+
|
|
598
|
+
```dart
|
|
599
|
+
// Only load heavy feature when accessed
|
|
600
|
+
import 'package:my_app/features/reports/reports.dart' deferred as reports;
|
|
601
|
+
|
|
602
|
+
Future<void> openReports() async {
|
|
603
|
+
await reports.loadLibrary();
|
|
604
|
+
navigator.push(reports.ReportsPage());
|
|
605
|
+
}
|
|
606
|
+
```
|
|
607
|
+
|
|
608
|
+
## 10. App Size Optimization
|
|
609
|
+
|
|
610
|
+
### Targets
|
|
611
|
+
|
|
612
|
+
| Metric | Good | Warning | Fail |
|
|
613
|
+
|---|---|---|---|
|
|
614
|
+
| APK (arm64) | ≤ 15 MB | ≤ 30 MB | > 50 MB |
|
|
615
|
+
| App Bundle (AAB) | ≤ 20 MB | ≤ 40 MB | > 60 MB |
|
|
616
|
+
| IPA | ≤ 30 MB | ≤ 50 MB | > 80 MB |
|
|
617
|
+
|
|
618
|
+
### Size Analysis
|
|
619
|
+
|
|
620
|
+
```bash
|
|
621
|
+
# Analyze app size
|
|
622
|
+
flutter build apk --analyze-size
|
|
623
|
+
flutter build appbundle --analyze-size
|
|
624
|
+
flutter build ios --analyze-size
|
|
625
|
+
|
|
626
|
+
# Compare two builds
|
|
627
|
+
flutter build apk --analyze-size --code-size-directory=v1
|
|
628
|
+
# ... make changes ...
|
|
629
|
+
flutter build apk --analyze-size --code-size-directory=v2
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
### Reduction Techniques
|
|
633
|
+
|
|
634
|
+
```bash
|
|
635
|
+
# Split APK per ABI (~40% reduction)
|
|
636
|
+
flutter build apk --split-per-abi
|
|
637
|
+
|
|
638
|
+
# Enable obfuscation + tree shaking
|
|
639
|
+
flutter build apk --obfuscate --split-debug-info=debug-info/
|
|
640
|
+
|
|
641
|
+
# Use App Bundle for Play Store (automatic per-device optimization)
|
|
642
|
+
flutter build appbundle
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
```dart
|
|
646
|
+
// Use WebP/AVIF instead of PNG for assets (50-70% smaller)
|
|
647
|
+
// Compress with: cwebp -q 80 input.png -o output.webp
|
|
648
|
+
|
|
649
|
+
// Remove unused packages from pubspec.yaml
|
|
650
|
+
// Run: flutter pub deps --no-dev | grep -c "^" // count dependencies
|
|
651
|
+
```
|
|
652
|
+
|
|
653
|
+
## 11. Anti-Patterns Quick Reference
|
|
654
|
+
|
|
655
|
+
| Anti-Pattern | Impact | Fix |
|
|
656
|
+
|---|---|---|
|
|
657
|
+
| No `const` on static widgets | Unnecessary rebuilds | Add `const` keyword |
|
|
658
|
+
| `setState` at widget root | Entire tree rebuilds | Extract StatefulWidget or use Builder |
|
|
659
|
+
| `Opacity` widget for fading | Creates extra render layer | Use `AnimatedOpacity` or color opacity |
|
|
660
|
+
| `ListView(children: [...])` for large lists | All items built upfront | Use `ListView.builder` |
|
|
661
|
+
| No `itemExtent` on fixed-height lists | Layout measurement each item | Add `itemExtent` |
|
|
662
|
+
| Full-res images for thumbnails | ~8MB RAM per image on Android | Request sized images from CDN |
|
|
663
|
+
| Heavy work in `build()` | Blocks frame rendering | Move to `initState` or isolate |
|
|
664
|
+
| Undisposed controllers/subs | Memory leaks | `dispose()` everything |
|
|
665
|
+
| Synchronous file I/O on main isolate | UI jank during I/O | Use `Isolate.run()` or `compute()` |
|
|
666
|
+
| Deep widget nesting (10+ levels) | Slow layout pass | Extract into separate widgets |
|
|
667
|
+
| `Column` + `SingleChildScrollView` for lists | No lazy loading | Use `ListView.builder` |
|
|
668
|
+
| Complex `ClipPath` with Impeller | Increased draw calls | Use simple `ClipRRect` |
|
|
669
|
+
| `context.watch` at root widget | Broadcast rebuilds | Use `Consumer` / `select()` |
|
|
670
|
+
| `setState` inside `StreamBuilder` | Double rebuild | Let StreamBuilder manage state |
|
|
671
|
+
| No `key` on dynamic list items | State mismatch on reorder | Use `ValueKey(item.id)` |
|
|
672
|
+
|
|
673
|
+
## 12. Performance Budgets
|
|
674
|
+
|
|
675
|
+
| Metric | Good | Warning | Fail |
|
|
676
|
+
|---|---|---|---|
|
|
677
|
+
| Frame Render Time (60fps) | ≤ 16ms | ≤ 24ms | > 32ms |
|
|
678
|
+
| Frame Render Time (120fps) | ≤ 8ms | ≤ 12ms | > 16ms |
|
|
679
|
+
| Cold Start TTI | ≤ 1.5s iOS / ≤ 2.0s Android | ≤ 2.5s | > 3.5s |
|
|
680
|
+
| Widget Rebuild Count/Frame | ≤ 5 | ≤ 15 | > 30 |
|
|
681
|
+
| Memory Peak (3GB device) | ≤ 150 MB | ≤ 250 MB | > 350 MB |
|
|
682
|
+
| APK Size (arm64) | ≤ 15 MB | ≤ 30 MB | > 50 MB |
|
|
683
|
+
| Scroll FPS | 60/120 FPS | 50-55 FPS | < 45 FPS |
|
|
684
|
+
| Image Decode Time | ≤ 5ms | ≤ 15ms | > 30ms |
|
|
685
|
+
|
|
686
|
+
## 13. DevTools Profiling
|
|
687
|
+
|
|
688
|
+
| Need | Tool |
|
|
689
|
+
|---|---|
|
|
690
|
+
| Frame rendering analysis | Performance Overlay (`showPerformanceOverlay: true`) |
|
|
691
|
+
| Build/Layout/Paint timing | DevTools → Performance tab |
|
|
692
|
+
| Widget rebuild tracking | DevTools → Performance → Track Widget Rebuilds |
|
|
693
|
+
| Memory leaks | DevTools → Memory tab → Heap Snapshots |
|
|
694
|
+
| CPU hotspots | DevTools → CPU Profiler |
|
|
695
|
+
| App size analysis | `flutter build --analyze-size` |
|
|
696
|
+
| Widget tree inspection | DevTools → Widget Inspector |
|
|
697
|
+
| Network requests | DevTools → Network tab |
|
|
698
|
+
| Jank detection | DevTools → Performance → Jank Flags |
|
|
699
|
+
|
|
700
|
+
### Profile Mode (Required for Accurate Benchmarks)
|
|
701
|
+
|
|
702
|
+
```bash
|
|
703
|
+
# ALWAYS profile in profile mode — debug mode is 10x slower
|
|
704
|
+
flutter run --profile
|
|
705
|
+
|
|
706
|
+
# On device (not emulator) for realistic numbers
|
|
707
|
+
flutter run --profile -d <device-id>
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
### Performance Overlay
|
|
711
|
+
|
|
712
|
+
```dart
|
|
713
|
+
MaterialApp(
|
|
714
|
+
showPerformanceOverlay: true, // shows GPU/UI thread frame times
|
|
715
|
+
)
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
## 14. Optimization Checklist by Effort
|
|
719
|
+
|
|
720
|
+
### Level 1 — Quick Wins (< 1 day)
|
|
721
|
+
- [ ] Add `const` to all static widgets (enable lint rules)
|
|
722
|
+
- [ ] Replace `ListView(children)` with `ListView.builder`
|
|
723
|
+
- [ ] Add `itemExtent` to fixed-height lists
|
|
724
|
+
- [ ] Dispose all controllers and subscriptions
|
|
725
|
+
- [ ] Move heavy computation out of `build()`
|
|
726
|
+
- [ ] Use `CachedNetworkImage` for network images
|
|
727
|
+
- [ ] Request appropriately-sized images from CDN
|
|
728
|
+
- [ ] Enable `--split-per-abi` for APK builds
|
|
729
|
+
- [ ] Profile in profile mode on real device
|
|
730
|
+
|
|
731
|
+
### Level 2 — Standard (1-3 days)
|
|
732
|
+
- [ ] Extract large build methods into separate widgets
|
|
733
|
+
- [ ] Add `RepaintBoundary` for animations/custom painters
|
|
734
|
+
- [ ] Use `select()` in state management (Riverpod/Provider/Bloc)
|
|
735
|
+
- [ ] Offload JSON parsing to isolates (`compute()`)
|
|
736
|
+
- [ ] Implement deferred imports for heavy features
|
|
737
|
+
- [ ] Tune `cacheExtent` on scroll views
|
|
738
|
+
- [ ] Add `precacheImage` for critical images
|
|
739
|
+
- [ ] Configure `ImageCache` size limits
|
|
740
|
+
- [ ] Defer non-critical service initialization
|
|
741
|
+
|
|
742
|
+
### Level 3 — Advanced (1-2 weeks)
|
|
743
|
+
- [ ] Migrate to Impeller (verify on Android API 29+)
|
|
744
|
+
- [ ] Replace `Opacity` with `AnimatedOpacity` / color opacity
|
|
745
|
+
- [ ] Use `Transform` instead of layout-changing animations
|
|
746
|
+
- [ ] Implement `AutomaticKeepAliveClientMixin` for tab preservation
|
|
747
|
+
- [ ] Set up CI performance benchmarks
|
|
748
|
+
- [ ] Analyze and reduce app size (`--analyze-size`)
|
|
749
|
+
- [ ] Implement cursor-based pagination for large lists
|
|
750
|
+
- [ ] Profile and optimize startup time
|
|
751
|
+
|
|
752
|
+
### Level 4 — Expert (> 2 weeks)
|
|
753
|
+
- [ ] Custom `RenderObject` for performance-critical layouts
|
|
754
|
+
- [ ] Long-running isolates for background processing
|
|
755
|
+
- [ ] Platform channel optimization (reduce serialization)
|
|
756
|
+
- [ ] Custom Impeller-aware painting strategies
|
|
757
|
+
- [ ] Automated performance regression testing in CI
|
|
758
|
+
|
|
759
|
+
## Scan Process
|
|
760
|
+
|
|
761
|
+
When asked to analyze a Flutter project for performance issues:
|
|
762
|
+
|
|
763
|
+
### 1. Determine Scope
|
|
764
|
+
Ask (or infer): full audit, specific screen, startup, memory, rendering, or app size.
|
|
765
|
+
|
|
766
|
+
### 2. Run Automated Checks
|
|
767
|
+
|
|
768
|
+
```bash
|
|
769
|
+
# Anti-pattern scan
|
|
770
|
+
./scripts/flutter-perf-scan.sh <project-path>
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
### 3. Manual Review
|
|
774
|
+
Follow the anti-patterns table above and check each category.
|
|
775
|
+
|
|
776
|
+
### 4. Report Findings
|
|
777
|
+
|
|
778
|
+
Structure by severity:
|
|
779
|
+
```
|
|
780
|
+
CRITICAL — Causes visible jank or crashes
|
|
781
|
+
HIGH — Significant perf impact, fix before release
|
|
782
|
+
MEDIUM — Defense-in-depth improvement
|
|
783
|
+
LOW — Best practice recommendation
|
|
784
|
+
```
|
|
785
|
+
|
|
786
|
+
For each finding:
|
|
787
|
+
```
|
|
788
|
+
**[SEVERITY] Title**
|
|
789
|
+
Location: file:line
|
|
790
|
+
Impact: Frame time / memory / size impact
|
|
791
|
+
Before: The anti-pattern code
|
|
792
|
+
After: The optimized code
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
### 5. Prioritize Fixes
|
|
796
|
+
1. Critical fixes (visible jank, memory leaks)
|
|
797
|
+
2. High fixes (rebuild storms, missing lazy loading)
|
|
798
|
+
3. Medium fixes (missing const, suboptimal caching)
|
|
799
|
+
4. Low fixes (style improvements)
|
|
800
|
+
|
|
801
|
+
## References
|
|
802
|
+
|
|
803
|
+
- `references/flutter-patterns.md` — Advanced patterns: custom RenderObjects, platform channels, Impeller internals, CI benchmarking
|