Jelajahi Sumber

TASKS/CLAUDE: P2-1 restpunt — Value Key reset een gepagineerde lijst niet

Op het toestel gemeten: chips + App State + API-parameters kloppen (na
pull-to-refresh staat het weekendresultaat er), maar de lijst herlaadt niet
vanzelf. Oorzaak nagelezen in de gegenereerde code: het componentmodel is
pagina-eigendom (wrapWithModel + disposeOnWidgetDisposal = false), dus een
nieuwe ValueKey hangt de nieuwe State aan dezelfde pagingController.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
bob 3 jam lalu
induk
melakukan
476e93b6c9
2 mengubah file dengan 125 tambahan dan 50 penghapusan
  1. 63 0
      CLAUDE.md
  2. 62 50
      TASKS.md

+ 63 - 0
CLAUDE.md

@@ -1165,6 +1165,69 @@ een zoekterm die 2+ resultaten oplevert** — een gedeeld woordstuk uit de
 variabelenamen (`upload`, `nid`, `sessie`) in plaats van de volledige naam.
 Scheelt het hele hover-en-blind-klikken-ritueel hieronder.
 
+**⚠️ Een Value Key op een component-instantie reset een gepagineerde lijst
+BINNEN dat component NIET — de `pagingController` overleeft 'm gewoon.**
+Bewezen 2026-09-15 (P2-1, op het toestel gemeten én in de export nagelezen); dit
+corrigeert de eerdere aanname dat de key "op de instantie i.p.v. binnenin" het
+probleem zou oplossen. De keten:
+- `wrapWithModel()` zet `model.disposeOnWidgetDisposal = false` en levert het
+  model via een `Provider` aan; het model zelf is een veld op het **pagina**model
+  (`_model.xComponentModel1`), aangemaakt in de `initState` van de pagina.
+- In het component doet `initState` `createModel(context, () => XModel())`, en
+  dat leest **het bestaande model uit de Provider**. `_init()` slaat `initState`
+  over zodra `_isInitialized` true is.
+- Gevolg: een nieuwe `ValueKey` bouwt wel een nieuwe `State`, maar hangt 'm aan
+  hetzelfde model — inclusief `staggeredViewPagingController` mét alle al
+  geladen pagina's. Je ziet dus exact dezelfde lijst.
+- De nieuwe parameterwaarden komen er wél in: `setStaggeredViewController()`
+  wordt élke build aangeroepen en overschrijft `staggeredViewApiCall`. Alleen
+  vraagt niemand een nieuwe eerste pagina op.
+**Wat het wél bewijst als je met pull-to-refresh omlaag trekt:** de
+`RefreshIndicator` die FlutterFlow om zo'n lijst zet roept
+`pagingController?.refresh()` aan, en dán staat de gefilterde lijst er meteen.
+Gebruik dat als test om databinding en refresh-trigger uit elkaar te trekken.
+**Er is géén builder-actie die dat `refresh()` genereert** — in dit hele project
+komt de aanroep uitsluitend uit `RefreshIndicator.onRefresh` voor. En een
+`Navigate To` naar dezelfde pagina helpt niet: `nav.dart` bouwt de page met
+`key: state.pageKey`, en die is voor hetzelfde pad identiek, dus go_router
+hergebruikt de `State` en het paginamodel. Wil je een gepagineerde lijst tóch
+op een filterwissel laten herladen, dan is de enige werkende route een echte
+herlaad van de route (`context.pushReplacement(...)` vanuit custom code, zie het
+punt over `go_router` elders) — met als prijs dat de hele pagina opnieuw opbouwt.
+
+**Een component-parameter aan een custom function binden: klik het kleine
+icoontje NAAST het parameterlabel, zoek op de VOLLEDIGE functienaam, en reken
+op de hover-truc bij elke optielijst.** Volledig recept, 2026-09-15 vijftien
+keer achter elkaar gelukt (P2-1, `datumVan`/`datumTot` op vijf
+`HomeUitgaantabelKaartComponent`-instanties):
+1. Icoontje naast het label (niet in het "Unset"-veld) → "Setting from
+   Variable" opent **onderaan** het venster; laat 'm daar staan.
+   ⚠️ **Slepen aan de bovenrand sluit de dialoog** (kostte 1x de hele invoer) —
+   dat werkt alleen bij de dialogen die elders in dit bestand genoemd worden.
+2. **Typen landt NOOIT in dezelfde tool-aanroep die de dialoog opende.** Klik
+   het zoekveld in aanroep A, typ in aanroep B. Extra kliks helpen niet; een
+   tweede `type` in dezelfde batch ook niet.
+3. **Zoek op de volledige naam** (`filterDatumVan`, niet `Datum`). Een brede
+   term geeft vier bijna identieke regels onder elkaar en dan klik je er zo
+   eentje mis — dat gebeurde: `datumVan` kreeg `filterDatumTot`. Herstellen kan
+   ongevaarlijk via het **potlood-icoontje** naast "Variable" in diezelfde
+   dialoog. Dat één zoekterm maar één bron overhoudt is hier géén probleem
+   (zie het punt hieronder) zolang je de hover-truc gebruikt.
+4. **De optielijst rendert leeg tot je de muis er NAARTOE beweegt.** Eén hover
+   op de juiste y is niet genoeg als de muis er al stond: hover eerst ergens
+   anders (bv. 100 px lager), dán op de rij, en **wacht 10 s**. Werkte elke
+   keer. De rij zit ±20 px onder de sectiekop ("Available Options"), de optie
+   ±38 px eronder; lees de exacte y met een `zoom`, want het schuift.
+5. Argument binden: icoontje naast **"Value"** in "Set Function Arguments" →
+   geneste dialoog → zelfde stappen (zoek `datumFilter`). **Die Value-klik
+   landt structureel pas bij de tweede poging**; doe 'm in een eigen aanroep
+   en controleer met een `zoom` of het zoekveld er staat.
+6. **Confirm in een eigen tool-aanroep.**
+**Value Key** heeft een eigen inklapbare sectie in het rechterpaneel, vlak
+bóven "Component Properties" — dus niet bij de Component Parameters zoeken.
+Zelfde dialoog, zelfde stappen. Genereert `key: ValueKey(FFAppState().x)` op de
+component-instantie.
+
 **"Set from Variable": een bronregel die leeg/onzichtbaar blijft ná het
 uitklappen wordt zichtbaar door er met de muis overheen te bewegen.** Bevestigd
 2026-09-03, tientallen keren: na een klik op "App State"/"Page State"/"Widget

+ 62 - 50
TASKS.md

@@ -294,56 +294,68 @@ tonen; alternatief is default "Amsterdam", te wisselen in dezelfde binding).
 Verifiëren op telefoonformaat (393 dp): hamburger + terugknop + logo + naam
 moeten op één regel passen, ook bij "'s-Hertogenbosch".
 
-**P2-1 · Datumfilter Vandaag / Dit weekend / Deze week · Drupal ✅ (Bob), app-kant
-voor 60% af (Claude, 2026-09-15) — de rest staat hieronder uitgeschreven.**
-
-*Drupal is klaar:* `datum_van` / `datum_tot`, formaat `yyyy-MM-dd HH:mm:ss`,
-lokale tijd, beide grenzen inclusief, werkt op alle zeven displays. Nagemeten
-door Claude: weekend 19-20 sep geeft 40 events (27 za + 13 zo, niets erbuiten),
-een lege waarde = geen filter (1775 items), één gezette grens filtert correct
-(114). Opbrengst landelijk op Uitgaan: **1525 → 637 (deze week) → 263 (dit
-weekend) → 37 (vandaag)**.
-
-*Wat er al staat (commit `870a7bc`, export-geverifieerd):*
-1. Custom functions **`filterDatumVan`** en **`filterDatumTot`** (String in,
-   String uit). `'vandaag'` / `'weekend'` / `'week'` geven de grenzen; **elke
-   andere waarde, ook leeg, geeft `''` = geen filter**. Lokaal in Dart getest
-   op jaargrens en beide DST-weekenden.
-2. API Call **`homeTabel`**: variabelen `datum_van` / `datum_tot` (lege default)
-   + query-parameters.
-3. Component **`HomeUitgaantabelKaartComponent`**: optionele parameters
-   `datumVan` / `datumTot`, doorgegeven aan de Backend Query.
-4. App State **`datumFilter`** (String, niet persisted, default leeg = Alles).
-5. **`homeCopy`** aangemaakt als vangnet vóór het Home-werk (Bob's verzoek).
-
-*Nog te doen — alleen op de pagina `home` zelf:*
-- **Per tab (5×) op de `HomeUitgaantabelKaartComponent`-instantie:**
-  `datumVan` = custom function `filterDatumVan(App State datumFilter)`,
-  `datumTot` = `filterDatumTot(App State datumFilter)`, en **Value Key** =
-  App State `datumFilter`.
-  ⚠️ **Die Value Key hoort op de instantie op Home, niet op de `StaggeredView`
-  binnen het component.** De `pagingController` komt uit `_model` van het
-  component; verandert alleen een key binnenin, dan blijft dat model (en dus de
-  oude, al geladen pagina's) staan. Een key op de instantie vervangt het hele
-  component inclusief model.
-- **Chips boven de TabBar**: een `Row` met vier knoppen — *Alles* (`''`),
-  *Vandaag* (`'vandaag'`), *Dit weekend* (`'weekend'`), *Deze week* (`'week'`).
-  Elke knop: On Tap → **Update App State** → `datumFilter` → Set Value →
-  **letterlijke tekst** (geen custom function; die zit al in de binding
-  hierboven). Standaard is *Alles* — Bob's keuze, en dat is meteen de
-  default-waarde van het App State-veld.
-- Daarna: verse export + profile-build, en controleren dat een filterwissel de
-  lijst écht opnieuw laadt (dat is waar de Value Key voor zit).
-
-*Zolang stap 1 niet gedaan is verandert er niets in de app: de parameters
-blijven leeg, dus de lijst gedraagt zich precies als voorheen.*
-
-**Oude omschrijving (achtergrond):**
-Met 6.202 komende events is "Uitgaan" een lijst van 3.275 items; zonder
-tijdsfilter scrolt niemand daar doorheen. Drupal-kant: twee exposed filters op
-`field_date_value` in `flutterflowmobiel1` (`datum_van` ≥, `datum_tot` ≤ —
-recept in `CLAUDE.md` bij "exposed date-filter"). App-kant daarna: twee
-query-variabelen op de call + drie chips boven de lijst die de datums zetten.
+**P2-1 · Datumfilter Vandaag / Dit weekend / Deze week · ⚠️ BIJNA AF —
+2026-09-15 (Drupal: Bob; app-kant: Claude, commit `f92c87c`). Alles staat en is
+gemeten; er is nóg één knoop door te hakken, zie het restpunt onderaan.**
+
+*Wat er staat, export-geverifieerd:*
+- Drupal: `datum_van` / `datum_tot` op alle zeven `flutterflowmobiel1`-displays,
+  formaat `yyyy-MM-dd HH:mm:ss`, lokale tijd, beide grenzen inclusief. Een lege
+  waarde = geen filter. Opbrengst landelijk op Uitgaan:
+  **1525 → 637 (deze week) → 263 (dit weekend) → 37 (vandaag)**.
+- Custom functions `filterDatumVan` / `filterDatumTot`: mappen de Nederlandse
+  chip-labels (`Vandaag` / `Dit weekend` / `Deze week`) op de interne sleutels
+  en geven de grens terug; elke andere waarde, ook leeg, geeft `''` = geen
+  filter. De tot-grens is `23:59:59`, niet `00:00:00`.
+- API Call `homeTabel` + component `HomeUitgaantabelKaartComponent`: parameters
+  `datumVan` / `datumTot` tot in de Backend Query.
+- App State `datumFilter` (String, niet persisted, default leeg = Alles).
+- Home: een **ChoiceChips** (Alles / Vandaag / Dit weekend / Deze week, initieel
+  Alles) **tussen de slider en de TabBar**; On Selected → Update App State
+  `datumFilter`, update type "Rebuild Current Page".
+- Alle **5** `HomeUitgaantabelKaartComponent`-instanties (services_3 t/m _7):
+  `datumVan` = `filterDatumVan(datumFilter)`, `datumTot` =
+  `filterDatumTot(datumFilter)`, en **Value Key = `datumFilter`** op de
+  instantie (niet binnenin het component — de `pagingController` zit in het
+  `_model` van het component, dus alleen een key op de instantie vervangt het
+  hele model en dwingt een verse eerste pagina af).
+- `homeCopy` staat als vangnet klaar (Bob's verzoek vóór het Home-werk).
+  **Opruimen mag zodra je tevreden bent — dat is een taak voor Bob.**
+
+⚠️ **RESTPUNT, en het is er één voor jou — keuze nodig (prio 1).** Op de
+telefoon-emulator getest met een profile-build: de chips wisselen, App State
+wisselt, de parameters gaan goed mee... **maar de lijst herlaadt niet vanzelf.**
+Trek je de lijst omlaag (pull-to-refresh), dan staat het weekendresultaat er
+meteen — *zaterdag 19 sep* bovenaan i.p.v. dinsdag 15 sep. De hele keten klopt
+dus; alleen de automatische herlaad-trigger ontbreekt.
+
+*Waarom de Value Key dit niet oplost (nagelezen in de gegenereerde code):* het
+model van het component is een veld op het **pagina**model en wordt via
+`wrapWithModel` doorgegeven met `disposeOnWidgetDisposal = false`. Een nieuwe
+key bouwt wel een nieuwe `State`, maar die pakt via `createModel` hetzelfde
+model terug — inclusief de `pagingController` met alle al geladen pagina's.
+Er bestaat géén builder-actie die `pagingController.refresh()` genereert (in dit
+project komt die aanroep alleen uit de `RefreshIndicator`), en een `Navigate To`
+naar `home` helpt ook niet: `nav.dart` gebruikt `key: state.pageKey` en die is
+voor hetzelfde pad identiek, dus go_router hergebruikt de pagina.
+
+*Drie routes, kies er één:*
+1. **Custom action die de route echt herlaadt**
+   (`context.pushReplacement(GoRouterState.of(context).uri.toString())` — dat
+   patroon staat al als werkend in `CLAUDE.md`). Kleinste ingreep, werkt zeker.
+   Prijs: de héle Home-pagina bouwt opnieuw op bij elke chip-tik (slider +
+   actieve tab, ±1-2 s). Vraagt er één extra ding bij: de chips moeten hun
+   selectie ná de herlaad terugkrijgen, dus **Initial Option Value** binden aan
+   `datumFilter` (met een mini-functie die leeg → `Alles` vertaalt).
+2. **Custom action die de gemonteerde `PagedMasonryGridView`s opzoekt in de
+   element-tree en er `refresh()` op aanroept.** Geen paginaherlaad, de slider
+   blijft staan, chips houden hun selectie. Prijs: ±20 regels custom code die
+   op FlutterFlow-interne widgets leunt.
+3. **Laten zoals het nu is** en pull-to-refresh als de manier accepteren. Kost
+   niets, maar niemand ontdekt dat uit zichzelf — feitelijk werkt het filter dan
+   niet voor een gewone gebruiker.
+
+Mijn advies: **2**, met **1** als veilige terugval. Zeg welke, dan bouw ik 'm af.
 
 **P2-26 · Zoekveld op de evenementenlijsten · ⚠️ HERZIEN 2026-09-15 (Claude,
 gemeten) — het horeca-patroon kan hier NIET, dit begint bij jou in Drupal.**