Summary
A sweep for two related things: data written into widgets rather than read from the database, and controls that look pressable but have no effect. Both lie to the user in the same way — they promise something the app cannot deliver.
Invented Data
The city picker (fixed)
feed_location_sheet.dart listed four city names hardcoded in the widget —
Reggio Emilia, Modena, Bologna, Milano — three of them under a ⭐ and the word
“saved”. Nobody had saved them.
Warning
The sheet claimed the user had made a choice they never made. The “add or edit saved places” tile below simply closed the sheet.
Meanwhile city-service already held the real list: the same cities the map uses to work out which one you are looking at, with bounding boxes, aliases and provinces. The picker was the last place in the app still guessing.
It now reads that list, sorted, searchable by name, province or alias — typing “Reggio” finds “Reggio nell’Emilia”, which is what the alias column is for. GPS stays pinned above the list: it is not one of the cities, it is the question “where am I” answered by the phone.
Used from two screens (feed_screen, event_discovery_screen), both unchanged
— the widget’s interface did not move.
Controls That Did Nothing
| Where | What it was | Done |
|---|---|---|
public_profile_screen | a second share button with onPressed: () {} | removed — share lives in the app bar, same as the venue screen |
ticket_wallet_screen | ”leave a review” on a past ticket → _promptReview was an empty body with a TODO | wired to /home/create-review, which has existed all along and takes exactly the two values the ticket already carries |
ticket_wallet_screen | export icon, permanently onPressed: null behind a TODO | removed |
feed_location_sheet | ”add or edit saved places” → popped the sheet | removed with the rewrite |
Note
A control that can never be pressed is not a promise of a feature. It is a dead pixel with an affordance.
The public profile had two share buttons: one was replaced with a working
ShareButton when feature-chat-sharing landed, and the second went
unnoticed. Worth remembering as a pattern — fixing one instance of something
is not the same as fixing the thing.
Deliberately Left Alone
Three other onPressed: null are legitimate disabled states, not dead
controls: the level-locked CTA on an event detail, and the join button on a
crew that is full (in both the bento card and the detail sheet). They say “not
now” honestly and are styled as disabled.
The Fallback Lists (resolved)
Three places kept a hardcoded list “for when the database returns nothing”. Checking the database changed the reading of all three.
The search grid was never a fallback
Four categories — Clubbing, Live, Chill, Culture — sat behind
_eventTypes.isNotEmpty ? … : fallback, which reads like a safety net.
Warning
event_typeshas zero rows and always has. Those four were not a fallback: they were what every user saw, every time, and tapping one searched for a category no event carries.
The real taxonomy was already loaded and cached two feet away in category-catalog — 34 rows, used by the event list and the filters. The grid now takes its top-level categories with their own icons.
The two vibe lists were genuinely defensive
There are 33 real VIBE tags in tags, so an empty result there means the
network failed rather than the data being missing. But eight invented genres
would send the user off to pick something the rest of the app has never heard
of. Both now report the failure and let the user carry on with the rest of the
form.
EmptyStateWidget.inline
A compact notice for a gap inside a screen rather than a whole empty page.
The full empty state — big icon, headline, action — is right when a page has nothing on it, and wrong inside a form step or a grid slot, where it shoves the actual controls off the bottom. This is what replaced the three invented lists: when a list comes back empty, the honest answer is to say so in the space that list would have filled.
Route in Error Reports
app_issues had collected 15 identical “RenderFlex overflowed by 38
pixels” reports with no way to tell which screen produced them. Flutter
attaches no stack trace to layout errors, so the route is the only clue there
will ever be.
IssueReportService now records it in device_info.route, best-effort and
silent — a reporter that throws while reporting is worse than one that reports
a little less.
Still Open
AppConstants.useMock* flags are all false and nothing reads them any
more. Harmless, but they are four constants describing a mode that no longer
exists.
Related
feature-chat-sharing — where the working share button came from localization — keys added for the city search