Fix — Creation bottom sheet, dark-mode safe + differenziazione Crew/Evento
Commit:
fix(ui): dark-mode safe creation sheet with distinct crew/event styling· Branch:edit/mobile-audit-ux-polish· Audit: 2026-04-20 UX polish (finding H2)
Cosa non andava
Il CreationBottomSheet è il gate principale verso la differenziazione del prodotto: è da qui che l’utente sceglie fra crew (il nostro differenziatore) e evento (il flusso classico). La qualità di questo momento definisce la percezione del valore unico dell’app.
Cosa trovavamo invece:
color: Colors.whitehardcoded nelBoxDecoration→ in dark mode un rettangolo bianco emerge dal fondo nero, rompendo il flusso visivo.Colors.grey[300],Colors.grey[400],Colors.grey[600],Colors.blacksparsi per tutto il file → zero adesione al theme.- Crew e Evento entrambi coral → visivamente indistinguibili al primo sguardo.
- Espressione assurda:
color: color == Colors.grey[400] ? Colors.grey : Colors.black(la condizione è sempre falsa perchécolorèelectricCoral) → rotto in dark mode, codice che mente sulla sua intenzione. - Naming asimmetrico: “Crew per stasera” (imperativo breve) vs “Evento Community” (noun phrase) → due registri, stessa schermata.
Cosa abbiamo cambiato
Colori — dal hardcoded al tema
| Elemento | Prima | Dopo |
|---|---|---|
| Sheet background | Colors.white | theme.bottomSheetTheme.modalBackgroundColor ?? colors.surface |
| Radius top | Radius.circular(32) literal | Radius.circular(AppTheme.radius28) |
| Drag handle | Colors.grey[300] | colors.onSurface @ 0.15 alpha |
| Title style | headlineSmall + override peso | theme.textTheme.headlineLarge (canonical) |
| Option title color | Colors.black via expr. bugata | colors.onSurface |
| Subtitle color | Colors.grey[600] | colors.onSurface @ 0.60 alpha |
| Chevron color | Colors.grey[400] | colors.onSurface @ 0.40 alpha |
| Card radius | circular(20) literal | AppTheme.radius20 (token) |
In dark mode ora tutto il sheet appare su surfaceDark e ogni testo pesca dai contrasti del ColorScheme.
Differenziazione Crew vs Evento
| Crew | Evento | |
|---|---|---|
| Accent color | AppTheme.electricCoral | AppTheme.electricViolet |
| Priorità semantica | Differenziatore brand (Pilastro 2) | Flusso community classico |
| Icona | Icons.groups_rounded | Icons.event_rounded |
Il coral segnala la priorità (Flow si vende sulla crew); il violet segnala la complementarità (l’evento è l’altro lato della stessa piattaforma). Visualmente i due riquadri ora pescano da colori brand diversi — l’utente distingue in un colpo d’occhio.
Naming — registri simmetrici
| Prima | Dopo | |
|---|---|---|
| Crew | Crew per stasera | Crea una crew |
| Crew subtitle | Trova subito compagni per il tuo giro | Trova subito compagni per stasera |
| Evento | Evento Community | Crea un evento |
| Evento subtitle | Organizza una festa o un ritrovo | Organizza una festa o un ritrovo (invariato) |
Verbo all’imperativo esplicito in entrambi i titoli. L’utente legge due azioni parallele: Crea una crew / Crea un evento. Zero ambiguità su “cos’è questo pulsante”.
Non toccato (deferito)
- i18n: le label sono ancora hardcoded in italiano. Il piano l10n gestirà le chiavi
creation.title,creation.crew.titleetc. in un commit a sé (coerente con il pattern di migrazione i18n già in corso). - A11y focus order: due
GestureDetectorconHitTestBehavior.opaquesenzaSemantics→ un lettore di schermo legge solo il testo dentro, non annuncia “bottone, premere per…“. Fix nel prossimo a11y pass.
Verifica
flutter analyze lib/shared/widgets/creation_bottom_sheet.dart
# → No issues foundCheck visivo manuale:
- Aprire il ”+” in light mode → sheet con background chiaro, Crew in coral e Evento in violet, titoli in nero
- Aprire il ”+” in dark mode → sheet su nero, NO rettangoli bianchi, Crew e Evento ancora distinguibili
- Tap su Crew → apre
CreateCrewScreencome sheet nested con stessa radius top - Tap su Evento → naviga a
/home/create-event
Lessons
- Un solo colore accent = zero gerarchia. Quando tutto è “primary brand”, niente è primary. Il coral deve comunicare questo è la priorità; il violet deve comunicare questo è il complemento. Perdere questa distinzione sulla schermata più strategica del prodotto è una ferita silenziosa.
- Naming simmetrico sopra ogni inventiva. La tentazione di essere clever (“Crew per stasera” suona caldo, evocativo) va bilanciata col bisogno di parallel structure. Se due opzioni sono siblings semantici, i loro label devono essere siblings grammaticali.
- Condizioni sempre-false sono bug mascherati. Il
color == Colors.grey[400] ? ... : ...è l’antipattern classico del copy-paste-da-stackoverflow che sopravvive perché “tanto funziona”. Non funziona: rompe il dark mode e mente sull’intenzione del codice. Vanno cacciati con grep periodici suColors\.[a-z]+nel layer UI.