Datum en tijd kiezen — vier varianten, één advies
Naar aanleiding van je opmerking van 26 juli over de datumpicker. Hieronder eerst wat er nu écht in de app zit (met bestand en regel), dan vier serieuze richtingen voor datum en drie voor tijd, en tot slot wat het kost om een keuze app-breed door te voeren.
Je hebt gekozen: wielen. Ik heb het periode-huiswerk gedaan.
"Voor de datum en tijd kiezen, vinden we de wielen het meest vertrouwd aanvoelen. Maar als dat voor een periode niet kan, dan vinden we de optie D2 het beste aanvoelen… Toch denk ik dat je misschien even moet kijken naar hoe we eenvoudig een periode kunnen doen met de wielen en hoe we dat wel kunnen laten werken."
Je hebt me terecht gecorrigeerd. In versie 1 schreef ik dat er "geen wiel-periodekiezer bestaat" — dat is waar voor kant-en-klare componenten, maar het is geen antwoord op de vraag of het kán. Het kan wél, op drie manieren, en één daarvan is beter dan alles wat ik in versie 1 voorstelde.
Nieuw in deze versie:
- Sectie 2b — Een periode met wielen: drie uitgewerkte vormen (W1 twee wielensets · W2 Van/Tot-schakelaar · W3 start + duur), met telefoon-maten en wat elk opgeeft.
- Sectie 2c — Wiel of kalender: per situatie of app-breed? Ik heb alle 42 plekken langsgelopen en geteld waar het maandbeeld écht nodig is. Uitkomst: 31 van de 42 keer niet. Met één regel om het uit elkaar te houden, in plaats van per scherm smaak.
- Sectie — Wielen en D2 achter dezelfde wrapper: ja, dat kan, en het kost ~2-3 dagen extra in plaats van dubbel werk. Daarna is de verdeling wiel/kalender een wijziging in één bestand, niet in 55 aanroepen.
- Tijd-advies omgedraaid: was T1 (raster), wordt T2 (wiel met kwartierstap). Mijn argument tegen T2 was "het botst met een kalender-datumkiezer" — nu datum een wiel wordt, pleit datzelfde argument juist vóór T2. Dat leg ik uit in de tijd-sectie.
- Nieuwe aanbeveling en twee nieuwe eigenaar-vragen (nachten-of-dagen, en mag de gebruiker zelf wisselen).
Ongewijzigd: deel 1 (wat er nu is, met bestand:regel) en de vier oorspronkelijke datum-varianten D1–D4. Die blijven staan als vastlegging van de keuze.
"De datumpicker is heel slecht qua UX. Het is heel moeilijk om snel naar een andere maand te gaan, want je moet er telkens naar voren of naar achteren. Daarnaast, als je ergens een range moet kiezen met een start- en einddatum, moet je alsnog twee aparte datumpickers kiezen."
Beide punten heb ik in de code nagezocht. Eén klopt volledig, de ander klopt bijna — en de nuance daarin verandert wat de goedkoopste oplossing is. Dat staat in deel 1.
1Wat er nu in de app zit
Kort: Material's standaard-kiezers, 55 keer los aangeroepen, zonder één gedeelde plek. Geen extern pakket, geen Cupertino, geen eigen component.
| Wat | Aantal | Waar |
|---|---|---|
showDatePicker — Material-kalenderdialoog | 42 aanroepen | in 34 bestanden |
showTimePicker — Material-klok/dial | 13 aanroepen | in 10 bestanden |
showDateRangePicker — Material-periodekiezer | 0 | nergens gebruikt |
| Cupertino-wiel, extern pakket, eigen kalender | 0 | staat niet in pubspec.yaml |
| Gedeelde wrapper rond de kiezers | geen | elke plek roept Material zélf aan |
Wat je nu ziet als je "Kies datum" tikt
‹ tikken. Material heeft dat simpelweg niet.15-03-2027 typen). Ook dat zit er al in alle 42 plekken — en ook dat vindt niemand.De huidige kiezer, nagetekend. Vandaag = 26 juli 2026.
Klacht 1 — "je moet telkens maand-voor-maand"
Klopt bijna Geen maandsprong. Wél een jaarsprong, maar onvindbaar.
Ik wil je hier niet napraten, want de nuance is geld waard: Material's kiezer heeft een jaarsprong (de kop is een knop) en heeft tekstinvoer (het potloodje). Wat hij niet heeft is een maandsprong. Het gevoel "ik moet altijd stappen" komt dus uit drie dingen samen:
- De affordances zijn verstopt. De jaarknop lijkt een titel; het potloodje lijkt decoratie.
- Na een jaarsprong land je in dezelfde maand. Kies je 2027, dan sta je in juli 2027 en moet je alsnog vier maanden terug. De sprong lost dus maar de helft op.
- Twee plekken hebben een onbruikbaar breed bereik, waardoor het jaargrid een scrollveld van honderden jaren wordt:
shared/widgets/recurrence_picker.dart:106-107gebruikt ±100 jaar, enfeatures/gezin/gezinsafspraken/presentation/add_agreement_sheet.dart:156-160gebruikt 1900–2200 (301 jaar). Daar is de jaarknop erger dan nutteloos. - Precies één van de 42 plekken doet het goed:
features/settings/presentation/add_member_sheet.dart:67-74opent métinitialDatePickerMode: DatePickerMode.year(geboortedatum). De andere 41 openen in dagweergave, ook waar het bereik 30 of 40 jaar is (leningen/add_loan_sheet.dart:140-143:+40 jaar).
Rekenwerk voor "15 maart volgend jaar", vandaag: via de kalender 8–9 tikken (veld → jaarknop → 2027 → 4× ‹ → 15 → OK). Via het potloodje 3 tikken + 8 toetsaanslagen. Via een kiezer mét maandsprong: 5 tikken.
Klacht 2 — "een periode vraagt twee aparte pickers"
Klopt volledig Acht periode-flows, allemaal twee losse dialogen
showDateRangePicker — die Flutter gewoon meelevert — wordt nul keer gebruikt. Elke periode is twee onafhankelijke kalenderdialogen met met de hand geschreven grenzenlogica. Concreet:
- Vastgoed-boeking —
features/vastgoed/vastgoed/presentation/add_booking_sheet.dart:87(aankomst) +:110(vertrek) - Vakantieplanning —
features/planning/vakantieplanning/presentation/add_vacation_sheet.dart:88(aankomst) +:109(vertrek) - Schoolvakanties —
features/gezin/schoolvakanties/presentation/add_school_holiday_sheet.dart:89+:105 - Co-ouderschap-periode —
features/gezin/coouderschap/presentation/add_co_parenting_period_sheet.dart:83(vanaf) +:101(tot) - Medicijnkuur —
features/gezondheid/pillen_vitamines/presentation/add_medication_sheet.dart:109(één functie met eenisStart-vlag, dus twee keer dezelfde dialoog) - Verzekering —
features/financieel/verzekeringen/presentation/add_insurance_sheet.dart:313(ingangsdatum) +:324(vervaldatum) - Vaste kosten / verzekeringsblok —
features/financieel/vaste_kosten/presentation/add_fixed_cost_sheet.dart:669+:680 - Sportseizoen —
features/gezin/sportkalender/presentation/add_sport_event_sheet.dart:151(datum) +:172(tot); enadd_sport_team_sheet.dart:98(seizoenseinde)
En er is een tweede probleem dat je nog niet gemeld hebt, maar dat hier vandaan komt: die acht plekken lossen "wat als de einddatum vóór de startdatum ligt?" op vier verschillende manieren op:
add_booking_sheet.dart:98-100— schuift het eind naar start + 1 dag (een boeking heeft minstens één nacht)add_vacation_sheet.dart:99-101— schuift het eind naar dezelfde dag als startadd_school_holiday_sheet.dart:99enadd_co_parenting_period_sheet.dart:93— wissen het eind (terug naar leeg)add_medication_sheet.dart,add_insurance_sheet.dart,add_fixed_cost_sheet.dart— doen niets; start en eind zijn volledig los, met bereiken die niet eens op elkaar aansluiten (verzekering: ingang-30..+5 jaar, vervaldatum-1..+10 jaar)
Vier gedragingen voor één regel. Dat is precies wat je met een gedeeld component in één keer opruimt.
Twee dingen die niet in je klacht zaten, maar wél in de code
Nuance De tijdkiezer dwingt géén minuut-precisie af
Je vermoedde dat de tijdkiezer minuut-precisie eist. Dat is niet zo: Flutter's minutenschijf rondt af op 5 minuten zodra je hem loslaat (flutter/lib/src/material/time_picker.dart:1292-1295, letterlijk "Round the minutes to nearest 5 minute interval"). De pijn zit ergens anders:
- Twee etappes per tijd: eerst de urenschijf, dan de minutenschijf, dan OK — en de minutenschijf moet je nauwkeurig slepen op kleine doelen.
- Geen enkele van de 13 plekken zet
initialEntryMode, dus je begint altijd op de schijf, nooit op het toetsenbord. - Een tijdvak kost twee volle kiezers.
features/agenda/presentation/add_appointment_sheet.dart:195(start) +:203(eind) ensportkalender/add_sport_event_sheet.dart:182+:190. Dezelfde klacht als bij datum, maar voor tijd — en die heb je nog niet gemeld. - Wél netjes: de eindtijd wordt voorgesteld als start + 1 uur (
add_appointment_sheet.dart:205-207). Die slimheid wil ik niet weggooien.
Meegenomen Wat een nieuwe kiezer móet blijven kunnen
Grenzen per plek
Alle 42 plekken geven eigen firstDate/lastDate. Enkele zijn alleen verleden: garantie-aankoopdatum (garanties/add_warranty_sheet.dart:108-111, lastDate: now), gewicht (gewicht/add_weight_entry_sheet.dart:56-60), kleedgeld-bon (gezinsafspraken/add_receipt_sheet.dart:229-233), aankoopdatum vastgoed. Andere zijn alleen toekomst: afval-pauze (afvalkalender/afval_pause_sheet.dart:42-46), taak-herhaling, klusherhaling.
Snelkoppelingen bestaan al
shared/widgets/quick_date_chips.dart wordt op 11 plekken gebruikt (Vandaag / Morgen / Gisteren / Volgende week / +6 maanden / +1 jaar / Geen) en delegeert daarna naar showDatePicker. Dit patroon is een succes en verhuist het beste in de nieuwe kiezer, zodat álle 42 plekken het krijgen in plaats van 11.
Drie talen
lib/l10n/app_nl.arb, app_en.arb, app_de.arb. De kalender zelf komt nu gratis vertaald uit GlobalMaterialLocalizations (lib/app.dart:330-335) — maandnamen, eerste weekdag, dd-mm-jjjj-hint, AM/PM-of-24u. Een eigen component moet die dingen daar blijven ophalen, niet zelf hardcoderen.
Toegankelijkheid
De app klemt tekstgrootte op 1.0–1.5× (lib/app.dart:341-344), dus elke variant moet er op 1.5× nog uitzien. Material's kiezer levert nu voorleeslabels per dag; die moet een eigen component zelf verzorgen.
Volledige inventaris: alle 42 datum- en 13 tijd-aanroepen de uitrolkost, per bestand
Datum — 42 aanroepen in 34 bestanden
- Agenda (1) —
agenda/presentation/add_appointment_sheet.dart:185 - Taken (2) —
tasks/presentation/add_task_sheet.dart:254(deadline),:340(herhaling vanaf) - Gedeeld (1) —
shared/widgets/recurrence_picker.dart:118(herhalen tot; gebruikt door taken, maaltijden, klussen, agenda) - Financieel (11) —
vaste_kosten/add_fixed_cost_sheet.dart:669,:680·verzekeringen/add_insurance_sheet.dart:313,:324·garanties/add_warranty_sheet.dart:108·leningen/add_loan_sheet.dart:140·leningen/loan_part_sheet.dart:75·spaardoelen/add_savings_goal_sheet.dart:109·spaardoelen/add_contribution_sheet.dart:55·wagenpark/add_vehicle_sheet.dart:120·wagenpark/add_vehicle_item_sheet.dart:66 - Gezin (11) —
schoolvakanties/add_school_holiday_sheet.dart:89,:105·coouderschap/add_co_parenting_period_sheet.dart:83,:101·sportkalender/add_sport_event_sheet.dart:151,:172·sportkalender/add_sport_team_sheet.dart:98·gezinsafspraken/add_chore_sheet.dart:90·gezinsafspraken/add_receipt_sheet.dart:229·gezinsafspraken/add_agreement_sheet.dart:156·verjaardagen/add_recurring_date_sheet.dart:76 - Planning (7) —
vakantieplanning/add_vacation_sheet.dart:88,:109·vakantieplanning/add_vacation_item_sheet.dart:83·maaltijden/add_meal_slot_sheet.dart:142·beweging/add_movement_session_sheet.dart:109·afvalkalender/afval_pause_sheet.dart:42·afvalkalender/add_afval_pickup_sheet.dart:48 - Vastgoed (3) —
vastgoed/add_booking_sheet.dart:87,:110·vastgoed/add_property_sheet.dart:138 - Gezondheid (2) —
pillen_vitamines/add_medication_sheet.dart:109·gewicht/add_weight_entry_sheet.dart:56 - Overig (4) —
documenten/documenten/add_document_sheet.dart:100·voorraad/voorraad/add_stock_item_sheet.dart:106·huis_onderhoud/onderhoud/add_maintenance_item_sheet.dart:99·settings/add_member_sheet.dart:67
Tijd — 13 aanroepen in 10 bestanden
- Tijdvak (start + eind), 4 —
agenda/add_appointment_sheet.dart:195,:203·sportkalender/add_sport_event_sheet.dart:182,:190 - Losse tijd, 5 —
tasks/add_task_sheet.dart:264·planning/beweging/add_movement_session_sheet.dart:119·planning/vakantieplanning/add_vacation_item_sheet.dart:93·gezondheid/pillen_vitamines/add_medication_sheet.dart:93(inname-moment) ·welzijn/quote/quote_settings_section.dart:82 - Instellingen-tijden, 4 —
settings/instellingen_screen.dart:276(digest) ·settings/quiet_hours_editor.dart:19,:25(stille uren = ook een tijdvak!) ·modules_beheer/module_settings_sheet.dart:572(meldingstijd per module)
Wat er al staat en niet weg hoeft
lib/core/theme/app_theme.dart:212-249— deDatePickerThemeDatais al volledig in huisstijl, inclusiefrangeSelectionBackgroundColor. Met andere woorden: het thema is al voorbereid op een periodekiezer die nooit gebruikt is.lib/core/theme/app_theme.dart:251-276—TimePickerThemeDataidem.- Testkoppeling is laag: maar 2 testbestanden raken de kiezers (
test/features/polish/save_gates_test.dart,test/shared/widgets/recurrence_picker_interval_test.dart). Dat maakt een uitrol veel minder riskant dan 55 call-sites doet vermoeden.
2Vier richtingen voor de datumkiezer
Alle vier zijn verdedigbaar; ze hebben elk een eigen profiel. Ik reken bij elke variant hetzelfde boodschappenlijstje af: "15 maart volgend jaar" (ver weg, precies) en "20 t/m 27 juli 2027" (een periode).
D3 (wielen) is je voorkeur, D2 je terugval. Deze vier kaarten laat ik staan zoals ze waren — ze zijn de vastlegging waartegen je gekozen hebt. Eén dingetje wil ik wél rechtzetten: bij D3 schreef ik "er bestaat geen wiel-periodekiezer, dus je krijgt twee ontwerptalen". Dat was te snel geconcludeerd. Sectie 2b doet dat werk over.
D1 Material beter configureren
Niets nieuws bouwen. Alleen aanzetten wat Flutter al meelevert, en de rare bereiken opruimen.
Vier ingrepen, allemaal parameters:
- Open in jaarmodus (
initialDatePickerMode: DatePickerMode.year) zodra het bereik breder is dan ~2 jaar. Dan begin je met de sprong in plaats van hem te moeten vinden. - Snoei de twee absurde bereiken (
recurrence_picker±100 jaar → ±5 jaar;add_agreement_sheet1900–2200 → ±5 jaar). - Zet
showDateRangePickerin op de acht periode-flows. Één dialoog, start en eind erin, met de al-bestaanderangeSelectionBackgroundColoruit ons thema. - Maak tekstinvoer zichtbaar: eigen
switchToInputEntryModeIcon, of standaard ininput-modus openen waar typen bijna altijd sneller is (geboortedatum, aankoopdatum, verloopdatum).
Material's showDateRangePicker: schermvullend, maanden onder elkaar doorscrollen, géén jaarknop.
Hoe het voelt
"15 maart volgend jaar": de kiezer opent in het jaargrid → 2027 → 4× ‹ → 15 → OK. Of via het nu-zichtbare toetsenbordicoon: typen. Voor de periode: één dialoog, twee tikken in dezelfde maand, klaar.
Wat het kost
Laag — 1 à 2 dagen. Geen nieuw component, geen nieuwe teksten behalve de range-dialoog-labels in 3 talen. Nul risico op regressie in de a11y of de locale, want het blijft Material.
Wat je opgeeft
- De maandsprong blijft weg. Material heeft hem niet en dat verander je niet met parameters. Je hoofdklacht is dus maar deels opgelost.
- De periodekiezer is schermvullend en heeft geen jaarknop — alleen doorscrollen. Een boeking in 2029 is scrollen door 30 maanden.
- Twee verschillende dialogen in één app: compact-met-OK voor één datum, schermvullend-met-Opslaan voor een periode. Dat voelt als twee producten.
- Je blijft in Material's vormtaal, niet in die van Tendlo.
D2 Eigen kalendersheet met maand- én jaarkiezer in de kop jouw tweede keuze
Eén sheet in Tendlo-huisstijl die zowel één datum als een periode doet. Maand en jaar zijn zelf knoppen; snelkoppelingen en typen zitten erin.
- Kop:
‹ maart ▾ 2027 ▾ ›. Tik op de maand → een lijstje van 12 maanden. Tik op het jaar → een korte lijst binnen het toegestane bereik. Geen scrollen door honderden jaren, want de lijst is per plek al begrensd. - Snelkoppelingen boven: hetzelfde rijtje dat nu op 11 plekken staat (Vandaag / Morgen / Volgende week / +1 jaar / Geen), maar nu op álle plekken.
- Typen als ontsnapping: een veldje dat
15-3-27,15 maart,morgenen+2wbegrijpt. Voor wie precies weet wat hij wil. - Eén datum bevestigt bij de tik — geen OK meer. Een periode heeft wel een "Klaar", met een levende teller ("7 nachten").
- Zelfde sheet, twee standen:
singleofrange. Visueel identiek, dus de app voelt overal hetzelfde.
Stand "single". Maand en jaar zijn knoppen; de tik op 15 sluit de sheet.
Stand "range". Zelfde kop, zelfde grid, zelfde huisstijl — alleen twee velden en een nachtenteller.
Hoe het voelt
Sandra tikt "Kies datum" → sheet komt op → tikt 2026 ▾ → 2027 → tikt juli ▾ → maart → tikt 15. Sheet sluit. Geen scrollen, geen OK, geen verstopte knop: de twee sprongen die ze nodig heeft staan er als knoppen. Voor de boeking: tikt 20, tikt 27, ziet "7 nachten", Klaar.
Wat het kost
Het hoogst van de vier — ruim de helft van het werk zit hier. Grof: 4–6 dagen voor het component (grid, maand/jaar-menu's, range-logica, parse-veldje, voorleeslabels, 1.5× tekstgrootte), plus teksten in 3 talen en widget-tests. Daarna is de uitrol goedkoop, want de wrapper is dan al gemaakt.
Wat je opgeeft
- Je onderhoudt zelf een kalender. Voorleeslabels, tekstgrootte, eerste weekdag, maandnamen, rechts-naar-links — dat krijg je nu gratis van Material. Beperk de schade door maandnamen en de eerste weekdag uit
MaterialLocalizationste blijven halen (dat kan, en dat is de helft van het risico weg). - Meer testoppervlak en een grotere kans dat een Flutter-upgrade iets aan de vormgeving verandert dan bij D1 (waar Flutter het simpelweg voor je oplost).
- Een maandmenu van 12 en een jaarmenu is nóg een laagje overlay. Op een klein toestel moet dat een sheet-in-sheet worden, niet een dropdown.
- Je bouwt iets dat Flutter bijna al heeft. Dat is een bewuste prijs voor de maandsprong.
D3 Wielen, iOS-stijl jouw keuze
Drie draaiwielen: dag · maand · jaar. Geen kalender, geen stappen.
Een sheet met drie ListWheelScrollView-kolommen. Verspringt niet per maand — je flickt direct naar het jaar dat je wil. Precies wat je vraagt qua "snel ergens anders komen", maar met een prijs die je moet zien voordat je hem kiest.
Belangrijk: er bestaat geen wiel-periodekiezer. Voor een start-en-eind moet je óf twee wielen zetten (dan heb je je tweede klacht níet opgelost), óf voor periodes alsnog een kalender bouwen — en dan heb je twee ontwerptalen in één app.
Correctie (v2): er bestaat geen kant-en-klare wiel-periodekiezer, maar dat is niet hetzelfde als "het kan niet". Er zijn drie werkbare vormen en één ervan lost méér op dan een kalender-periodekiezer. → Sectie 2b.
Snel en vertrouwd — maar geen weekdag te zien, en je kunt 30 februari draaien.
Hoe het voelt
Vertrouwd van iOS. "15 maart volgend jaar" = drie flicks en Klaar. Maar: je ziet niet dat 15 maart 2027 een maandag is, en je ziet niet welke dagen eromheen al vol zitten. Voor een afspraak, een sportwedstrijd of een afvalpauze is dat precies de informatie die je nodig hebt.
"15 maart 2027" ≈ 3 flicks + 1 tikWat het kost
Midden — 2 à 3 dagen voor het wiel zelf (dagen-per-maand bijhouden, schrikkeljaren, klemmen op firstDate/lastDate). Maar je bent dan pas op de helft, want je hebt nog geen periodekiezer.
Wat je opgeeft
- De weekdag en het maandoverzicht. Dat is voor het grootste deel van de app (agenda, taken, sport, maaltijden, afval) de belangrijkste informatie op het scherm.
- Precisie bij grote letters. Op tekstgrootte 1,5× worden wielrijen hoog en flicken onnauwkeurig — je draait er net naast.
De periodekiezer. Er is er geen; je krijgt óf twee wielen (klacht 2 blijft staan) óf een tweede ontwerptaal.→ ingetrokken in v2: een periode kán met wielen, zie 2b.
Maar wel de winnaar op twee plekken: geboortedatum (add_member_sheet.dart, 120 jaar terug) en verre streefdata (add_savings_goal_sheet.dart, +30 jaar) zijn met een wiel écht beter dan met een grid.
D4 Typen als hoofdweg, kalender als hulp
Het datumveld is een echt tekstveld. Je typt 15-3, morgen, zaterdag, +3d. Het kalendericoon is er voor als je wilt kijken.
De snelste variant voor wie weet wat hij wil — en dat is de eigenaar zelf, in de meeste financiële en documentflows. Het veld toont onder je invoer meteen wat het ervan begrijpt ("za 15 mrt 2027"), zodat er nooit stil iets verkeerd wordt opgeslagen.
Voor een periode: 20-27 juli in één veld, of twee velden naast elkaar.
15 maart · morgen · za · +3d · over 2 wekenGeen dialoog nodig. Maar je moet wel weten wat je mag typen.
Hoe het voelt
Voor jou: heerlijk. Twee tikken en acht toetsen, geen overlay. Voor "welke zaterdag komt het beste uit?": nutteloos — dan moet je alsnog de kalender open. En voor een gezinslid dat de app net heeft: een leeg veld waar geen enkele hint in staat wát het accepteert.
"15 maart 2027" = 1 tik + typenWat het kost
Midden tot hoog — 3 à 4 dagen, en het meeste daarvan is de parser: 15-3-27, 15/3, 15 mrt, 15. März, March 15, morgen/tomorrow/morgen (het Duitse "morgen" is Nederlands "ochtend"!) — drie talen, en het Duits botst met het Nederlands. Plus een stevige testset, want een stil verkeerd geparseerde datum is een databug.
Wat je opgeeft
- Ontdekbaarheid. Een tekstveld vertelt niet wat het kan. Je lost dat op met hintteksten, maar dan ben je alweer ruimte kwijt.
- Vertrouwen. Elke parser heeft een grijs gebied (is
3-4nu 3 april of 4 maart? — in NL het eerste, in EN het tweede). Dat moet je per locale hardmaken. - De blik op de maand. Voor alles wat met plannen te maken heeft blijf je de kalender nodig hebben, dus dit is nooit de hele oplossing — het is een uitstekende tweede weg.
De vier naast elkaar
| D1 Material+ | D2 Eigen sheet | D3 Wielen | D4 Typen | |
|---|---|---|---|---|
| Maandsprong | nee | ja | ja | n.v.t. |
| Jaarsprong | ja, verstopt | ja | ja | ja |
| Periode in één dialoog | ja | ja | ja* | deels |
| "15 maart volgend jaar" | 7 tikken | 5 tikken | 4 handelingen | 1 tik + typen |
| Weekdag / maandbeeld | ja | ja | nee | nee |
| Bouwkosten | 1–2 dagen | 4–6 dagen | 2–3 dagen | 3–4 dagen |
| Eén ontwerptaal | 2 dialogen | 1 sheet | 2 talen | veld + kalender |
| A11y / 3 talen gratis | ja | zelf doen | zelf doen | parser per taal |
| Goed op 1,5× letters | ja | ja | krap | ja |
* Gecorrigeerd in v2 — zie sectie 2b. In versie 1 stond hier "nee".
2bEen periode met wielen — kan dat?
Ja. Er is geen kant-en-klaar component voor, maar er zijn drie werkbare vormen, en ze verschillen sterk in hoe goed ze zijn. Ik heb ze alle drie afgerekend op vier dingen: past het op een telefoon (ook op een kleine, ook op 1,5× letters), hoeveel handelingen kost "3 t/m 10 augustus", kan "eind vóór start" nog gebeuren, en past het op alle acht bestaande periode-flows.
De maat waar alles tegenaan loopt
Eén wielenset heeft ~200 px nodig. Een bottom-sheet heeft er op een kleine telefoon ~545 px.
Concreet: een wiel is pas een wiel bij 5 zichtbare rijen (~40 px per rij = 200 px). Daarbovenop komt de sheet-kop, een samenvattingsregel en een Klaar-balk: samen ~150 px. Op een iPhone 15 (852 px hoog) heb je ~700 px sheet; op een iPhone SE (667 px) ~545 px. En bij tekstgrootte 1,5× worden die 200 px ineens ~290 px. Dat is de rekensom die bepaalt of W1 kan.
W1 Twee wielensets in één sheet
Start bovenaan, eind onderaan, met een samenvattingsregel ertussen. De meest rechttoe-rechtaan lezing van je klacht.
Dit is niet de klacht die je had — je klaagde over twee aparte dialogen, niet over twee wielen in één beeld. In die zin is W1 een volledig geldig antwoord: één sheet, één keer openen, één keer Klaar, en je ziet start én eind tegelijk veranderen.
Het probleem is niet het idee, het is de hoogte en de verwisselbaarheid:
- Hoogte. 2 × 200 px wielen + ~150 px chroom = ~550 px. Dat past net op een moderne telefoon en past niet op een iPhone SE of bij 1,5× letters. De uitweg is terugvallen op 3 zichtbare rijen per wiel (~110 px elk), maar dan verlies je precies de context die een wiel prettig maakt — je ziet alleen de dag ervoor en erna. Je hebt dan dus twee opmaakvarianten te onderhouden.
- Verwisselbaarheid. Twee identiek uitziende wielenbanken boven elkaar: je draait het verkeerde. En dat gaat stil — je verzet de vertrekdatum terwijl je dacht de aankomst te verzetten, en niets waarschuwt je. Sterke labels helpen, maar lossen het niet op.
- "Eind vóór start" blijft mogelijk. Je moet dus alsnog klemmen of waarschuwen. Winst tegenover nu: die regel staat op één plek in plaats van acht. Maar de faalmodus zelf blijft bestaan — en dat was precies het rommeltje dat ik in deel 1 vond.
Alles in beeld — maar dit is ~550 px hoog. Op een kleine telefoon of bij grote letters moet je terug naar 3 rijen per wiel.
2 × 200 px wiel + 150 px chroom ≈ 550 px
iPhone 15: past · iPhone SE: past niet · 1,5×: past niet
Hoe het voelt
Overzichtelijk en direct: je ziet beide datums en de nachtenteller live meelopen. Op een grote telefoon prettig. Maar er zijn zes wielkolommen in beeld en dat is druk — je moet twee keer kijken welke rij je te pakken hebt.
"3 t/m 10 aug" ≈ 4 flicks + KlaarWat het kost
Midden — 3 à 4 dagen. Het wiel is er al (uit D3), dus dit is vooral opmaak. De extra kost zit in de tweede opmaakvariant voor kleine schermen en 1,5× letters, plus de klem-logica die je alsnog nodig hebt.
Wat je opgeeft
- Past niet overal. Twee opmaakvarianten om te onderhouden en te testen.
- Stille vergissingen door twee gelijkende wielenbanken.
- De faalmodus blijft. "Eind vóór start" kan nog steeds, dus de klem-regel blijft bestaan — netter dan nu, maar niet weg.
W2 Eén wielenset met een Van/Tot-schakelaar
Beide datums staan bovenaan als twee tabs. Je tikt Van of Tot en draait daaronder dezelfde wielenset.
De helft van de hoogte van W1, en geen verwisseling mogelijk: er is altijd precies één wielenset en de kop zegt onomstotelijk wat je aan het verzetten bent. Beide datums blijven zichtbaar, dus je verliest het overzicht niet.
En het belangrijkste: je kunt "eind vóór start" hier onmogelijk maken zonder achteraf te corrigeren. Sta je op de Tot-tab, dan begint het dagwiel simpelweg bij de startdatum — de ongeldige waarden zitten niet in het wiel. Dat is een fundamenteel betere oplossing dan klemmen ná de keuze, want de gebruiker ziet nooit een fout en er wordt nooit stil iets voor hem verzet.
- Hoogte: ~200 px wiel + ~40 px tabs + ~150 px chroom ≈ 390 px. Past overal, ook op een SE, ook op 1,5×.
- De prijs: als je van Van naar Tot wisselt, springt het wiel naar de andere datum. Dat is even desoriënterend — je ziet de cijfers wegdraaien. Met een korte animatie (~200 ms) is dat te verzachten, maar het blijft een beweging die W1 niet heeft.
- Werkt op alle acht flows, inclusief de twee met een open einde: de Tot-tab kan gewoon "—" tonen met een "geen einddatum"-knop.
Half zo hoog als W1, geen verwisseling, en op de Tot-tab bestaan ongeldige dagen niet in het wiel.
200 px wiel + 40 px tabs + 150 px chroom ≈ 390 px
iPhone 15: past · iPhone SE: past · 1,5×: past (net)
Hoe het voelt
Sandra opent de kiezer, staat automatisch op Aankomst, draait naar 3 augustus, tikt "Vertrek", draait de dag naar 10, ziet "7 nachten", tikt Klaar. Rustig en compact. De sprong bij het wisselen van tab is de enige oneffenheid.
"3 t/m 10 aug" ≈ 3 flicks + 2 tikkenWat het kost
Laag — 2 dagen bovenop het wiel uit D3. Twee tabs, een wiel dat zijn eigen bereik herberekent, en één animatie. Geen tweede opmaakvariant nodig.
Wat je opgeeft
- Het gevoel van "lengte". Je ziet twee datums, maar niet hóe lang de periode is — behalve via de nachtenteller. Bij een kalender zie je het blok; hier lees je een getal.
- Het wiel springt bij het wisselen van tab. Kleine desoriëntatie, elke keer.
- Nog steeds twee losse datums om te zetten, dus twee kansen om er één te vergeten. Bij een open einde is dat gewenst; bij een boeking is het onnodig.
W3 Startdatum + duur dit is het antwoord
"Vanaf 3 augustus, 7 nachten." Wielen voor de startdatum, en de lengte als tweede keuze. De einddatum wordt berekend en staat er als tekst bij.
Je noemde deze richting het meest belovend en dat is hij ook — om drie redenen die elkaar versterken.
1 · Het is vaak het echte mentale model
"We gaan 3 augustus weg, een weekje." "De gasten komen vrijdag, twee nachten." Bij een verhuurboeking en een vakantie denk je in lengte, niet in een tweede datum — die tweede datum reken je nu zelf uit en tik je daarna in. W3 haalt dat rekenwerk weg.
2 · "Eind vóór start" wordt onmogelijk door constructie
Dit is het sterkste argument en het is geen cosmetiek. Als de einddatum start + duur is en duur is minstens 1, dan is een ongeldige periode niet representeerbaar. Weet je nog die vier verschillende noodgrepen uit deel 1 — één plek schoof het eind een dag op, één plek naar dezelfde dag, twee plekken wisten het, drie plekken deden niets? Die verdwijnen niet omdat we ze op één plek zetten, maar omdat het probleem ophoudt te bestaan. Er valt niets meer te klemmen.
3 · Het is dezelfde taal als T3
In versie 1 stelde ik al voor om de tweede tijdkiezer te vervangen door duurchips (09:00 + "1 uur" in plaats van 09:00 en 10:00). W3 doet exact hetzelfde voor datum. Dan heeft de hele app één regel: een periode is een startpunt plus een lengte — of het nu om uren of om dagen gaat. Dat is de ene ontwerptaal die je vroeg, en hij komt nu uit de logica in plaats van uit de opmaak.
De vorm die ik voorstel
Niet een tweede wielenset voor de duur (dat wordt weer hoog), maar één wielenset voor de startdatum plus een duurregel eronder: chips voor de gangbare lengtes (1 · 2 · 3 · 7 · 14 nachten) en een …-chip die een klein duurwiel opent voor de rest. De einddatum staat als tekst rechts, en is aantikbaar — tik je erop, dan klapt W2's Tot-tab open. Zo heb je de ontsnapping ingebouwd zonder een tweede dialoog.
Eén wielenset, één chiprij. De einddatum is een uitkomst — en aantikbaar als je hem toch los wil zetten.
200 px wiel + 70 px duurregel + 150 px chroom ≈ 420 px
iPhone 15: past · iPhone SE: past · 1,5×: past
Hoe het voelt
Sandra draait naar 3 augustus en tikt "7 nachten". Ze leest "t/m ma 10 aug" en is klaar. Ze heeft geen tweede datum bedacht, geen dagen geteld, en kon geen fout maken. Voor de boeking waar je over viel is dit van vier handelingen naar twee.
"3 aug, 7 nachten" ≈ 2 flicks + 1 tikWat het kost
Midden — 3 à 4 dagen bovenop het wiel uit D3, waarvan de helft in de duur-semantiek gaat zitten (zie hieronder) en niet in de opmaak. Plus W2 als ingebouwde tweede stand, want die heb je nodig — reken die 2 dagen erbij: samen ~5-6 dagen voor de complete periodekiezer.
Wat je opgeeft
- Duur is niet altijd het model. Een schoolvakantie ken je uit een brief als "1 t/m 16 augustus", niet als "16 dagen". Een verzekering heeft een vervaldatum. Co-ouderschap heeft soms géén einde. Daarom is W2 geen luxe maar een noodzakelijk tweede standje — en dat is de eerlijke prijs van W3: je bouwt er twee, niet één.
- Nachten of dagen — dat is een valkuil met echte gevolgen. Een boeking rekent in nachten (3 → 10 aug = 7 nachten) en eist een eind ná de start; een schoolvakantie rekent in dagen t/m (1 t/m 16 aug = 16 dagen) en mag op één dag beginnen en eindigen. Die twee conventies zitten al in de code (
add_booking_sheet.dart:98-100versusadd_school_holiday_sheet.dart:99). De component moet dus per aanroep weten welke eenheid geldt. Verkeerd = een stille dag-verschuiving in de data. Dit is de reden dat ik hier een expliciete uitspraak van je vraag (vraag 6 onderaan). - Lange periodes lopen vast. Een hypotheek van 30 jaar is geen "10.957 nachten". Voor leningen, verzekeringstermijnen en spaardoelen is duur onzin — daar is de einddatum het enige zinnige. Dus W3 geldt voor korte periodes (dagen tot weken) en W2 voor de lange. Weer een reden dat het er twee zijn.
De drie naast elkaar, en wat ze doen met de acht bestaande flows
| W1 twee sets | W2 Van/Tot | W3 start + duur | |
|---|---|---|---|
| "3 t/m 10 aug" | 4 flicks + Klaar | 3 flicks + 2 tikken | 2 flicks + 1 tik |
| Hoogte | ~550 px | ~390 px | ~420 px |
| Past op iPhone SE | nee | ja | ja |
| Past op 1,5× letters | nee | net | ja |
| "Eind vóór start" mogelijk | ja, klem nodig | nee (wielbereik) | nee (onmogelijk) |
| Verkeerde wiel draaien | makkelijk, en stil | onmogelijk | onmogelijk |
| Je ziet de lengte | als teller | als teller | is de invoer |
| Open einde ("tot nader order") | kan | kan | alleen via W2-stand |
| Lange termijn (30 jaar) | kan | kan | alleen via W2-stand |
| Bouwkosten (na D3's wiel) | 3–4 dagen | 2 dagen | 3–4 dagen |
Per bestaande flow: welke stand past?
| Flow | Nu | Stand | Waarom |
|---|---|---|---|
Vastgoed-boekingadd_booking_sheet.dart:87+:110 | 2 dialogen | W3 · nachten | "3 aug, 7 nachten" is letterlijk hoe je het zegt. Minimaal 1 nacht wordt een ondergrens van de duur i.p.v. een klem-regel. |
Vakantieperiodeadd_vacation_sheet.dart:88+:109 | 2 dialogen | W3 · nachten | Idem. Let op: hier wil je wél kunnen zien of je in een schoolvakantie valt — zie de kanttekening onder deze tabel. |
Medicijnkuuradd_medication_sheet.dart:109 | 2× dezelfde dialoog | W3 · dagen | "Vanaf morgen, 10 dagen" staat op het doosje. Dit is de flow waar duur het meest natuurlijk is van allemaal. |
Schoolvakantieadd_school_holiday_sheet.dart:89+:105 | 2 dialogen | W2 | Je neemt twee datums over uit een brief of van een website. Duur zou je zelf moeten uitrekenen — precies verkeerd om. |
Co-ouderschap-periodeadd_co_parenting_period_sheet.dart:83+:101 | 2 dialogen | W2 · open einde | "Vanaf 1 maart, tot nader order." Er is geen duur, en het eind mag leeg blijven. |
Verzekeringadd_insurance_sheet.dart:313+:324 | 2 dialogen, bereiken sluiten niet aan | W2 | Ingangsdatum en vervaldatum zijn twee feiten van de polis, geen lengte. Wél de niet-aansluitende bereiken repareren. |
Vaste kosten / verzekeringsblokadd_fixed_cost_sheet.dart:669+:680 | 2 dialogen | W2 | Idem. |
Sportseizoenadd_sport_event_sheet.dart:151+:172 | datum + "tot" | W2 | Een seizoen loopt tot een datum die de club bepaalt. "Tot 30 juni" is het feit; "312 dagen" niet. |
Score: drie flows willen duur, vijf willen twee datums. Dat is geen argument tegen W3 — de drie duur-flows zijn juist de flows waar je over viel (boeking en vakantie) en waar de meeste fouten in zaten. Maar het is wel het bewijs dat W2 en W3 samen één component moeten zijn, niet twee alternatieven waaruit je kiest. Eén sheet, twee standen, per aanroep ingesteld — en de gebruiker kan altijd wisselen door de einddatum aan te tikken.
Bij een vakantieperiode is het maandbeeld juist wél nuttig
Van de drie duur-flows is de vakantieperiode de twijfelaar. Als je een vakantie plant, wil je zien of je in de schoolvakantie valt, of je een weekend meepikt, en welke weken al bezet zijn — precies de informatie die een wiel niet toont. Bij een verhuurboeking geldt hetzelfde: welke weken zijn nog vrij?
Dat is een echte spanning en de oplossing is niet "kies er maar één". Het is: op deze twee flows staat de wissel-knop naar het maandgrid standaard in beeld, en onthouden we de keuze van de gebruiker. Wie op gevoel plant tikt naar de kalender; wie een datum al weet blijft op het wiel. Dat kan alleen als beide bodies achter dezelfde wrapper zitten — zie de sectie daarover.
2cWiel of kalender — per situatie of app-breed?
Je vroeg me expliciet of het antwoord per gebruikssituatie verschilt, en of dat de conventie niet te rommelig maakt. Dat is de scherpste vraag in dit hele stuk, dus ik heb het niet op gevoel gedaan: ik ben alle 42 datumplekken langsgelopen met één toets.
De toets
Is deze datum een gegeven dat je al weet, of een keuze die je afweegt tegen andere dagen?
Een gegeven: de aankoopdatum op je bon, de geboortedatum van je kind, de incassodag van je verzekering, de houdbaarheidsdatum op het pak. Je weet het al; je hoeft alleen te tikken wat je weet. → wiel
Een keuze: wanneer plan ik deze afspraak, welke dag zetten we die klus, past dat naast de training? Je hebt de omliggende dagen nodig om te kunnen kiezen. → kalender
De telling
| Body | Aantal | Welke plekken |
|---|---|---|
| Wiel de datum is een gegeven |
31 van 42 | Geboortedatum (add_member_sheet) en terugkerende datum (add_recurring_date_sheet) · aankoopdatum vastgoed · garantie-aankoop · kleedgeld-bon · loonsverhoging-datum · verloopdatum document · uiterste houdbaarheid voorraad · verzekering ingang + vervaldatum (2) · vaste kosten (2) · lening einddatum (2) · spaardoel streefdatum + contributie (2) · wagenpark (2) · onderhoud volgende keer · gewicht · seizoenseinde sportteam · herhalen-tot (recurrence_picker) · en de periodes: boeking (2)‡, vakantie (2)‡, medicijnkuur, schoolvakantie (2), co-ouderschap (2), sportseizoen-einde |
| Maandgrid (D2) de datum is een afweging |
11 van 42 | Agenda-afspraak · taak-deadline · taak-herhaling vanaf · maaltijd inplannen · sportwedstrijd-datum · klus-herhaling vanaf · beweegsessie · afval-pauze · afval-ophaal · vakantie-item (dag binnen de vakantie) · medicijn-startdatum† |
† Grensgeval: de startdatum van een kuur is meestal "vandaag" of "morgen" (dus een chip), maar soms plan je hem rond een reis. Deze zit als startpunt in de kuur-periode, dus in de praktijk erft hij de body van die periode.
‡ Dit zijn de twee flows waar de wissel-knop naar het maandgrid standaard in beeld staat — zie de kanttekening aan het eind van sectie 2b. Ze staan in de wielrij omdat het wiel er de standaard is, niet omdat de kalender er geen zin heeft.
Twee dingen die deze telling zegt, en één die ze niet zegt.
- Het wiel is voor de meerderheid het juiste antwoord — 31 van de 42. Jouw voorkeur is dus niet alleen smaak, hij past ook op de code.
- Het wiel lost jouw hoofdklacht schoner op dan D2. Van juli naar maart is nu vier tikken op
‹. In D2 is het twee tikken (maandknop + maand kiezen). Op een maandwiel is het één veeg. Op de 31 wiel-plekken is het wiel dus strikt beter dan zowel de huidige kiezer als D2. - Maar aantal is niet gebruik. Die 11 maandgrid-plekken zijn de schermen die je gezin dagelijks aanraakt: agenda, taken, maaltijden. De 31 wiel-plekken zijn grotendeels formulieren die je één keer per polis of per apparaat invult. Als je op frequentie zou wegen in plaats van op aantal, kantelt het beeld. Daarom is "app-breed één vorm" in beide richtingen een slecht idee.
Wordt het dan niet rommelig?
Dat was je zorg, en die is terecht — twee soorten datumkiezers in één app is precies het soort inconsistentie waar je aan het begin over viel. Drie dingen houden het bij elkaar:
- Het is één sheet, niet twee dialogen. Dezelfde bottom-sheet, dezelfde kop, dezelfde snelkoppelingschips bovenaan (Vandaag / Morgen / Volgende week), hetzelfde typveld onderaan, dezelfde Klaar-balk. Alleen het middenstuk wisselt tussen een wielenbank en een maandgrid. Dat leest niet als twee producten; het leest als één kiezer die twee gezichten heeft — zoals een toetsenbord dat wisselt tussen letters en cijfers.
- De regel is uit te leggen in één zin (de toets hierboven). Een conventie die je in één zin kunt zeggen, is geen rommel. Een conventie die per scherm anders is "omdat het daar beter voelde", dát is rommel.
- Er staat een wissel-knop in de sheet. Een klein wiel/kalender-icoontje in de hoek. Per plek zetten wij de standaard; de gebruiker mag altijd omschakelen en we onthouden zijn keuze. Zo is een verkeerde gok van ons één tik werk voor hem in plaats van een klacht. Dit is ook het antwoord op de vakantie-kanttekening uit 2b.
Mijn keuze, expliciet: per situatie, langs die ene regel, met een wissel-knop als vangnet — en niet app-breed één vorm. De reden dat ik dit durf: het kost bijna niets extra zodra beide bodies achter dezelfde wrapper zitten, en het maakt de vraag "wiel of kalender hier?" omkeerbaar in plaats van definitief.
Eén component of twee? — de vraag die je openliet
Je zei: "dat mogen eventueel twee aparte componenten zijn of een variatie op hetzelfde component." Dit is geen detail, want het bepaalt hoe die 42 plekken eruit gaan zien. Ik werk beide kanten uit.
A Eén component, één aanroep met een stand
pickTendloDate(mode: single | range) — één functie, één sheet, één antwoord.
Wat het je geeft
- Eén plek waar de vormgeving, de voorleeslabels, de talen en het parse-veldje leven. Verander je iets, dan verandert het overal.
- Onmogelijk om per ongeluk twee verschillende sheets te krijgen.
Wat het je kost
- Het antwoord is een of-of. Eén datum óf een periode terug. Elk van de 42 plekken moet dan uitpakken welk geval het is — een
switchop iets waarvan de aanroeper al zeker wist wat hij vroeg. Dat is 42 keer overbodige code, en 42 kansen op een fout in het "onmogelijke" geval. - Parameters die maar in één stand betekenis hebben (
minimaal aantal nachten,eind mag leeg zijn) hangen dan alsnog aan de enkelvoudige aanroep. Dat leest slecht.
B Twee aanroepen, één kern advies
pickTendloDate(...) → DateTime? en pickTendloDateRange(...) → DateTimeRange?, die beide dezelfde TendloCalendar-body openen.
Wat het je geeft
- Voor de gebruiker is het één component — zelfde sheet, zelfde kop, zelfde grid, zelfde snelkoppelingen. Het verschil is één regel velden bovenin en een nachtenteller.
- Voor de code zijn het twee duidelijke vragen met elk één soort antwoord. Geen uitpakwerk op 42 plekken.
- De periode-eigen regels (eind na start, minimaal 1 nacht bij een boeking, eind mag leeg blijven bij co-ouderschap) staan alleen in de periode-aanroep, waar ze horen. Precies de vier-verschillende-gedragingen uit deel 1 die daarmee verdwijnen.
Wat het je kost
- Twee functies om te onderhouden in plaats van één. In de praktijk zijn dat twee dunne laagjes van ~20 regels boven dezelfde body — de kern blijft één plek.
- Je moet discipline houden dat de body nooit uit elkaar groeit. Één test die de twee standen naast elkaar zet, houdt dat vast.
Kort: één component in de ogen van Sandra, twee ingangen in de ogen van de code. Dat is niet dubbelop — dat is het verschil tussen "hoe het eruitziet" en "wat je vraagt".
Drie richtingen voor de tijdkiezer
Uitgangspunt uit de code: in Tendlo vallen tijden bijna altijd op een kwartier of een half uur (afspraken 09:00, sport 18:30, medicijnen 08:00, digest 07:00, stille uren 22:00–07:00). En op vier plekken is een tijd eigenlijk een tijdvak, geen tijdstip. De schijf van Material rondt al af op 5 minuten, dus het probleem is niet de precisie — het is dat je twee schijven moet slepen waar één tik genoeg was.
Was T1 (raster), wordt T2 (wiel) — en dat is dezelfde redenering, niet een nieuwe
In versie 1 adviseerde ik het kwartierraster (T1) en wees ik het wiel (T2) af met precies één argument: "het botst met een grid-datumkiezer in de zes formulieren waar datum en tijd samen staan." Dat argument was gebonden aan D2. Nu jij voor wielen kiest, pleit datzelfde argument voor T2: het wiel is dan de vorm die overeenkomt met de datumkiezer.
Dus: T2 wordt het advies, T1 verhuist naar het reservebankje, en T3 (duurchips) wordt belangrijker dan hij was — want T3 is nu niet meer alleen een handigheidje voor tijdvakken, het is de tweelingbroer van W3. Beide zeggen: een periode is een startpunt plus een lengte.
Eén wrinkel die ik niet wegpoets: op de 11 maandgrid-plekken uit sectie 2c (agenda, taken, maaltijden) krijg je een maandgrid voor de datum en een wiel voor de tijd in hetzelfde formulier. Dat is minder erg dan het klinkt — datum en tijd zijn ook echt verschillende grootheden, ze zitten in dezelfde sheet-vormgeving, en de wissel-knop laat wie dat wil alles op wielen zetten. Maar het is een oneffenheid, en je hoort hem van mij te weten.
T1 Kwartierraster in een sheet was v1-advies
Een raster van tijden: uren als rijen, :00 :15 :30 :45 als kolommen. Eén tik is een tijd.
- Opent op het relevante blok (ochtend/middag/avond), met dagdeel-knoppen om te springen.
- Snelkoppelingen: nu, +15 min, +1 uur, en (waar zinnig) de vorige keuze van de gebruiker.
- "Precieze tijd" klapt onderaan een 4-cijferveld open (
0752→ 07:52) voor de uitzonderingen. - Vormtaal is identiek aan D2: bottom-sheet, chips boven, raster in het midden, typen als ontsnapping.
18:30 is één tik. 07:52 is één tik extra.
Hoe het voelt
Sandra tikt "Kies tijd", ziet de avond staan, tikt 18:30. Klaar. Geen slepen, geen tweede etappe, geen OK. Vergelijk met nu: urenschijf slepen → naar minuten → minutenschijf slepen → OK.
"18:30" = 1 tik (nu: 4 handelingen)Wat het kost
Laag tot midden — 2 à 3 dagen. Het is voornamelijk opmaak plus voorleeslabels. Geen wiskunde, geen parser (behalve het 4-cijferveldje), geen locale-valkuilen behalve 12- versus 24-uursweergave, die je uit MaterialLocalizations kunt halen.
Wat je opgeeft
- Vijf-minuten-tijden (07:05) kosten een extra tik via "precieze tijd". Dat is een bewuste ruil: 95% van de gevallen wordt sneller, 5% een tik trager.
- Het raster is lang. 24 uur × 4 = 96 cellen; je scrollt binnen een dag. De dagdeel-knoppen halen dat weg, maar het is wél een lijst.
- Op tekstgrootte 1,5× worden vier kolommen krap — dan moeten het twee kolommen worden (
:00 :30) met de kwartieren achter "precieze tijd". Dat is een echte concessie op grote letters.
T2 Wiel met kwartierstap advies in v2
Uur- en minutenwiel, minuten in stappen van 15 (of 5), met een schakelaar naar minuutprecisie.
De iOS-standaard. Twee flicks en je bent er. Simpel te bouwen en heel voorspelbaar — en nu je voor wielen kiest bij datum, is dit óók de vorm die bij de rest past.
Waarom dit nu wint: mijn enige bezwaar in versie 1 was dat een tijdwiel botst met een datumgrid. Op de 31 wiel-plekken uit sectie 2c is dat bezwaar weg — daar wordt datum óók een wiel, en dan is één formulier één handeling: flicken. Op de 11 maandgrid-plekken blijft de oneffenheid staan; die accepteer ik bewust, want een tijdraster naast een datumwiel zou de spiegelbeeldige botsing geven en dan schuif je het probleem alleen op.
Belangrijk detail: zet de minutenstap op 15, niet op 5. Uit deel 1 bleek dat de huidige schijf al op 5 minuten afrondt en dat toch traag voelt — met stappen van 15 wordt het minutenwiel 4 waarden lang in plaats van 12, en dan is 18:30 letterlijk één korte veeg. De "elke minuut"-schakelaar onderaan dekt de uitzonderingen (medicijn om 07:52).
Vertrouwd en snel — maar een andere handeling dan het datumgrid.
Hoe het voelt
Zeer bekend op iPhone; twee flicks + Klaar. Maar je ziet maar 2 waarden boven en onder de keuze: geen overzicht van "wat kies ik meestal", en op Android voelt het vreemd.
"18:30" ≈ 2 flicks + 1 tikWat het kost
Het laagst — 1 à 2 dagen. Twee wielen, klaar. Geen scroll-anker, geen dagdelen, geen raster.
Wat je opgeeft
- Het overzicht van "wat kies ik meestal". Een raster laat de vier kwartieren van vijf uren tegelijk zien; een wiel laat twee waarden boven en onder je keuze zien. Voor "even kijken welke tijd handig is" is dat armer.
- Flick-precisie op 1,5× letters, net als bij het datumwiel. Bij twee kolommen is dat minder erg dan bij drie, maar het blijft slepen in plaats van tikken.
- Eén ontwerptaal op de 11 maandgrid-plekken (agenda, taken, maaltijden): daar staat een maandgrid boven een tijdwiel. Bewust geaccepteerd, zie de notitie bovenaan deze sectie.
- Nog steeds twee kiezers voor een tijdvak — tenzij je T3 erbij neemt, en dat is precies waarom T3 nu geen extraatje meer is.
T3 Duurchips in plaats van een tweede kiezer tweelingbroer van W3
Niet een andere tijdkiezer, maar de oplossing voor het tijdvak: kies een starttijd, tik dan een duur.
Dit is dezelfde vondst als W3, maar voor uren. Waar W3 zegt "vanaf 3 augustus, 7 nachten", zegt T3 "vanaf 09:00, 1 uur". Dat is niet toevallig hetzelfde patroon — het is de reden dat datum en tijd na deze wijziging écht één taal spreken, en die taal is: een periode is een startpunt plus een lengte. Bouw je W3, dan is T3 er bijna gratis bij (en omgekeerd).
Dit is de tijd-tegenhanger van je tweede klacht. Nu kost een afspraak van 09:00 tot 10:00 twee volledige kiezers. Met duurchips is het: starttijd kiezen → 1 uur tikken. De eindtijd wordt afgeleid, staat er zichtbaar bij, en blijft aanpasbaar voor het uitzonderingsgeval.
Belangrijk: ik stel niet voor om "duur" op te slaan. De eindtijd blijft het opgeslagen veld — duur is puur een snellere manier om die te zetten. Dat houdt het datamodel ongemoeid en is dus geen migratie.
Van toepassing op: agenda-afspraak, sportwedstrijd, beweegsessie, en (in andere vorm) de stille uren.
Eén tik in plaats van een tweede kiezer. De eindtijd blijft zichtbaar en aanpasbaar.
Hoe het voelt
Een afspraak inplannen wordt: tik 09:00 in het raster, tik "1 uur". Twee tikken voor iets wat nu twee schijven en twee keer OK kost.
"09:00–10:00" = 2 tikken (nu: 8 handelingen)Wat het kost
Midden — 2 dagen, vooral in de vier tijdvak-formulieren (agenda, sport, beweging, stille uren) plus teksten in 3 talen. Geen datamodelwijziging, dus geen migratie en geen sync-risico.
Wat je opgeeft
- Een tweede manier om hetzelfde te zetten. De eindtijd is nu op twee manieren te wijzigen (duurchip of direct). Dat moet consequent blijven: tik je op de eindtijd, dan moet de duurchip meelopen. Dat is precies het soort ding dat je op vier plekken los kunt verknoeien — dus dit móet in het gedeelde component, niet per formulier.
- Voor stille uren (22:00–07:00) klopt "duur" niet als woord — daar blijft het twee tijden, in één sheet met twee velden (zelfde patroon als de periode-datumkiezer).
Waarom datum en tijd hier dezelfde taal spreken
Je vroeg expliciet om één ontwerptaal. In add_appointment_sheet, add_sport_event_sheet, add_task_sheet, add_medication_sheet, add_movement_session_sheet en add_vacation_item_sheet staan datum en tijd in hetzelfde formulier onder elkaar. Na deze wijziging delen ze twee lagen — en de tweede is belangrijker dan de eerste.
Laag 1 — dezelfde vorm
- Bottom-sheet, geen dialoog in het midden van het scherm.
- Snelkoppelingen bovenaan als chips (Vandaag / Morgen — nu / +15 min).
- Wielen in het midden: dag/maand/jaar voor datum, uur/kwartier voor tijd. Eén veeg = één keuze. (Op de 11 maandgrid-plekken is het middenstuk een maandgrid; de rest van de sheet blijft identiek.)
- Typen als ontsnapping onderaan, altijd zichtbaar maar nooit in de weg.
Laag 2 — dezelfde logica, en dít is de echte winst
Een periode is overal een startpunt plus een lengte. "Vanaf 3 augustus, 7 nachten" (W3) en "vanaf 09:00, 1 uur" (T3) zijn hetzelfde idee op twee schalen. Dat betekent dat een gebruiker die één keer begrijpt hoe een vakantieperiode werkt, óók weet hoe een afspraak van anderhalf uur werkt. En het betekent dat "eind vóór start" in de hele app geen bestaande toestand meer is — niet bij dagen, niet bij uren.
Dat is een sterkere vorm van consistentie dan gelijke opmaak. Opmaak kun je zien; logica voel je pas als je een fout niet meer kunt maken.
Kunnen wielen en D2 naast elkaar bestaan?
Je vroeg dit expliciet, en het antwoord bepaalt of de keuze uit sectie 2c omkeerbaar is of dat je er nu voorgoed aan vastzit. Het antwoord is ja, en het sluit precies aan op het wrapper-advies dat al in versie 1 stond — dat advies wordt hier van "netjes" naar "de spil van het hele plan".
Hoe het werkt
Alle 55 aanroepen gaan door pickTendloDate / pickTendloDateRange / pickTendloTime. Die functies openen één sheet die drie dingen los van elkaar vaststelt:
- Het chroom — sheet, kop, snelkoppelingschips, typveld, Klaar-balk. Altijd hetzelfde, ongeacht de rest.
- De body — een wielenbank óf een maandgrid. Dit is de enige plek waar wiel-versus-kalender bestaat.
- De periodestand — één datum, of start+duur (W3), of Van/Tot (W2). Onafhankelijk van de body: een periode kan zowel met wielen als met een maandgrid.
Omdat die drie los staan, is de verdeling uit sectie 2c niets meer dan één tabel van standaarden in één bestand. Die tabel aanpassen — na jouw feedback, na tester-feedback, of omdat we het gewoon verkeerd gokten — raakt nul van de 55 aanroepen.
| Wat je later wil veranderen | Wat er dan moet gebeuren |
|---|---|
| "Agenda moet toch een wiel worden" | Eén regel in de standaardentabel. 1 bestand |
| "Alles op wielen, ook agenda" | Standaard omzetten. 1 bestand |
| "Ik wil het per gebruiker instelbaar" | Eén voorkeurinstelling die de standaardentabel overrulet. 1 bestand + 1 instelling |
| "De duur-eenheid bij vakanties moet dagen zijn, niet nachten" | Eén regel in dezelfde tabel. 1 bestand |
| "Toch helemaal terug naar Material" | De body-keuze op Material zetten. 1 bestand |
| Zonder wrapper: elk van deze wijzigingen | 34 bestanden (datum) + 10 (tijd) |
Wat het kost om béide bodies te bouwen
Dit is de vraag die je waarschijnlijk echt stelt: is dat niet dubbel werk? Nee — het is ongeveer 2 à 3 dagen extra, omdat het meeste werk niet in de body zit.
| Onderdeel | Kost | Gedeeld? |
|---|---|---|
| Sheet-chroom, chips, typveld, min/max, voorleeslabels, 3 talen | 3 dagen | gedeeld |
| Periodelogica W2 + W3 (duur, eenheden, open einde) | 2–3 dagen | gedeeld |
| Body A — wielenbank (dag/maand/jaar, schrikkeljaren, klemmen op bereik) | 2 dagen | eigen |
| Body B — maandgrid (D2: grid, maand/jaar-menu, weeknummers) | 3 dagen | eigen |
| Wissel-knop + onthouden van de voorkeur | ½ dag | gedeeld |
Alleen wielen: ~8 dagen. Wielen + D2: ~11 dagen. Voor 3 dagen koop je: de 11 afweeg-plekken goed bediend, een vangnet als we een plek verkeerd inschatten, en een keuze die je later kunt verschuiven zonder migratie. Dat is de goedkoopste verzekering in dit hele voorstel.
En het omgekeerde is óók waar en daarom vermeld ik het: als je die 3 dagen niet wil, dan is "alleen wielen, overal" een verdedigbare keuze — met de eerlijke prijs dat agenda, taken en maaltijden hun maandbeeld kwijt zijn en dat je dat later alleen met een tweede bouwronde terughaalt.
Aanbeveling
Wielen als hoofdvorm · W3 (start + duur) voor periodes met W2 als ingebouwde ontsnapping · D2's maandgrid als tweede body op de 11 afweeg-plekken · T2 + T3 voor tijd · alles achter één wrapper
Datum, hoofdvorm: het wiel (D3) — jouw keuze, en de telling in sectie 2c bevestigt hem: 31 van de 42 plekken hebben het maandbeeld niet nodig, en op een wiel is "naar een andere maand" één veeg in plaats van vier tikken.
Periode: W3 — startdatum op wielen plus een duurregel, met de einddatum als aantikbare uitkomst die W2's Van/Tot-stand opent. Drie flows draaien op duur (boeking, vakantie, medicijnkuur), vijf op twee datums (schoolvakantie, co-ouderschap, verzekering, vaste kosten, sportseizoen). Eén component, twee standen.
Datum, tweede body: D2's maandgrid op de 13 plekken waar de datum een afweging is (agenda, taken, maaltijden, sport, afval, klussen) — plus als omschakelbare standaard op vakantie en boeking. Niet als terugval, maar als het tweede gezicht van dezelfde kiezer.
Tijd: T2 (uur/kwartier-wiel, minutenstap 15) plus T3 (duurchips) op de vier tijdvak-plekken.
Vorm: B — voor Sandra één component, voor de code twee ingangen (pickTendloDate / pickTendloDateRange) op één gedeelde kern.
Voorschot: D1's parameterfixes gaan meteen in de wrapper (de twee absurde bereiken snoeien, jaarmodus aan waar het bereik breed is). Een halve dag, en de huidige kiezer is direct minder erg terwijl de rest gebouwd wordt.
De rationale, kort
- Het wiel lost je hoofdklacht schoner op dan D2. Dat is de belangrijkste uitkomst van dit huiswerk. Van juli naar maart: nu 4 tikken, in D2 2 tikken, op een maandwiel 1 veeg. Op de 29 plekken waar je het maandbeeld niet nodig hebt, is het wiel dus niet alleen vertrouwder — het is ook objectief minder werk.
- W3 is de vondst. Ik ging op zoek naar "kan een periode met wielen" en vond iets beter dan een periodekiezer: door een periode te definiëren als start + lengte houdt "eind vóór start" op te bestaan. De vier verschillende noodgrepen die ik in deel 1 vond, verdwijnen niet doordat we ze centraliseren maar doordat het probleem verdampt. Dat had ik met een kalender-periodekiezer (D2/W1) nooit gekregen.
- W3 en T3 zijn hetzelfde idee. "3 augustus + 7 nachten" en "09:00 + 1 uur". Daarmee is de ene ontwerptaal die je vroeg geen opmaakafspraak meer maar een regel: een periode is een startpunt plus een lengte. Consistentie die je voelt doordat je een fout niet meer kunt maken, is sterker dan consistentie die je ziet.
- W1 wijs ik af, maar niet als stroman. Twee wielensets in één sheet is de meest letterlijke lezing van je klacht en volledig geldig. Hij verliest op twee harde dingen: hij past niet op een kleine telefoon of bij 1,5× letters (~550 px), en twee gelijkende wielenbanken laten je stil de verkeerde draaien. Als je duur-semantiek niet vertrouwt, is W1 wél de veilige keuze — zeg het en we doen W1+W2.
- D2 er niet uitgooien is 3 dagen waard. Beide bodies achter dezelfde wrapper kost ~11 dagen in plaats van ~8. Daarvoor krijg je: de dagelijks gebruikte schermen goed bediend, een vangnet als we een plek verkeerd inschatten, en een verdeling die later verschuift zonder de 55 aanroepen aan te raken. Dat is de goedkoopste verzekering in dit voorstel.
- Per situatie, langs één regel — niet app-breed. "Is deze datum een gegeven dat je weet, of een keuze die je afweegt?" Dat is in één zin uit te leggen, en het chroom van de sheet blijft identiek, dus het leest als één kiezer met twee gezichten in plaats van twee producten. De wissel-knop maakt onze gok omkeerbaar.
- De wrapper is nu de spil, niet de nette bijzaak. In versie 1 was hij een migratietruc. Nu draagt hij de hele keuze: twee bodies, drie periodestanden en een per-situatie-tabel, allemaal op één plek. Dat is de reden om hem als eerste te bouwen en niet als laatste.
De twee eerlijke tegenargumenten
1 · Aantal is niet gebruik. Die 31 wiel-plekken zijn grotendeels formulieren die je één keer per polis invult; de 11 maandgrid-plekken zijn agenda, taken en maaltijden — dagelijks. Als je op frequentie weegt in plaats van op aantal, is de kalender de hoofdvorm en het wiel de uitzondering. Ik kies toch voor het wiel als hoofdvorm omdat jij dat vertrouwd vindt en omdat het de maandsprong beter oplost, maar je moet weten dat de telling niet zo eenduidig is als "31 tegen 11" suggereert.
2 · Op agenda krijg je grid + wiel in één formulier. Datum als maandgrid, tijd als wiel, onder elkaar. Dat is een oneffenheid. Ik accepteer hem omdat de spiegelbeeldige keuze (tijdraster naast datumwiel) het probleem alleen verplaatst, en omdat de wissel-knop wie dat wil alles op wielen laat zetten. Maar het is geen vlekkeloze uitkomst en ik wil niet doen alsof.
En als je het goedkoop wil houden
Dan is het antwoord wielen + W3/W2, zonder D2: ~8 dagen. Elke datum en elke tijd wordt een wiel, periodes worden start+duur met een Van/Tot-ontsnapping, en agenda/taken/maaltijden verliezen hun maandbeeld. Dat is een consistente, verdedigbare app — en als het gezin het maandbeeld gaat missen, is D2 er later bij te bouwen als tweede body zonder één aanroep aan te raken. Dat is precies waarom de wrapper eerst komt.
Over externe pakketten — bewust niet
Er zijn pakketten die dit doen (omni_datetime_picker, board_datetime_picker, table_calendar). Ik stel er geen voor, en dat is een afweging, geen dogma:
table_calendaris een inline kalender-widget, geen kiezer-dialoog. Hij heeft geen maand/jaar-kop en geen periodelogica die past; je bouwt de sheet, de snelkoppelingen en de range-regels alsnog zelf. Je krijgt dus 30% en erft 100% van de afhankelijkheid.omni_datetime_picker/board_datetime_pickerdoen wél single + range + tijd, maar brengen hun eigen vormtaal mee die je vervolgens gaat overrulen — en ze zouden een interactie dragen die op 55 plekken in de app zit. Raakt zo'n pakket achterop bij een Flutter-upgrade, dan staat je hele invoer stil. Voor een randfunctie is dat een prima ruil; voor de kern van elk formulier niet.- Het component dat ik voorstel is naar schatting 350–450 regels en leunt voor maandnamen, eerste weekdag en 12/24-uursweergave op
MaterialLocalizations— het risico dat je zelf draagt is dus vooral opmaak, niet logica.
Eén nuance nu je voor wielen kiest, want er is wél iets kant-en-klaars: Flutter levert CupertinoDatePicker en CupertinoTimerPicker mee, gratis en zonder pakket. Waarom ik die alsnog niet voorstel:
CupertinoDatePickeris niet te thematiseren naar Tendlo's huisstijl (vaste iOS-kleuren en -typografie), en op Android voelt hij als een vreemd eiland.- Hij heeft geen vierde kolom en geen ruimte voor een duurregel — W3 is er dus niet mee te maken, en dat is juist de vondst waar dit hele voorstel op rust.
- Wat we wél gebruiken is de onderliggende bouwsteen:
ListWheelScrollView, ook uit Flutter zelf. Dat is de motor van elk wiel, geeft hetzelfde vertrouwde gevoel (magnetisch inklikken, dezelfde vertraging), en laat zich volledig in onze eigen kleuren en teksten zetten. Het wiel dat ik voorstel is dus geen namaak-iOS-wiel — het is hetzelfde mechaniek in onze vormgeving.
3Hoe dit app-breed wordt doorgevoerd
Eerlijk over de omvang: 55 aanroepen in 44 bestanden. Dat klinkt als een berg, en dat is het ook — maar de vorm van het werk maakt het beheersbaar, op één voorwaarde.
Eerst de wrapper, dan het component — niet omgekeerd
Als we eerst de mooie sheet bouwen en die daarna op 44 bestanden inzetten, dan is dat één grote onomkeerbare stap waarin ontwerpfouten en migratiefouten door elkaar lopen. Daarom eerst een gedeelde wrapper die niets verandert: lib/shared/widgets/date_time/ met pickTendloDate, pickTendloDateRange en pickTendloTime, die in versie 1 gewoon showDatePicker/showDateRangePicker/showTimePicker aanroepen — mét de D1-fixes erin.
Alle 55 plekken migreren dan naar de wrapper zonder dat er visueel iets verandert. Dat is mechanisch werk dat je per module kunt reviewen en testen. Daarna is de omslag naar de wielen één bestand. En als er iets niet bevalt, is het één bestand terug.
Na jouw keuze is dit niet langer alleen een migratietruc. De wrapper draagt de hele beslissing: twee bodies (wiel / maandgrid), drie periodestanden (één datum / start+duur / Van-Tot), de per-situatie-tabel uit sectie 2c, en de duur-eenheid per plek. Alles wat je later wil verschuiven, verschuift daar — en nergens anders. Zie de sectie over naast elkaar bestaan.
De veilige volgorde, in zes groepen
Wrapper + D1-fixes — nog geen zichtbare verandering
Wrapper aanmaken; de twee absurde bereiken snoeien (recurrence_picker.dart:106-107 ±100 jaar, add_agreement_sheet.dart:159-160 1900–2200); jaarmodus aanzetten waar het bereik >2 jaar is. En meteen de standaardentabel aanleggen die de rest gaat dragen: per plek de body (wiel / grid), de periodestand (één datum / duur / Van-Tot), de duur-eenheid (nachten / dagen) en de bestaande grenzen (minimaleDuur, eindMagLeegZijn, alleen-verleden / alleen-toekomst).
1 nieuw bestand + 2 gesnoeide bereiken · risico: laag · levert al merkbare winst
Het wiel bouwen en op één plek toetsen
Wielenbank maken (dag/maand/jaar, schrikkeljaren, klemmen op het bereik) en inzetten op drie wiel-plekken met de laagste inzet: geboortedatum (add_member_sheet.dart:67), verloopdatum document (add_document_sheet.dart:100) en uiterste houdbaarheid (add_stock_item_sheet.dart:106). Dat zijn losse datums, geen periodes, en het zijn precies de gevallen waar het wiel evident beter is dan wat er nu staat — dus als het hier niet goed voelt, weten we het meteen en is er nauwelijks werk verspild.
3 bestanden · 3 datum-aanroepen · gate: hier stoppen en jouw oordeel vragen over het wielgevoel
De periodes — de grootste winst, en de riskantste logica
W3 en W2 bouwen, en inzetten op de acht periode-flows volgens de tabel in sectie 2b: duur-stand voor boeking, vakantie en medicijnkuur; Van/Tot-stand voor schoolvakantie, co-ouderschap, verzekering, vaste kosten en sportseizoen. Zeventien aanroepen worden acht periode-aanroepen.
Hier verdwijnen de vier verschillende "eind ligt vóór start"-gedragingen — bij de duur-flows omdat het probleem ophoudt te bestaan, bij de Van/Tot-flows omdat het wielbereik de ongeldige dagen niet bevat. Let op de eigenaardigheden die móeten blijven werken: een boeking heeft minstens één nacht (wordt een ondergrens van de duur), add_booking_sheet.dart:133 ankert de gekozen dag op het middaguur (tijdzone-bug uit een eerdere panel-review, mag de nieuwe kiezer niet wegnemen), en de niet-aansluitende bereiken bij verzekeringen worden hier gerepareerd.
Dit is de groep waar de duur-eenheid hard wordt (nachten versus dagen t/m, zie vraag 6). Geen enkele andere groep kan hier omheen — dus die vraag moet beantwoord zijn vóór groep 2 begint.
Eén flow is een hybride en die is leerzaam: bij een sportwedstrijd wil de datum (add_sport_event_sheet.dart:151) het maandgrid, want je plant rond andere wedstrijden — terwijl het seizoenseinde (:172) prima een Van/Tot-wiel is. Dat kan alleen doordat body (wiel/grid) en periodestand (één datum / duur / Van-Tot) in de wrapper onafhankelijk van elkaar staan. Zonder die scheiding zou deze flow niet kloppen.
8 bestanden · 15 → 8 aanroepen · risico: hoog — echte gedragswijziging én een dag-verschuiving-valkuil
Financieel, de losse datums — veel herhaling van hetzelfde patroon
Wat er van financieel overblijft nadat verzekeringen en vaste kosten in groep 2 zijn meegegaan: garanties, leningen (2×), spaardoelen (2×), wagenpark (2×). Allemaal losse datums op het wiel. Garanties heeft lastDate: now — de "alleen verleden"-stand moet hier bewijzen dat hij werkt, en dat is bij een wiel een ander soort werk dan bij een grid (het jaarwiel mag simpelweg niet verder dan dit jaar).
Lening-einddatum (+40 jaar) en spaardoel (+30 jaar) zijn de plekken waar het wiel het meest wint van de huidige kiezer: van "scroll door een jaargrid" naar "één veeg op het jaarwiel".
7 bestanden · 7 aanroepen · risico: laag
De staart — hier landt de tweede body
De resterende 17 losse datums. Dit is de groep waar de verdeling uit sectie 2c voor het eerst zichtbaar wordt: negen ervan krijgen het maandgrid (agenda-afspraak, taak-deadline, taak-herhaling vanaf, maaltijd, klus-herhaling vanaf, beweegsessie, afval-pauze, afval-ophaal, vakantie-item) en acht het wiel (vastgoed-aankoopdatum, kleedgeld-bon, loonsverhoging, verjaardag/terugkerende datum, gewicht, onderhoud, seizoenseinde sportteam, herhalen-tot).
Doe deze groep pas ná groep 1–3, want dan is het wiel al bewezen en kun je de kalender-body ernaast zetten zonder dat twee onbewezen dingen tegelijk fout kunnen gaan.
16 bestanden · 17 aanroepen · risico: laag, maar het is de eerste keer dat beide bodies naast elkaar in de app staan
Tijd — het wiel plus de vier tijdvakken
De 13 tijd-aanroepen krijgen T2's uur/kwartier-wiel. Agenda, sport en beweging krijgen daarbij T3's duurchips; stille uren (quiet_hours_editor.dart:19 + :25) worden één sheet met een Van/Tot-schakelaar — dezelfde W2-vorm als bij datum, want "duur" is daar geen zinnig woord.
Let op de drie instellingen-tijden (digest, module-meldingstijd): die geven nu helpText mee (instellingen_screen.dart:279, quiet_hours_editor.dart:22, :28); de wrapper moet dat blijven doorgeven, en dáár wil je geen duurchips.
10 bestanden · 13 aanroepen · risico: laag-midden
Wat dit bij elkaar kost
| Onderdeel | Grove schatting | Opmerking |
|---|---|---|
| Wrapper + D1-fixes (groep 0) | 1–2 dagen | draagt straks de hele keuze; onomkeerbaar niets |
| Gedeelde kern: chroom, chips, typveld, min/max, a11y, 3 talen | 3 dagen | onafhankelijk van welke body je kiest |
| Body A — wielenbank (D3) | 2 dagen | op ListWheelScrollView uit Flutter zelf |
| Periodestanden W3 + W2 | 2–3 dagen | de vondst; hier verdwijnt "eind vóór start" |
| Tijd: T2-wiel + T3-duurchips | 2–3 dagen | deelt kern en logica met datum |
| Body B — maandgrid (D2) + wissel-knop | 3½ dagen | optioneel — dit is de 3 dagen "verzekering" uit de vorige sectie |
| Migratie van 55 aanroepen (groep 1–5) | 3–4 dagen | mechanisch, per groep te reviewen; 15 → 8 bij de periodes |
| Voorleeslabels, tests, 3 talen afmaken | 2 dagen | testkoppeling is laag: maar 2 bestaande testbestanden raken de kiezers |
| Totaal zonder body B | 15–19 dagen | alles op wielen; agenda c.s. zonder maandbeeld |
| Totaal met body B | 18–23 dagen | in 6 leverbare stappen, met een beslismoment na groep 1 |
Wat de omvang meevalt: de testkoppeling. Slechts 2 testbestanden raken de kiezers, dus de migratie breekt bijna niets. Wat de omvang tegenvalt: de 55 aanroepen zijn nu allemaal net iets anders geconfigureerd (elk eigen firstDate/lastDate/initialDate-berekening, soms met eigen klem-logica zoals add_sport_event_sheet.dart:164-171). Die eigenaardigheden moeten één voor één worden gelezen en overgezet — dat kun je niet met zoeken-en-vervangen.
Wat ik van jou nodig heb voordat er code in gaat
✅ Beslist door de eigenaar op 27 juli — vraag 1, 2 en 6
Vraag 6 (nachten of dagen): de eenheid wordt per aanroep vastgelegd — boeking en vakantie in nachten, medicijnkuur in dagen t/m — en de gebruiker moet altijd de mogelijkheid houden om begin- en einddatum los in te geven, waarbij de component het aantal nachten of dagen zelf uitrekent en toont. Dat is niet een extra wens naast W3 maar precies de W2-stand: de duur-stand is de snelle weg, de Van/Tot-stand is een volwaardig alternatief dat altijd bereikbaar is. Daarmee is ook vraag 1 beslist: W3 + W2 als één component met twee standen, W1 afgewezen.
Vraag 2 (maandgrid erbij): ja, erbij bouwen. ~18-23 dagen in plaats van ~15-19. Wielen en maandgrid achter dezelfde wrapper, zodat de verdeling wiel/kalender daarna een wijziging in één bestand is in plaats van in 55 aanroepen. Agenda, taken en maaltijden houden hun maandbeeld.
Wat dit betekent voor de bouw: de duur-stand mag nooit de enige weg zijn. Elke periode-aanroep krijgt dus twee dingen mee: de eenheid (nachten of dagen t/m) én de garantie dat de Van/Tot-stand bereikbaar is. De uitkomst staat altijd in tekst in de sheet ("7 nachten · t/m ma 10 aug"), in beide standen — dat is wat een verkeerde eenheid zichtbaar maakt in plaats van stil laat doorlekken.
Hieronder de acht vragen zoals ze voorlagen. 1, 2 en 6 zijn hierboven beslist en blijven staan als vastlegging van de afweging; 3, 4, 5, 7 en 8 zijn nog open en geen van alle blokkerend voor de eerste uitrolgroep.
1 · Klopt mijn uitwerking van de periode-met-wielen? — nieuw
Je vroeg om W1, W2 en W3 uitgewerkt. Mijn conclusie: W3 (start + duur) als hoofdstand met W2 (Van/Tot) als ingebouwde ontsnapping, en W1 afgewezen omdat hij niet op een kleine telefoon past en je er stil het verkeerde wiel draait. Drie flows op duur, vijf op twee datums.
Advies: W3 + W2 als één component met twee standen. Als je duur-semantiek niet vertrouwt: W1 + W2, dan blijven het gewoon twee datums en is de eenheid-vraag (6) minder scherp — maar dan houd je de klem-regel én de opmaakproblemen op kleine schermen.
2 · Bouwen we D2's maandgrid erbij, of alles op wielen? — nieuw
Dit is de 3-dagen-vraag uit de sectie over naast elkaar bestaan. Alles op wielen kost ~15-19 dagen en agenda, taken en maaltijden verliezen hun maandbeeld. Met het maandgrid erbij ~18-23 dagen, en de verdeling is later te verschuiven zonder één aanroep aan te raken.
Advies: erbij bouwen — het is de goedkoopste verzekering in dit voorstel. Maar: als je snel wil zien of wielen bevallen, kun je groep 1–3 doen (alles wiel) en pas daarna beslissen. De wrapper houdt die deur open.
3 · Klopt mijn verdeling wiel-versus-kalender?
De tabel in sectie 2c zet 29 plekken op het wiel en 13 op het maandgrid, langs één regel: is deze datum een gegeven dat je weet, of een keuze die je afweegt? Schuif rijen die je anders ziet — vooral vakantieperiode en verhuurboeking zijn twijfelaars (daar wil je misschien juist wél zien welke weken vrij zijn).
Advies: per situatie langs die ene regel, met een wissel-knop in de sheet als vangnet — niet app-breed één vorm. Vakantie en boeking krijgen de wissel-knop standaard in beeld.
4 · Mag de gebruiker zelf wisselen, en onthouden we dat? — nieuw
Ik stel een klein wiel/kalender-icoontje in de sheet voor. Twee subvragen: onthouden we die keuze per soort datum (dus "Sandra kiest bij afspraken altijd het wiel") of globaal (één voorkeur voor de hele app)? En zetten we hem ook in de instellingen, of alleen in de sheet zelf?
Advies: onthouden per soort, alleen in de sheet, geen instelling. Per soort omdat de behoefte per situatie verschilt; geen instelling omdat een voorkeur die je in de sheet zet waar je hem nodig hebt, beter vindbaar is dan een schakelaar drie menu's diep. Als het gezin erom vraagt, is de instelling er later bij te zetten.
5 · Mag "duur" de tweede tijdkiezer vervangen — en hoe grof mag het minutenwiel?
Bij een afspraak, wedstrijd of beweegsessie stel ik voor de eindtijd te zetten via duurchips (15 min / 30 min / 1 uur / 2 uur / hele dag) in plaats van een tweede kiezer. De eindtijd blijft opgeslagen en zichtbaar — duur is alleen invoer. Of wil je dat de eindtijd-kiezer altijd apart bereikbaar blijft?
Daarbij: het minutenwiel op stappen van 15 (4 waarden, één korte veeg) of van 5 (12 waarden, zoals de huidige schijf al afrondt)? Met 15 is 18:30 sneller, maar 07:05 vereist de "elke minuut"-schakelaar.
Advies: duurchips als hoofdweg, eindtijd blijft aantikbaar. Minutenwiel op 15, met de precisieschakelaar eronder. Geen datamodelwijziging, dus geen migratie.
6 · Nachten of dagen? — nieuw, en de belangrijkste
Dit is de enige vraag in dit stuk met een stille databug erachter, en hij bestaat alleen doordat W3 met duur werkt. De app gebruikt nu twee verschillende conventies:
- Een boeking rekent in nachten en eist een eind ná de start: 3 → 10 augustus = 7 nachten, en start = eind is verboden (
add_booking_sheet.dart:98-100). - Een schoolvakantie rekent in dagen t/m en mag op één dag beginnen en eindigen: 1 t/m 16 augustus = 16 dagen (
add_school_holiday_sheet.dart:99laat eind = start toe).
Als de component de verkeerde eenheid gebruikt, schuift alles één dag op — en dat zie je niet, want de datum ziet er plausibel uit. Bij een verhuurboeking raakt dat het rendement per jaar; bij een medicijnkuur de laatste innamedag.
Advies: de eenheid per aanroep vastleggen in de standaardentabel, niet één keuze voor de hele app. Boeking en vakantie = nachten (met minimum 1). Medicijnkuur = dagen t/m (een kuur van één dag mag). En in de sheet altijd de uitkomst in tekst tonen ("7 nachten · t/m ma 10 aug") zodat een verkeerde eenheid direct opvalt in plaats van stil door te lekken. Deze uitspraak heb ik nodig vóór groep 2 begint.
7 · Mag een periode een leeg einde hebben?
Twee bestaande flows hebben dat nodig: co-ouderschap ("vanaf 1 maart, tot nader order") en verzekeringen zonder vaste vervaldatum. Bij W3 kan dat per definitie niet — duur is altijd een getal. Dus die twee flows draaien op de W2-stand, waar de Tot-tab "—" kan tonen met een "geen einddatum"-knop.
Advies: ja, per aanroep instelbaar (eindMagLeegZijn), en die vlag dwingt de W2-stand af. Boekingen en vakanties eisen wél beide; co-ouderschap en verzekeringen niet.
8 · Weeknummers in het maandgrid?
Alleen relevant als je vraag 2 met "ja" beantwoordt. Voor vakantieplanning en schoolvakanties denk je in weeknummers ("week 30"), voor de rest van de app niet. Ze erbij zetten kost een kolom breedte — op een kleine telefoon met grote letters is dat merkbaar.
Advies: weeknummers aan in vakantieplanning en schoolvakanties, uit elders — één parameter. Eerste weekdag komt uit de taalinstelling (maandag bij NL/DE), niet uit een eigen keuze.