# Flutter Performance – Profileringsguide ## Mål | Mätpunkt | Gränsvärde | |---|---| | Frame build-tid (60 Hz) | < 16 ms | | Frame build-tid (120 Hz) | < 8 ms | | Scroll jank (tappade frames) | 0 vid normal scroll | | Minnesfotavtryck (app) | < 200 MB | --- ## 1. Starta i profile-läge Kör alltid profilmätningar i **profile mode**, inte debug. Debug-läget har JIT-kompilering och extra overhead. ```bash # Mot fysisk enhet flutter run --profile # Mot Chrome (web) flutter run -d chrome --profile ``` --- ## 2. Flutter DevTools – Öppna ```bash flutter pub global activate devtools flutter pub global run devtools ``` Eller anslut direkt från terminalen när appen körs i profile mode – Flutter skriver ut en DevTools-URL. --- ## 3. Timeline – Mät frame-tider 1. Öppna **Performance**-fliken i DevTools. 2. Klicka **Record**. 3. Utför den aktion du vill mäta (t.ex. byt vy, scrolla). 4. Klicka **Stop**. 5. Granska: - **UI thread** (Dart-kod) – bör vara < 16 ms per frame. - **Raster thread** (GPU) – bör vara < 16 ms per frame. - Röda/gula staplar = jank. ### Kritiska mätpunkter i appen | Scenario | Vad att leta efter | |---|---| | Byta vy (NavigationBar) | Frame-tid vid `StatefulShellRoute`-byte; bör vara < 32 ms totalt | | Scrolla receptlista | Inga röda frames; `GridView.builder` bör recykla element | | Scrolla adminpaneler | `ListView.builder` i embedded-läge; verifiera att ingen `NeverScrollableScrollPhysics` blockerar | | Kvittoimport – kryssa i rad | Endast den berörda raden bör rebuilda (`ConsumerWidget.select`) | | Kvittoimport – "Välj alla" | Batch-uppdatering via `setSelectedForAll` – en enda `state =` | --- ## 4. Widget Rebuild-spårning Aktivera rebuild-räknare i DevTools under **Inspector → Widget rebuild counts**. Alternativt: lägg till tillfällig räknare i en widget: ```dart int _buildCount = 0; @override Widget build(BuildContext context, WidgetRef ref) { debugPrint('${widget.runtimeType} build #${++_buildCount}'); // ... } ``` ### Förväntade rebuild-mönster efter optimeringar - `_ReceiptImportResultRow` med index X ska bara rebuilda när `selected[X]` ändras, inte när andra rader kryssas. - `AppShell` ska inte rebuilda vid vy-byte (StatefulShellRoute bevarar grenar). - Admin-paneler ska inte rebuilda hela listan vid en alias-ändring. --- ## 5. Memory Profiler 1. DevTools → **Memory**-fliken. 2. Klicka **Take snapshot** före och efter en tung operation. 3. Jämför levande objekt – leta efter läckor (ackumulerade `StreamSubscription`, `Timer`, `Notifier`). --- ## 6. flutter analyze + dart fix ```bash flutter analyze dart fix --apply ``` Åtgärda alla varningar om `const` och onödiga rebuilds. --- ## 7. Identifierade optimeringar (genomförda) | Område | Åtgärd | Effekt | |---|---|---| | Admin-paneler | Tog bort `NeverScrollableScrollPhysics` + `shrinkWrap` | Scroll fungerar, O(n) layout istället för O(n²) | | Admin alias-lista | `ListView.builder` istället för spread | Virtualiserad lista | | FABs | Explicita `heroTag` på alla FABs | Eliminerar hero-animation-krasch vid vy-byte | | Scrollables | `PageStorageKey` på alla listvy | Scrollposition bevaras vid vy-byte | | Router | `StatefulShellRoute.indexedStack` | Branch-state bevaras; ingen ombyggnad vid tab-byte | | Kvittoimport – resultatlista | `ListView.builder` + `SizedBox` bound height | Virtualiserad; max 620 px synlig | | Kvittoimport – radwidget | `ConsumerWidget` med `provider.select((s) => s?.selected[index])` | Endast ändrad rad rebuildar vid checkbox-toggle | | Kvittoimport – batch-API | `setSelectedForAll`, `setSelectedForIndexes`, `setImportedResult` | En `state =` och en SharedPreferences-skrivning per operation | --- ## 8. Snabbtest – Verifiera förbättringar ```bash # Kör i profile mode och öppna DevTools automatiskt flutter run --profile --devtools-server-address=http://127.0.0.1:9100 ``` Kontrollchecklista: - [ ] Vy-byte NavigationBar: inga röda frames i Timeline - [ ] Scroll i receptlista: < 2 tappade frames per 100 frames - [ ] Scroll i admin-flikar: fungerar utan lock - [ ] Kvittoimport – checkbox-toggle: rebuild-räknare ökar bara för berörd rad - [ ] Kvittoimport – "Välj alla": en burst av rebuilds (en per rad), inga dubbla