Drive (ride-hailing)
Passenger and driver rides with a Mapbox map. Server-side price estimate; real payments need Stripe later.
Drive is the kit's ride-hailing module: users request a ride as a passenger or switch to driver mode to accept nearby requests. The full flow runs on all 3 backends (Firebase, Supabase, REST API) with the same UI.
| Role | What it does |
|---|---|
| Passenger | Picks pickup and drop-off on the map, sees distance, duration, and an estimated fare, tracks the ride in real time |
| Driver | Registers a vehicle, goes online, accepts nearby requests, and drives through completion or cancel |
The map uses Mapbox via flutter_map (HTTP tiles, works on native and web). Route and price are computed on the server (Mapbox Directions + your coefficients), not in the app.
The amount shown on screen is an estimate only. The kit does not charge the passenger or pay the driver. For real payments, wire in the Stripe (web) module or another provider on your backend.
Implementation
Via MCP: ask your assistant "add drive to my project". It runs add_feature for you. See Kasy MCP.
-
Turn on the module (it doesn't ship in Quick mode by default):
kasy add drive # adds it (asks for Mapbox token and price coefficients) kasy remove drive # removes it kasy add --list # shows what's onkasy add drivewritesMAPBOX_ACCESS_TOKENto.env, seedsDRIVE_*coefficients, and on Firebase/Supabase tries to store the Mapbox token as a server secret. -
Create the Mapbox token:
- Create an account at mapbox.com
- Generate a public access token (
pk.…) - Set usage limits and URL/bundle restrictions in the Mapbox console
-
Configure secrets and coefficients:
kasy configure driveOr through the Drive (Ride-hailing) section of
kasy configure:Variable Where it lives Purpose MAPBOX_ACCESS_TOKENApp .envMap tiles in Flutter MAPBOX_ACCESS_TOKENServer secret (Firebase/Supabase) Directions and fare on the backend DRIVE_BASE_FAREfunctions/.envor Supabase secretsBase fare (default 5)DRIVE_PRICE_PER_KMsame Price per km (default 1.5)DRIVE_PRICE_PER_MINsame Price per minute (default 0.25)DRIVE_CURRENCYsame ISO currency code (default USD)On REST API, coefficients live in your server
.env. See the contract indocs/drive.md(shipped with the project) and the API patch README.Via MCP: paste the Mapbox token and the coefficients in chat and ask "set up Drive" — the assistant writes them with
configure_project_keys(Firebase/Supabase). See Kasy MCP. -
Deploy the backend:
Backend What to run Firebase kasy deploy(publishesdriveFunctions+ push triggers)Supabase supabase db push(drivemigration) +supabase functions deploy request-rideREST API Implement POST /drive/rides,POST /drive/rides/{id}/accept, etc. (see patch README)Via MCP (Firebase): "deploy the backend" (
deploy_backend). -
Confirm the native permissions. The app requests location at runtime (pickup, driver online). On iOS,
Info.plistalready includes usage strings when the module is on. Validate on a real device before release. -
Test the flow. Two ways:
-
Alone, without real GPS: outside production, turn on "Automated test mode" in Drive's side menu — it simulates the driver walking a fixed route, so a single account can test passenger and driver by switching roles. See Automated test mode below.
-
With two devices or accounts (or emulator + physical device), simulating passenger and driver at the same time:
- Open Settings
- Tap the Drive tile (car icon)
- Choose Request a ride (passenger) or Driver mode
-
-
For real payments (outside the Drive module's scope), add Stripe (web) or another gateway and tie billing to the ride
completeevent on your backend.
Information
How the flow works
On first open (Settings → Drive), the app asks once whether the user wants to ride as a passenger or drive as a driver, and saves the choice. After that, Drive opens straight into the saved role's screen; switching roles is done from Drive's side menu.
The active ride is recognized from any browser or device signed into the same account — switching devices mid-ride never loses tracking or lets you accidentally request a duplicate ride.
Passenger:
- Searches the destination by text or adjusts the pickup/drop-off pins on the map
- Sees the route, distance, time, and the estimated price (calculated on the server)
- Requests the ride and tracks it live: driver position, vehicle and plate, live ETA
- If no driver accepts within about a minute, the screen shows "No drivers available" with a live countdown and a try again option — up to 2 times. After that, only canceling and retrying later is left
- Can cancel only while the ride is
requestedoraccepted. Once on board (in_progress), cancellation is no longer available in the app
Driver:
- Must register the vehicle (onboarding) before going online. The switch opens the form automatically if it is missing
- Online starts continuous GPS: the position is published to the backend, and that is what the passenger sees on the map
- Nearby requests arrive as a full-screen overlay with sound and haptics, with Accept and Decline (declining only hides it for that driver). While on a ride, no new requests come in
- Accepting opens navigation to the pickup. The "Picked up passenger" button only unlocks when GPS is within 100 m of the pickup point
- Then navigation switches to the destination, and "Complete ride" only unlocks within 100 m of the drop-off. Outside the radius the button stays disabled with a hint on screen
Transition rules (validated on the server)
The unlocked button on screen is not the only guard. The backend validates every transition, with the same rules on all 3 backends:
| Action | Only accepted when |
|---|---|
| Accept | Ride still requested (if two drivers accept, only the first wins) |
| Start (picked up) | Ride accepted and the caller is the assigned driver |
| Complete | Ride in_progress and the caller is the assigned driver |
| Cancel | Ride not yet completed or canceled; only the ride's passenger or driver |
Automated test mode (dev)
Outside production (any build with ENV other than prod), Drive's side menu has an "Automated test mode" item. Turned on, it lets you test the whole flow — request, accept, decline, pick up, complete — alone, with a single account, without needing two devices or real GPS:
- Location becomes a fixed simulated route (a real stretch, routed through Mapbox); the driver "walks" that route alone, at an accelerated pace (the whole ride takes seconds, not minutes).
- The back button during tracking does not cancel the ride — it switches your own account to the other role (turns you into the driver so you can accept/decline that same request, or back into the passenger), since it's the same person testing both sides.
- Declining does not cancel the ride: it stays
requestedand visible to other drivers, same as in production.
Only in this mode: a hot restart (uppercase R in flutter run/kasy run, or reloading the tab from scratch) resets the simulated vehicle's position — it walks the route again from the start, even if it had already arrived. This is a limitation of the simulator (its position only lives in the app's memory, never saved anywhere), not a bug in the real flow. Production has no equivalent: the phone's GPS is an external source that keeps whatever position it was at — no restart ever "forgets" the driver's position.
This toggle never shows up in a production build (ENV=prod) — no end user ever sees it.
Where to look in the Firebase Console
Drive does not store driver or ride data under Authentication. That tab only shows the account and the User UID. Data lives in Firestore on the same project the app uses.
| What you need | Where | Note |
|---|---|---|
| Test account | Authentication → Users | Copy the User UID |
| Driver registration | Firestore → drivers/{uid} | Same {uid} as Authentication |
| Rides | Firestore → rides/ | Created by the requestRide callable |
| Device push token | Firestore → users/{uid}/devices/ | Not the driver's name |
Automated test mode: only GPS is simulated. Request, accept, and status changes always hit the backend. If rides/ is empty, check function deploy, Mapbox secret, and that drive_api.dart matches functionsRegion in kit_setup.json.
On Supabase, use the drive schema (drivers, rides) in Table Editor.
Testing without the full setup
With withDrive on, the Drive screens open even without a Mapbox token (the map stays blank). For the full flow (request, accept, start, complete), you need the token in .env, the secrets configured, and the backend deployed.
| Situation | What works |
|---|---|
| Debug without Mapbox token | Screens open; map without tiles |
| Debug with token + deployed backend | Full passenger + driver flow |
| Release without token | Broken map; do not ship like this |
What's in the UI
- Entry: Drive intro with role choice on first open (Settings → Drive); the role is saved
- Passenger: full-screen map home, destination search, pin adjustment, estimate, and live tracking
- Driver: vehicle onboarding, online/offline switch, nearby-request overlay, navigation with progressive route and ETA
- Push: notifications when a ride is created or changes status (Firebase triggers / Supabase equivalent)
There is no separate role system: a user with a drivers/{uid} profile is a driver; any signed-in user can request a ride.
Last updated on 08/02/2026

