What inspired us

If you’ve ever used the minibus taxi system in Lesotho, or anywhere else in Southern Africa, you know that most routes live in people’s heads, not on a map.

There’s no timetable to check or app that tells you exactly where to go. Instead, you ask someone: Which rank should I use? Which taxi should I take? Is this the short route or the long one?

That system works when you already know your way around. But if you’re visiting, have recently moved, or are simply travelling somewhere new, finding the right taxi can be confusing.

TaxiFlex grew out of that experience. We wanted to take something people already do, asking someone where to go, and make it searchable. That’s why the app is voice-first. You say your destination just as you would to someone at the taxi rank, and TaxiFlex helps you find the right rank, pickup point, and route.

What we learned

  • The hardest part wasn’t the interface. It was making sense of the real-world taxi system. Taxi ranks don’t fit neatly into a spreadsheet. One base might have several sub-ranks, similar names, or different spellings. We created a clear “Base (Sub-rank)” structure and built parsing logic to organise that information.

  • Voice is about understanding, not just transcription. Turning speech into text was only the first step. The real challenge was understanding what someone meant when they said something like “town” or used a local nickname, then matching that phrase to the correct rank and both sides of the journey.

  • Trust and moderation need to be part of the product from the beginning. Once users can contribute routes, you need to make sure their submissions don’t accidentally overwrite verified information. We keep community submissions separate and only allow administrators to update the official rank data.

  • Firestore works best when the data is carefully structured. Using GeoPoint for coordinates, keeping queries focused, and separating the client from the database through service adapters saved us a lot of trouble. It also helped us catch a surprisingly easy-to-miss bug where a latitude was being stored with the wrong sign.

  • Codex is surprisingly powerful. As this was my first time making or building an app, I didn't expect how helpful AI tools would be. I've tried building apps in the past, but you would get stuck somewhere, and finding help or finding answers was always a problem. Now, with these sorts of AI tools, you could just ask it what is causing the error or show it the error and can I help you sort of solve it? That was a surprise to me. I wasn't expecting things to go that easily to the point where you could say that, I guess, 98% of this project is AI-built, with me just writing prompts and essentially directing the behavior of the app rather than actual code.

How we built it

TaxiFlex is a React Native app built with Expo SDK 57, Expo Router, and TypeScript in strict mode.

Our stack includes:

  • Firebase Authentication for phone number sign-in, along with an international country picker and a simple name-entry step.

  • Cloud Firestore as the main source of truth for ranks, routes, and user-submitted route information. Screens never communicate with Firestore directly. Instead, all database access goes through service adapters in src/services/.

  • A voice-guided journey flow that uses expo-audio to capture speech, interpret the destination, suggest possible ranks, and match the request against route data stored in Firestore.

  • react-native-maps for the live journey map, with Reanimated and Gesture Handler powering the resizable panels and trip timeline.

  • A token-based design system using tokens.json and src/theme/, which keeps colours, spacing, and typography consistent throughout the app.

We built TaxiFlex one feature at a time. We started with authentication, followed by the home map and panels, journey search, voice input, and finally community route submissions with a review step.

Challenges we faced

  • The voice and AI layer is still a prototype. Speech interpretation currently runs through a development service adapter, and some route and fare information still comes from fixture data rather than live operational data. We designed the architecture so that a production service can be added behind the same interface later. We also wanted to be honest about what works today and what still needs to be built.

  • Real-world rank data is messy. Duplicate names, inconsistent spellings, and different ways of referring to the same place remain an open challenge. We solved the structural parsing needed for the prototype, but full data cleanup and deduplication will require more work.

  • Community contributions need strong safeguards. A user-submitted alias should never be able to change an official rank record. Getting the Firestore security rules right was essential because the rules, not the client app, are the real authorization boundary.

  • We found a subtle coordinate bug. Some rank latitudes were being stored with the wrong sign. We corrected them at the service boundary and added safeguards so the map wouldn’t accidentally send someone to the wrong hemisphere.

  • Time was always against us. Like most hackathons, the final stretch came down to making honest choices about scope. We focused on delivering the smallest version that works from beginning to end, instead of building a polished demo that only works under perfect conditions.

Built With

  • android
  • cloud-firestore
  • expo-audio
  • expo-haptics
  • expo-location
  • expo-notifications
  • expo-router
  • expo.io
  • firebase
  • firebase-authentication
  • ios
  • javascript
  • lucide-react-native
  • openai
  • react-native
  • react-native-firebase
  • react-native-gesture-handler
  • react-native-maps
  • react-native-reanimated
  • react-native-svg
  • typescript
Share this project:

Updates