[Prestaties][P1] Elke toetsaanslag herbouwt de hele AppShell, alle zichtbare miniaturen en (macOS) het native menu #2623

Closed
opened 2026-10-28 16:31:35 +00:00 by brenno · 0 comments
Owner

Symptoom

Elke toetsaanslag in een dia kost meer dan nodig, en dat schaalt met het aantal open tabbladen en het aantal zichtbare miniaturen.

Oorzaak

  1. De hele shell herbouwt per toetsaanslag. TabsNotifier luistert naar de stroom van elke DeckNotifier en zet bij elke emissie een nieuwe toestand: state = state.copyWith(tabs: List.from(state.tabs)) (lib/state/tabs_provider.dart:258). AppShell.build watcht tabsProvider volledig (lib/widgets/app_shell.dart:819), dus de hele shell — menubalk, sneltoetsen, DropTarget, de werkruimte met per-tab ProviderScopes — herbouwt bij elke letter, plus de ref.listen's die op tabwijzigingen de bannersynchronisatie draaien.
  2. macOS stuurt daarbij het complete native menu opnieuw. AppPlatformMenuBar maakt per build nieuwe PlatformMenuItems (lib/widgets/shell/app_menu_bar.dart:81). Flutter vergelijkt die met listEquals op identiteit (platform_menu_bar.dart, didUpdateWidget) en serialiseert bij verschil het hele menu over het platformkanaal — dus per toetsaanslag.
  3. Elke zichtbare miniatuur herbouwt en rekent deckbreed. SlideListPanel watcht het hele deck (lib/widgets/panels/slide_list_panel.dart:614) en bouwt per item een nieuwe SlideThumbnail met deckScopeCiaIndex(deck.slides) (regel 543) — een gang over álle dia's per zichtbare miniatuur, per toetsaanslag. De render-layoutmemo dempt de tekstinpassing (zie de kop van slide_layout_metrics.dart: "editing one slide rebuilds every visible thumbnail in the rail even though their slides never changed"), maar de herbouw zelf blijft.

Voorgestelde oplossing

  1. TabsNotifier alleen laten uitzenden als een tabfeit verandert (label, isDirty, isOpen, soort, bestandspad): vergelijk een afgeleide signatuur vóór state =. Wie per toetsaanslag iets nodig heeft, watcht het deck van zijn eigen tab al.
  2. Menu-items memoïseren (alleen opnieuw bouwen als een enabled-vlag of label verandert), of AppPlatformMenuBar laten vergelijken op een signatuur.
  3. In SlideListPanel deckbrede waarden (deckScopeCiaIndex, numberedListStarts, split-runindex) één keer per build berekenen en doorgeven; en per dia een widget-instantie cachen zolang Slide-identiteit en de deckbrede invoer gelijk zijn — Flutter slaat dan de herbouw van die miniatuur helemaal over.

Wat overeind moet blijven

Dirty-stip en label op het tabblad verversen direct; de beveiligings- en verbeterbanners volgen de tabwissel zoals nu.

Toets

Widgettest met een tellende build-teller: tien toetsaanslagen in dia 3 → AppShell herbouwt 0 keer (alleen bij de eerste die het tabblad vuil maakt), miniaturen van ongewijzigde dia's herbouwen 0 keer.

Onderdeel van de prestatie- en stabiliteitsinspectie van 28-10-2026 (EPIC #2637).

## Symptoom Elke toetsaanslag in een dia kost meer dan nodig, en dat schaalt met het aantal open tabbladen en het aantal zichtbare miniaturen. ## Oorzaak 1. **De hele shell herbouwt per toetsaanslag.** `TabsNotifier` luistert naar de stroom van elke `DeckNotifier` en zet bij **elke** emissie een nieuwe toestand: `state = state.copyWith(tabs: List.from(state.tabs))` (`lib/state/tabs_provider.dart:258`). `AppShell.build` watcht `tabsProvider` volledig (`lib/widgets/app_shell.dart:819`), dus de hele shell — menubalk, sneltoetsen, `DropTarget`, de werkruimte met per-tab `ProviderScope`s — herbouwt bij elke letter, plus de `ref.listen`'s die op tabwijzigingen de bannersynchronisatie draaien. 2. **macOS stuurt daarbij het complete native menu opnieuw.** `AppPlatformMenuBar` maakt per build nieuwe `PlatformMenuItem`s (`lib/widgets/shell/app_menu_bar.dart:81`). Flutter vergelijkt die met `listEquals` op identiteit (`platform_menu_bar.dart`, `didUpdateWidget`) en serialiseert bij verschil het hele menu over het platformkanaal — dus per toetsaanslag. 3. **Elke zichtbare miniatuur herbouwt en rekent deckbreed.** `SlideListPanel` watcht het hele deck (`lib/widgets/panels/slide_list_panel.dart:614`) en bouwt per item een nieuwe `SlideThumbnail` met `deckScopeCiaIndex(deck.slides)` (regel 543) — een gang over álle dia's per zichtbare miniatuur, per toetsaanslag. De render-layoutmemo dempt de tekstinpassing (zie de kop van `slide_layout_metrics.dart`: "editing one slide rebuilds every visible thumbnail in the rail even though their slides never changed"), maar de herbouw zelf blijft. ## Voorgestelde oplossing 1. `TabsNotifier` alleen laten uitzenden als een **tabfeit** verandert (label, `isDirty`, `isOpen`, soort, bestandspad): vergelijk een afgeleide signatuur vóór `state =`. Wie per toetsaanslag iets nodig heeft, watcht het deck van zijn eigen tab al. 2. Menu-items memoïseren (alleen opnieuw bouwen als een `enabled`-vlag of label verandert), of `AppPlatformMenuBar` laten vergelijken op een signatuur. 3. In `SlideListPanel` deckbrede waarden (`deckScopeCiaIndex`, `numberedListStarts`, split-runindex) één keer per build berekenen en doorgeven; en per dia een widget-instantie cachen zolang `Slide`-identiteit en de deckbrede invoer gelijk zijn — Flutter slaat dan de herbouw van die miniatuur helemaal over. ## Wat overeind moet blijven Dirty-stip en label op het tabblad verversen direct; de beveiligings- en verbeterbanners volgen de tabwissel zoals nu. ## Toets Widgettest met een tellende `build`-teller: tien toetsaanslagen in dia 3 → `AppShell` herbouwt 0 keer (alleen bij de eerste die het tabblad vuil maakt), miniaturen van ongewijzigde dia's herbouwen 0 keer. Onderdeel van de prestatie- en stabiliteitsinspectie van 28-10-2026 (EPIC #2637).
brenno 2026-10-29 12:19:39 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
LibreKAT/Ocideck#2623
No description provided.