Werk uitsluitend binnen deze projectdirectory
(/home/bob/Projects/ff-app/uitgaanskrant-1qhvtd). Niet daarbuiten
zoeken of scannen — scope alle bestandsoperaties tot dit project.
CLAUDE.md + TASKS.md
zijn samen het volledige geheugen tussen sessies.TASKS.md-status kan achterlopen op de echte code (Bob werkt
gelijktijdig, en documentatie-updates lopen niet altijd synchroon
met de export). Check een taak die er al even staat met
git log --oneline -S"<kenmerkende string/tekst>" of een gerichte
grep vóórdat je 'm oppakt — niet blind vertrouwen dat "open"
betekent "nog niet gefixt". (Precedent: 2026-08-04 bleken 2 "open"
P0-taken al op 2026-08-02 gefixt te zijn, git-bevestigd.)TASKS.md heeft een stabiel ID (bv. P0-1) en een
Eigenaar:-regel — Bob (sneller/simpeler voor hem zelf, meestal
builder-UI met een bekend fragiele dialoog, zie hieronder), Claude
(onbeklaimd, vrij op te pakken), of ... — bezig (iemand is er
nu actief mee bezig). Vuistregel voor wie een nieuwe taak zou
moeten doen: een kort, mechanisch herhaald patroon zonder geneste
dialogen → Claude; ConditionalBuilder/JSON-Path-condities,
List-typed function-argumenten, of iets dat eerder al vastliep →
Bob.ff-run-fvm.sh/flutter run-proces is géén
betrouwbaar signaal dat er "nu" iemand actief mee bezig is —
bevestigd 2026-08-07: meerdere van die processen bleken dagen oud
(gestart Aug05/Aug06, nog steeds draaiend), puur omdat het
interactieve hot-restart-loop-karakter ze nooit vanzelf afsluit. Het
betrouwbaardere signaal voor "hier ligt nog niet-afgerond werk": een
niet-gecommitte git status/git diff (vooral als TASKS.md/
CLAUDE.md zelf ook wijzigingen tonen t.o.v. de laatste commit) —
dát is een aanwijzing dat een sessie iets heeft afgerond/uitgevoerd
maar de afsluitroutine (commit+push, zie hieronder) nog niet heeft
gedaan. Zie zo'n situatie: bouw erop voort waar zinnig, maar commit
niet over andermans nog-niet-afgeronde wijziging heen zonder
navraag.Aan het eind van elke sessie/taak:
TASKS.md: een afgeronde taak wordt volledig verwijderd
(niet gearchiveerd — onnodige context/kosten voor latere sessies).
Elke resterende open taak moet zelfstandig te begrijpen zijn
(concreet, met bestandspad) zonder de ontstaanschat gelezen te
hebben.CLAUDE.md: alleen aanvullen met blijvend herbruikbare inzichten
(conventie, architectuurkeuze, bekend valkuil-patroon) die een
latere sessie anders opnieuw zou moeten uitzoeken. Geen
sessieverslag/changelog. Ruim verouderde info op i.p.v. eronder te
plakken — dit bestand moet klein en scanbaar blijven..md-bestanden direct (geen
FlutterFlow-export nodig voor pure documentatiewijzigingen).Geen apart memory-systeem meer nodig voor projectfeiten — die
staan allemaal hier en in TASKS.md, wat al elke sessie automatisch
geladen wordt. (Auto-memory-bestanden zijn per 2026-08-04
opgeschoond omdat ze dit bestand 1-op-1 dupliceerden.)
Dit project wordt gebouwd via FlutterFlow (app.flutterflow.io). De FlutterFlow-cloudomgeving is de bron van waarheid, niet deze lokale code-export.
lib/custom_code/) — ook die voert Bob in via de Custom
Code-editor op de website, niet hier lokaal.flutter
analyze), maar voer het eindresultaat uit via de builder (zelf via
browser-automation, of als instructie aan Bob). Ga nooit uit van
behoud van een lokale bestandswijziging.git commit.mcp__claude-in-chrome__* (Bob's gedeelde Chrome), niet
de in-app Browser pane.Projectdirectory = eigen schone git-repo, origin
ssh://gogs.digitalforce.tv:2222/Uitgaanskrant.com/flutterflow.git,
branch master. Los van de grotere/rommelige repo hoger in de
mappenstructuur (AndroidStudioProjects, Flutter-SDK e.d.) — commits en
pushes horen hier.
Vaste workflow na elke afgeronde taak (staand akkoord, geen aparte
bevestiging nodig): niet los flutterflow export-code, maar:
export PATH="/home/bob/fvm/bin:$HOME/.pub-cache/bin:$PATH" && /home/bob/Projects/ff-run-fvm.sh emulator-5554 uitgaanskrant-1qhvtd -s
export PATH=... prefix is verplicht — Bash draait
niet-interactief, ~/.bashrc wordt niet geladen. Zonder prefix
faalt het script stil ("fvm: command not found" — geen harde error).run_in_background: true. Volg tot minimaal "All done!" én
idealiter een succesvolle app-launch (Launching lib/main.dart...,
geen nieuwe EXCEPTION CAUGHT BY RENDERING LIBRARY).emulator-5554 (licht_avd, telefoon-formaat,
laag RAM-profiel) en emulator-5556 (tablet_avd, tablet-
schermverhouding) — beide starten automatisch bij Bob's
desktop-login via systemd user-services
(~/.config/systemd/user/android-emulator*.service). WayDroid en
losse fysieke devices zijn niet meer in gebruik (WayDroid's
adb shell screencap leed structureel aan PTY/CRLF-corruptie bij
screenshots — vandaar de overstap). Check altijd eerst met
adb devices welke van de twee daadwerkelijk draaien vóórdat je
ff-run-fvm.sh of de android-mcp-tools gebruikt — draait geen van
beide: navragen bij Bob i.p.v. blind op het eerste beschikbare
device te draaien. De android-mcp-server-tools
(mcp__android__*) accepteren een device_id-parameter per call,
dus beide devices zijn los aan te sturen zonder config-wijziging —
handig om ontwerp/layout op zowel telefoon- als tabletformaat te
verifiëren (bv. relevant voor P1-11's responsive-grid-taak).EXCEPTION CAUGHT BY RENDERING LIBRARY/crash tijdens de run is
niet per se een regressie van je eigen wijziging — check eerst de
stack trace op bestandsnaam. lib/kanweg/ is Bob's eigen
scratch-testgebied (zie Opschonen hieronder) en kan legitiem crashen
zonder dat dat iets met de huidige taak te maken heeft; de app kan
daar staan door een eerdere hot-restart die Bob's laatst bezochte
route onthield, niet per se doordat het de echte initialLocation is
(check nav.dart).EXCEPTION CAUGHT BY RENDERING LIBRARY-dump
per proceslevensduur, ongeacht hoeveel verschillende widgets
daarna overflowen. Flutter's dumpErrorToConsole toont de volledige
stack trace + widget/bestand/regel alleen bij de allereerste
exceptie sinds process- of hot-restart; elke volgende (ook een heel
andere widget/pagina) wordt ingekort tot Another exception was
thrown: <bericht> zonder bestandslocatie. Bevestigd 2026-08-09
(P1-13-onderzoek): de Home-pagina's al bekende overflow
(PUitgaanSliderKaartComponent, P1-3) consumeerde de enige volledige
dump nog vóórdat er genavigeerd kon worden naar de eigenlijk
onderzochte pagina — alle latere overflows op die andere pagina
bleven anoniem. Om een verse stack trace voor een specifieke,
latere crash te pakken: eerst een hot-restart (R) sturen vlak
vóór je die crash reproduceert, ván een schone/crash-vrije pagina uit
— dat reset de teller. Een run_in_background-Bash-aanroep van
ff-run-fvm.sh heeft stdin op /dev/null (niet-interactief),
dus r/R is dan niet te versturen; voor dat scenario is een
losse flutter run met stdin aan een mkfifo-named-pipe gekoppeld
nodig (niet via de standaardwrapper). Flutter DevTools (de
devtools-URL uit dezelfde run-log) is zelf ook een
Flutter-Web-canvas-app zonder toegankelijke DOM — geen bruikbare
omweg om alsnog bij de volledige historische foutenlijst te komen.Daarna automatisch:
git status --short, dan git add — niet blind -A. Alleen
echte FlutterFlow/app-wijzigingen (lib/, android/, ios/,
pubspec*, .gitignore). .claude/ blijft uitgesloten
(sessiestate, geen app-code). Bob's eigen concurrente wijzigingen
horen gewoon mee in dezelfde commit.git commit met duidelijke boodschap.git push (-u origin master als tracking nog niet staat).Valkuil: flutterflow export-code overschrijft .gitignore bij
elke export terug naar FlutterFlow's standaardversie. Voeg na elke
export, vóór staging, deze regel weer toe als hij ontbreekt:
# Claude Code session state (not app code)
.claude/
(CLAUDE.md zelf overleeft een export altijd — check voor de
zekerheid toch even.)
Vóór een niet-triviale taak (bugfix, nieuw patroon, integratie): kort online zoeken naar bestaande oplossingen i.p.v. zelf trial-and-error in de builder. Geldt niet voor mechanische herhaling van een patroon dat al bevestigd werkt.
Rechterpaneel-clipping: gebruik de "Search properties..."-zoekbalk
i.p.v. scrollen. Het rechterpaneel (Widget Tree → property-editor)
scrollt via browser-automation vaak niet (muiswiel-events lijken geen
effect te hebben op die specifieke sectie) — de bekende
breedte-clipping (zie hieronder) geldt dus ook verticaal. Werkende
workaround: het zoekveld bovenaan het paneel ("Search properties...")
filtert op eigenschapsnaam (bv. "blur", "offset", "radius", "padding",
"shadow") en toont dan alléén die eigenschap, ongeacht scrollpositie —
vrijwel altijd binnen het zichtbare/klikbare gebied. Bij een
samengestelde eigenschap met 2 velden naast elkaar (bv. Offset X/Y)
kan het tweede veld nog steeds net buiten beeld vallen; een
Tab-naar-volgend-veld-workaround is niet betrouwbaar gebleken (de
waarde bleef soms op de oude staan zonder foutmelding, 2026-08-06
bevestigd op PUitgaanSliderKaartComponent's schaduw-offset). Voor
sliders zonder zichtbaar numeriek veld (bv. "Uniform Radius/Padding"
na het omzetten van per-hoek naar uniform): klikpositie op de track
geeft alleen een grove benadering, geen exacte waarde — prima voor
"ruwweg kleiner/groter", niet voor een precieze doelwaarde.
Ctrl+A + typen +
Return/Tab bleek meermaals niet vast te houden. Werkte
betrouwbaarder: triple-click op het veld → Delete → typen (i.p.v.
Ctrl+A → typen).https://app.flutterflow.io/code/<project>?component=<Naam> (of
?page=<Naam>) i.p.v. te klikken op het </>-icoon rechtsboven —
die iconenrij verschuift afhankelijk van app-state (een
commit-icoon verschijnt/verdwijnt), waardoor een vast coördinaat
soms een heel ander icoon raakt. Bevestigd 2026-08-06: 2x per
ongeluk een "Create Commit"-dialoog geopend, 1x een "Make Project
Public"-dialoog (met een Make Public-knop — nooit klikken), 1x de
app-preview. Geen van alle bevestigd/doorgeklikt, maar wel
tijdverlies — klik in die iconenrij rechtsboven nooit blind op een
onthouden coördinaat, altijd eerst een screenshot ná de vorige
actie. In de code-view zelf: gewoon muiswiel-scrollen werkt daar
evenmin betrouwbaar, gebruik Ctrl+F (opent een echte
browser-find-balk, werkt wel).
Ctrl+F opent hier een Monaco-stijl in-editor
zoekbalk (geen browser-native find), en die zoekbalk toonde bij
mij geen bruikbare matchteller (mogelijk zelf ook geclipt) — voor
het vinden van een specifieke regel werkte Ctrl+G ("Go to
Line") betrouwbaarder: typ een regelnummer (desnoods een schatting
op basis van de zichtbare structuur) + Enter, springt direct daarheen.
De editor is read-only (typen geeft een "Cannot edit"-tooltip) —
geen risico op per ongeluk wijzigen.Geneste "Set Variable"-dialoog lijkt vast te zitten. Bij een
conditie (ConditionalBuilder, Visibility → Conditional) op een
niet-triviaal type (JSON Path, API-response-veld, List<DataType>
function-argument) opent een tweede dialoog bovenop de eerste;
Confirm/Cancel reageren soms niet zichtbaar. Twee oorzaken, in
volgorde van proberen:
navigate naar dezelfde URL); wrap blijft staan, conditie moet
opnieuw.Component Name kan per ongeluk overschreven worden. Vlak na paginanavigatie kan een klik bedoeld voor "Search properties..." op het Component Name-veld landen (focus/z-order race), en typen hernoemt dan stilletjes het component. Zelfde risico bij een widget-tree zoekactie die per ongeluk double-click-to-rename triggert i.p.v. navigeren — druk direct Escape om te herstellen. Mitigatie: na elke click-before-type eerst een screenshot om focus te bevestigen, zeker vlak na navigatie. Herstel: rechtsklik component in zoekresultaten → "Rename Component".
Rechterpaneel kan te breed zijn voor de viewport. Sommige controls
(Expansion segmented control, Visibility → Conditional
expression-builder, maar ook simpele checkboxen zoals "Show Empty List
Widget" op een Carousel/ListView) renderen soms deels buiten beeld —
geen resize_window-probleem (niet zelf resizen, zie boven).
Bevestigd 2026-08-04: dit blijft optreden zelfs nadat Bob zijn eigen
venster al vergroot had — de FlutterFlow-app zelf lijkt de extra
breedte niet te gebruiken (real window 1970px, maar bruikbare
schermafbeelding/klikbare ruimte bleef begrensd tot ~1176px; de
JS-laag rapporteert wel de volle vensterbreedte, dus dit zit in hoe
Flutter Web rendert/schaalt, niet in het venster zelf). Geen
DOM/accessibility tree beschikbaar (canvas-rendering) — find en
read_page werken hier niet, alleen coördinaat-gebaseerd klikken.
Geprobeerd en zonder succes: direct klikken op meerdere
x-posities, klikken + Space-toets, horizontaal scrollen. Na 1-2
bevestigde pogingen stoppen en aan Bob vragen — geef het exacte pad
(component, tree-node, veldnaam) zodat hij het in seconden kan doen.
HomeUitgaanSliderComponent, PUitgaanSliderComponent,
EvenementComponent, horecagelegenheidCurrent, allemaal Carousel).
Niet meer opnieuw proberen per component — deze checkbox specifiek
is voor Claude via browser-automation niet bereikbaar, ongeacht welk
component. Verzamel in plaats daarvan de volledige lijst van
componenten die de fix nodig hebben en geef die in één keer aan Bob
(elk 10 sec in zijn eigen browser, geen viewport-beperking daar).Patroon: lege/ontbrekende afbeeldings-URL laat de app crashen.
CachedNetworkImage gooit een synchrone ArgumentError bij het
bouwen van de widget als imageUrl een lege string is — dit gebeurt
vóórdat er ooit een netwerkverzoek is, dus errorWidget/"Show Error
Image on Failure" vangt dit niet (dat vangt alleen échte
laadfouten zoals 404's). Werkende fix:
null als lege string (in-builder tooltip: "if the resulting
value is null or empty") — géén ConditionalBuilder nodig. Werkt hier
niet voor Image-widgets met Image Type: Network zolang er geen
gehoste fallback-afbeeldings-URL bestaat in dit project. Komt die er
ooit (bv. default-logo op FlutterFlow-CDN/Drupal-server): gebruik
dan Default Variable Value + "Show Error Image on Failure" — sneller
te bouwen dan ConditionalBuilder.Patroon: "Is Set and Not Empty"-conditie tegen een
Default-Value/valueOrDefault-gebonden veld is een no-op — en kan een
crash verbergen. Bevestigd 2026-08-07
(evenement_horecagelegenheid_widget.dart, zie TASKS.md P1-15):
gegenereerde code als if (valueOrDefault<String>(veld, 'placeholder')
!= null && valueOrDefault<String>(veld, 'placeholder') != '') is
altijd waar, omdat valueOrDefault bij een lege/ontbrekende bron
juist de placeholder-tekst teruggeeft (nooit null/''). Gevolg: een
widget die eigenlijk verborgen zou moeten zijn bij ontbrekende data
blijft zichtbaar mét de placeholder-tekst — en als die widget een
onTap/actie heeft die de (placeholder-)waarde gebruikt (bv.
launchURL(valueOrDefault(...)) op een social-media-/website-knop),
crasht de app bij tikken i.p.v. gewoon niets te doen. Fix: bind de
Visibility/If-conditie's First Value niet aan een veld waar al een
Default Value op zit, maar rechtstreeks aan de rauwe API-response-
expressie (dezelfde JSON Path als de widget's eigen Path-property, vóór
een eventuele Default Value-substitutie) met operator "Is Set" —
zelfde recept als de CachedNetworkImage-fix hierboven. Check dit
patroon bij elke knop/link die een API-veld gebruikt, niet alleen bij
de al bekende P1-6-lijst met lelijke placeholder-tekst — die twee
problemen (lelijke tekst vs. crash-bij-tikken) hebben dezelfde bron
maar zijn niet automatisch allebei opgelost met alleen een betere
Default Value-tekst.
Custom-Function met meerdere argumenten in een Set-Variable-actie:
tweede argument bevriest de pagina. Bij het configureren van een
multi-argument custom function (bv. gemeenteNaamById(lijst, id)) als
Set-Variable-waarde: het eerste argument instellen via de
"Search variables..." → categorie-expand → item-klik-flow werkt
betrouwbaar. Zodra je daarna probeert het tweede argument in te
stellen (klik op de argument-dropdown-selector, bv. van "lijst" naar
"id"), kan de hele pagina volledig bevriezen (alle clicks doen
niets meer, geen enkele visuele terugkoppeling) — bevestigd
reproduceerbaar 2026-08-05 op gemeenteNaamById, NIET opgetreden bij
hetzelfde patroon op provincieNaamById een moment eerder (niet
100% deterministisch, maar bij deze functie 3x achter elkaar
gereproduceerd). Reload lost dit niet volledig op zoals bij het
bekende "echt bevroren pagina"-patroon hierboven: de
Custom-Function-keuze zelf (bv. welke functie, welk App-State-veld het
target is) overleeft een reload wél, maar het al ingestelde eerste
argument (bv. lijst) gaat weer naar "UNSET" terug — dus geen
gratis doorstart, elke reload kost een herhaling van stap 1.
SelectStateDropDownComponent → DropDownGemeente → Actions →
On Selected → Action 1 → Set Fields → gemeenteSelectNaam): de
functie gemeenteNaamById staat al goed gekozen (herkenbaar aan
rode "gemeenteN…" tekst i.p.v. "Unset" in de Set Fields-lijst), nog
toe te voegen: argument lijst = App State gemeentelijst,
argument id = App State gemeenteSelectId, dan Confirm.EventCurrent's AppBar-Row, in totaal 5x
bevroren bij het klikken op "Confirm" (soms al bij de binnenste
Single-Condition-Confirm, soms pas bij de outer Confirm) ná het
instellen van een Single Condition (Is Set and Not Empty). Precies
dezelfde stappen lukten wél zonder freeze op HeaderButtonsComponent,
én lukten wél voor het vergelijkbare Gemeente-Custom-Function-
argumentenpunt na een computer-restart — dus dit is niet louter
algemene omgevingsinstabiliteit die met een restart oplost. Een
computer-restart tussen pogingen 2 en 3 loste dit specifieke geval
niet op (2x nieuwe freeze ná restart, identiek patroon). Blijft
onverklaard waarom dit specifieke widget/actie op dit specifieke
component structureel vaker vastloopt dan elders — mogelijk iets
aan de AppBar-Row-context van EventCurrent zelf. Na 5x: gestopt
met proberen, definitief overgedragen aan Bob (zie TASKS.md
P0-3). Vuistregel blijft: 1-2 pogingen (incl. 1 reload), dan
overdragen met exacte stappen — niet blijven proberen.Tooltip toevoegen aan een icon-only widget: rechtsklik → Wrap
Widget (Ctrl+B) → 4e rij van de grid (kan geclipt lijken) → 1e icoon
(spraakwolkje) = "Tooltip". Genereert een AlignedTooltip, geen losse
styling nodig. Werkt niet op iconen embedded als suffixIcon van een
TextFormField (bv. Login-pagina wis-/toon-wachtwoord-iconen — geen
losse wrapbare tree-node).
Widget toevoegen: gebruik de kleine inline "Insert Widget", niet de
grote centrale "Insert"-modal. Rechtsklik op een Widget Tree-rij →
"Insert Widget" (of het kleine "+"-icoon naast een tree-node) opent
een compacte dropdown die betrouwbaar werkt: klik op een widget-kaart
voegt 'm meteen toe. De grote centrale Insert-modal (bv. via de
"+"-knop bovenaan het linkerpaneel) opent wél, en widget-kaarten lijken
klikbaar/highlighten bij hover, maar een klik registreert niet —
geen insertie, dialoog blijft open, geen foutmelding. Bevestigd
2026-08-05 op EventCurrent (Button toevoegen na
EvenementHorecagelegenheid): pas de inline-variant via rechtsklik
werkte. Sluit aan bij een eerdere observatie (2026-08-04, op
HorecagelegenheidCurrent): een widget toevoegen in een AppBar-Row
faalde herhaaldelijk stil via beide insert-varianten, terwijl exact
dezelfde inline-picker op een gewone body-Column wél meteen werkte —
mogelijk is een AppBar-Row specifiek een moeilijkere insertie-plek,
sowieso eerst de inline-variant op een Column proberen vóór de grote
modal.
HorecagelegenhedenOverzicht (een gewone Column, TabBar-node)
opende rechtsklik → "Insert Widget" herhaaldelijk wél de grote,
kapotte modal (klikken/dubbelklikken/zoeken op widgetnaam, niets
registreerde — 5+ pogingen). Blijkbaar niet 100% voorspelbaar per
context. Werkende omweg wanneer je alleen decoratie/styling nodig
hebt (geen echt nieuw kind-widget), bevestigd op ditzelfde
component: i.p.v. een nieuwe widget te inserten, de bestaande
node (bv. TabBar, of een Column met tags) Wrap Widget (Ctrl+B)
→ Container — dat dialoogvenster werkt wel betrouwbaar (bevestigd
meerdere keren deze sessie) — en geef die nieuwe Container dan een
BoxShadow/Fill Color/Border Radius via de
eigenschappen-zoekbalk. Voor een schaduw onder een widget (bv.
TabBar-afscheiding): Box Shadow → Shadow Color (hex-veld, bv.
33000000) → apart zoeken op "offset y" (aparte property, niet
samen met "offset" x/y in één filter) en "blur". Voor een simpele
achtergrond-scrim (bv. tegen storende achtergrond-afbeelding): alleen
Fill Color op de wrap-Container volstaat al, ook zonder eigen
padding als de wrap toevallig al een bestaande Padding-widget omvat.
Bij een écht nieuw structureel widget (geen bestaande node om te
wrappen) blijft de Insert-modal het enige pad — dan na 1-2 pogingen
stoppen en aan Bob overdragen, zoals altijd.Set-Variable/parameter-waarde koppelen aan App State: klein
icoontje naast het "Value"-label, niet de tekstinvoer zelf. Bij een
actie-parameter (bv. Navigate To met page-parameters) toont het
"Value"-veld standaard een tekstinvoer (voor een letterlijke waarde).
Om aan een variabele (App State, Page Parameter, …) te binden: klik op
het kleine icoontje vlak vóór/naast het woord "Value" (niet in het
invoerveld zelf) — dat opent een "Set Variable"-dialoog met een
zoekbalk en een "Source"-lijst (Page Parameters/App State/Global
Properties/…) om uit te klappen en te doorzoeken. Dit icoontje is
makkelijk te missen/verkeerd te klikken (kleiner dan de rest van het
paneel) — bij twijfel zoom gebruiken op de regio rond "Value" om de
exacte pixelpositie te bepalen vóór je klikt.
itemBuilder-variabele als "evenementen item"), 2026-08-07
bevestigd: het item zelf selecteren in de Set Variable-dialoog is
niet genoeg — je krijgt dan het hele JSON-object, niet een veld
ervan. Klik het item, dan "Available Options" (dropdown, staat
eerst op "No Further Changes") → "JSON Path" → typ het pad met een
voorloop-$, bv. $.nid of $.horecagelegenheidid (exacte
veldnamen uit de API-respons, zie lib/backend/api_requests/api_calls.dart).
Werkt identiek aan de al bestaande getJsonField(item, r'''$.veld''')-code
elders in dit project — dit is precies hetzelfde patroon, alleen via
de builder-UI ingesteld i.p.v. handmatig in code.serializeParam('', ParamType.String) — een lege string i.p.v. de
parameter-referentie. Reproduceerbaar herstel: Value-icoontje
opnieuw aanklikken, dialoog opnieuw openen, dezelfde parameter
opnieuw selecteren — de tweede keer sloeg het wél correct op. Vertrouw
dus nooit alleen op het rechterpaneel na zo'n binding; controleer
altijd even via View Code (zie hieronder) dat de gegenereerde
code echt naar de variabele verwijst en niet naar een lege/letterlijke
waarde.API-call headers: check op hardcoded literals i.p.v. [varname]
templates. Werkt een call via curl wél maar vanuit de app/Response &
Test-panel niet: check het Headers-tabblad — een handmatig ingeplakte
testwaarde (bv. Cookie-header) kan per ongeluk blijven staan i.p.v.
[sessionname]=[sessionid]. Check ook het per-variabele
"Include"-vinkje in Response & Test — staat die uit, dan wordt de
letterlijke [varname]-tekst meegestuurd i.p.v. de testwaarde.
Custom Code-editor (Custom Functions/Widgets/Actions): tekst-editor,
betrouwbaar te bewerken — maar wijziging verschijnt pas in een export
ná een expliciete Ctrl+S. In tegenstelling tot widget-canvas/
rechterpaneel (coördinaat-onbetrouwbaar, zie hieronder) is dit een
echte Monaco-editor: getypte code (incl. auto-closing brackets/quotes)
komt gewoon correct aan, geen speciale precautions nodig. Kritieke
valkuil, 2026-08-09 bevestigd: de header toont "Synced" al direct
na typen, maar dat weerspiegelt niet dat de wijziging al naar de
export-pipeline is doorgezet — een flutterflow export-code direct
daarna (ook na een volledige herstart van ff-run-fvm.sh, dus geen
lokale cache-issue) gaf 2x achter elkaar nog de oude bestandsinhoud
terug. Pas ná een expliciete Ctrl+S in de editor (zichtbaar:
kort "Syncing…" i.p.v. "Synced", + auto-format herschrijft de
zojuist getypte regels) nam een verse export de wijziging over.
Vuistregel: na elke Custom Code-wijziging altijd Ctrl+S drukken
vóórdat je ff-run-fvm.sh/export-code draait, en bij twijfel eerst
verifiëren met een losse
flutterflow export-code --project <id> --dest /tmp/check --token
"$(cat ~/.ff_token)" --no-parent-folder --as-debug + grep op de
verwachte regel, i.p.v. blind op de "Synced"-badge te vertrouwen.
⌘+K-commandpalet (zoek op de custom-action-naam,
bv. "drupalLogin") — betrouwbaarder dan door de linkerzijbalk
klikken (die leed in deze sessie aan hetzelfde klik-coördinaat-
probleem als de widget-tree, zie hieronder). Let op: klik het
zoekresultaat aan met de muis, druk geen Enter — een nog openstaand
rechtsklik-contextmenu elders op de pagina kan de Enter-toets
onderscheppen (2026-08-09 1x per ongeluk een widget gedupliceerd op
deze manier; met Ctrl+Z + Delete op de duplicaat hersteld, geen
blijvende schade). Sluit dus eerst met Escape elk open contextmenu
vóór je ⌘+K gebruikt.Widget Tree/canvas: klik-coördinaten zijn NIET betrouwbaar 1-op-1
met wat een screenshot toont, en dit is geen vast te rekenen offset.
Los van de al bekende rechterpaneel-breedte-clipping (hierboven) bleek
2026-08-09: een klik op een zichtbare tree-rij selecteerde herhaaldelijk
een héél andere rij (soms 2, soms 3 rijen verderop, niet consistent
in pixels of in aantal rijen). Werkende aanpak: na een klik nooit
blind vertrouwen — verifieer met Home + Shift+End (selecteert de
hele regel/rij zonder iets te wijzigen) of een read-only zoom op het
rechterpaneel, en pas de klik-y-coördinaat handmatig aan op basis van
wat je net echt selecteerde, vóór je verdergaat. Reload van de
pagina (navigate naar dezelfde URL) hielp de eerstvolgende klik weer
correct te laten landen, maar het patroon kwam later opnieuw terug —
geen structurele fix gevonden, alleen de per-klik-verificatie hierboven.
double_click op een widget (zowel canvas als tree) selecteerde
herhaaldelijk de root-pagina i.p.v. in te zoomen — vermijd double-click
voor selectie, gebruik single-click + verificatie.
Delete-toetsaanslag wordt
door de Bash/computer-permissie-classifier geblokkeerd als
destructieve actie — niet bruikbaar, gebruik het menu-item.key-events, zelfs opzettelijk buiten-viewport-coördinaten (in de
aanname dat click-ruimte niet 1-op-1 met screenshot-ruimte is) —
niets kwam aan (waarde bleef letterlijk ongewijzigd). Dit is erger
dan de bekende checkbox-clipping: niet alleen "net buiten beeld",
maar functioneel onbereikbaar. Bob's tip (2026-08-09): voor een
Text-widget zit naast het "Text"-label een klein bolletje/
globe-icoontje dat de vertaling direct opent — sneller dan via
App Settings → Languages, en waarschijnlijk ook betrouwbaarder
bereikbaar in Bob's eigen (niet-geclipte) browser. Nog niet
bevestigd of dat icoontje voor Claude wél bereikbaar is (waarschijnlijk
ook geclipt, niet meer getest deze sessie) — bij een volgende
soortgelijke taak eerst dat proberen vóór je het opgeeft.adb shell cmd locale set-app-locales <package> --locales nl-NL
(per-app override, Android 13+/API 33+) i.p.v. het systeembrede
settings put system system_locales + am broadcast -a
android.intent.action.LOCALE_CHANGED — dat laatste faalt zonder
root (Neither user ... nor current process has
android.permission.CHANGE_CONFIGURATION) en werkte zelfs als root
niet betrouwbaar zonder herstart. De per-app cmd locale-route werkte
in één keer, direct zichtbaar na een am force-stop + relaunch, geen
reboot nodig — dit is de snellere/betrouwbaardere manier om
P1-17-achtige locale-afhankelijke bugs te reproduceren/verifiëren.Actions-paneel voor een Button-widget (onPressed/If-Then na een
custom action) — niet gevonden in deze sessie, ondanks uitgebreid
zoeken. De 2 extra tab-iconen naast de standaard Widget Styling-tab
bleken Animations en Accessibility te zijn — geen "Actions"-tab
ertussen. Ook niet gevonden: via rechtsklik (canvas of tree, beide
andere contextmenu's, geen "Edit Actions"-optie), via het
"Search properties…"-veld (geen match op "action"/"tap"), via de losse
tree-rij-iconen (leken niets te doen), via de globale "24"-badge
rechtsboven (Action Blocks-teller, opent geen paneel bij klikken). Het
⌘+K-commandpalet vindt wel "Custom Action: <naam>" maar dat
springt naar de Custom Code-editor van de actie zelf (functie-body),
niet naar de knop's eigen Actions-configuratie. Nog niet uitgeprobeerd:
uiBuilder-canvasmodus met een expliciete hover (i.p.v. click) op de
rand van de knop, of Bob zelf vragen waar dit paneel in zijn versie
zit. Bij een volgende taak die een knop-actie/conditie moet wijzigen:
eerst hier verder zoeken vóór blind te herhalen wat hierboven al niet
werkte, en anders vroeg overdragen aan Bob i.p.v. tijd te verliezen
(kostte deze sessie ~1,5 uur zonder resultaat).
Taal/locale volgt het toestel, niet hardcoded Nederlands — zonder
opgeslagen voorkeur (FFLocalizations/_kLocaleStorageKey) valt
MaterialApp.locale terug op de systeemtaal van het toestel; matcht
die met 'en' (tweede taal in FFLocalizations.languages() =
['nl','en']), dan draait de app in het Engels. Er is vrijwel geen
echte Engelse vertaling (zie TASKS.md P1-17, live bevestigd
2026-08-07: van 169 sleutels in
lib/flutter_flow/internationalization.dart zijn er maar 2 waar
nl/en daadwerkelijk verschillen, en één daarvan is een fout
i.p.v. een vertaling). Test dus altijd met het testtoestel/de
emulator op Nederlands (adb shell getprop ro.product.locale
checken), anders lijkt de UI kapot terwijl het eigenlijk gewoon de
ontbrekende Engelse vertaling is.
Drupal 7 Services sessie-auth: sessid + session_name (uit
LoginCall's response) vormen samen de sessie-cookie:
Cookie: <session_name>=<sessid>. token is een los CSRF-token,
alleen nodig bij schrijf-requests (POST/PUT/DELETE), nooit bij GET.
Drupal page cache bootstrapt vóór de sessie — een ooit anoniem gecachete response (bv. een 403) kan session-bootstrap, hooks én custom-module-logging volledig overslaan. Bij twijfel: Drupal-cache legen en opnieuw testen vóór verder debuggen.
Views numeric filter: $view->filter['uid']->value moet
array('value' => $uid) zijn, niet array($uid) — de foute vorm
faalt stil (geen filter toegepast) i.p.v. een error te geven.
Provincie/gemeente blijft het basismodel voor content-scoping
(bevestigd: mensen zoeken primair lokaal). Home is de landelijke
standaard-startpagina zónder verplichte gemeente-keuze vooraf — dat
vervangt het provincie/gemeente-model niet, het is een aanvullende
ingang. De Home-prefixed componenten (HomeUitgaantabelKaartComponent
e.d.) zijn een bewuste kopie van PUitgaanSliderComponent/
UitgaantabelKaartComponent + een eigen cityid-loze API-call — geen
dode code, niet meenemen in opschoonacties.
Favorieten/login zijn P0. Backend-endpoint bestaat al; Drupal 7 views die de respons voeden hebben soms nog aanpassing nodig.
Fout-/leeg-afhandeling bij API-calls: het "geen enkele
FutureBuilder checkt op meer dan !snapshot.hasData"-beeld klopte
niet helemaal (gecorrigeerd 2026-08-04). ApiCallResponse vangt
netwerkfouten zelf op (api_manager.dart, catch (e) => ApiCallResponse(null, {}, -1, ...))
— de Future voltooit dus altijd, geen oneindige spinner. Het
eigenlijke risico was een synchrone crash: bij jsonBody: null
gooit getJsonField(null, ...).toList() een NoSuchMethodError
vóórdat de lijst-widget ooit gebouwd wordt. Bob heeft hiervoor op
HorecagelegenhedenOverzicht een werkend patroon gebouwd
(2026-08-04, rechtstreeks in de builder): bij lege/mislukte data
toont elke tab nu een standaardplaatje i.p.v. te crashen. Exacte
builder-stappen (welke widget/property) nog niet gedocumenteerd —
bij het uitrollen naar andere pagina's (zie TASKS.md P1-1) eerst
navragen/naspeuren i.p.v. blind het Carousel-"Empty List
Widget"-patroon hierboven te kopiëren, want dat lost alleen
itemCount: 0 op, niet per se de null-jsonBody-crash die hier de
kern van het probleem was.
API-call cache: true werkt functioneel correct — ApiCallOptions
(lib/backend/api_requests/api_manager.dart) extends Equatable met
params/headers in de props-lijst, dus de in-memory _apiCache
vergelijkt op echte parameterwaarden (deep equality), niet op
object-referentie. Geen reference-equality-bug om je zorgen over te
maken — cache: true is een veilige, lichte performance-fix voor
calls met effectief statische data binnen een sessie (referentie-
lijsten zoals provincies/gemeenten, categorieën). Lost wél alleen
herhaalde identieke calls op (bv. bij een rebuild), niet een
eerste gelijktijdige burst van meerdere verschillende calls (bv. een
eager TabBarView met 6+ tabs die elk hun eigen data ophalen bij
page-load, zie TASKS.md P1-10).