MotoGate
Event access control, in the hands of the people on the gate.
iOS · Android

Live event list 
Registration form 
Entry pass 
Rider applications 
Guest list 
Access roles 
Digital licence
Access at a motorsport event is not one list. Riders with valid licences, their companions, officials, pit crew and spectators are each allowed into different areas — verified by someone at a gate with a phone and no time to argue.
Product definition, interface design, application architecture, the Flutter app for both stores, the Supabase backend and data model, the access and licence rules, notifications — and continuous releases since.
- iOS
- Android
- Application
- Flutter
- Dart
- BLoC
- Clean Architecture
- Backend
- Supabase
- PostgreSQL
- Row Level Security
- Postgres RPC
- Infrastructure
- Firebase Cloud Messaging
- Supabase Storage
- Product strategy
- UI/UX design
- Technical architecture
- Mobile development
- Backend development
- Database design
- Integrations
- Testing & QA
- Deployment
- Maintenance
What it does
Events
Capacity, pricing, and admissions counting up live as people come through.
QR verification
One scan confirms identity, entitlement and area. The pass brightens the screen so it reads in daylight.
Digital licences
Issued, renewed, revoked and reinstated in the app, with the full history and race-number checks.
Guest registration
Guests register themselves, with companions attached to the participant who brought them.
Access roles
Nine distinct permissions, granted person by person.
Participation & results
Entries, participation fees and vehicle verification, alongside championships and results.
One entry, traced
A rider walks up to the gate with a pass on their phone. This is everything that happens between the camera seeing it and the barrier opening — or not.
Read from scan_ticket_page.dart, participation_repository.dart and the Supabase function list. Every name below appears in the source exactly as written.
- 01
The pass sits on the rider’s phone
The code carries three fields and nothing else: which event, which ticket, whose phone number. The screen goes to full brightness so it still scans in direct sun, and the operating system is told to refuse screenshots of it.
- qr_flutter
- screen_protector
- FLAG_SECURE
- 02
A marshal scans it
The camera reads the code and the phone buzzes the moment it has something — before any verdict. That first buzz tells the marshal to hold still rather than keep re-aiming, which is most of what makes a gate queue move.
- mobile_scanner
- 03
Two checks before the network is touched
A code missing any of its three fields is refused on the spot, and so is a perfectly valid pass for a different event. Neither needs a round trip, so the common mistakes are answered instantly.
- eventId
- ticketCode
- phoneNumber
What it says when it refuses
“Invalid ticket”, or “Ticket does not belong to this event”. Both are refusals, and both are written to the log.
- 04
The event is read back from the server
The decision is never made from whatever the scanning phone happens to be holding. The event is fetched with its invites, their companions, its teams, their members and its scan log — all under row-level security, so a marshal’s own permissions decide what comes back.
- events
- event_invites
- event_invite_companions
- event_teams
- event_team_members
- event_scan_logs
What it says when it refuses
After twelve seconds, “Verification timeout”. It fails closed: a slow network never becomes an open gate.
- 05
The holder is resolved
The ticket code and phone number are matched against the invites, the companions attached to them, and the team members. If that number belongs to a registered account, the name on file replaces the one typed onto the invite — so the marshal reads the person’s real name.
- profiles
- 06
The ticket’s own history is checked
Every previous scan of this code is examined. One earlier success, or one earlier supervisor override, and the pass is spent. This is what stops a screenshot of a valid QR walking three friends in.
- event_scan_logs
What it says when it refuses
“Ticket already used” — shown with who scanned it and when, so the marshal can settle the argument on the spot.
- 07
The licence rule is asked of the database
For a competitor, the licence has to be valid on the day of the event. That rule is a stored procedure, not app code: it cannot be argued with at the gate, and it cannot be bypassed by an old build still on someone’s phone.
- participation_entry_check
What it says when it refuses
“Licence not valid on the event date”, with the reason the database gave.
- 08
The verdict, and the record
Granted or denied, with any companions the holder is entitled to bring listed underneath. A light haptic for allow, a heavy one for deny, so it can be felt without looking. Then every attempt is written down — refusals included, and a supervisor override recorded as its own status rather than quietly as a success.
- record_scan_log
- success
- failed
- overriden
Engineering detail
Taken from the project source, not from marketing material.
- Architecture
- Clean Architecture with BLoC state management, feature-first structure, repository pattern
- Backend
- Supabase — PostgreSQL with row-level security policies and server-side RPCs for licence and admission rules
- Data model
- 21 tracked migrations covering licence lifecycle, revocation history, renewals, manual admission and realtime
- Device capabilities
- Camera scanning, QR generation, brightness control, screenshot protection, PDF generation, contact import
- Notifications
- Firebase Cloud Messaging for push, with local notifications on device
- Release
- Shipping on iOS and Android; version 1.5.1, build 59 at the time of writing
Event access control, in the hands of the people on the gate.
