All work

Driving school operations

Qeyadah

Lead Flutter developer · freelance
2026
Qeyadah preview

2 roles

student and instructor experiences

~31

unit and bloc tests

AR / EN

bilingual from the first screen

CI

format, analyze, test, build on every push

The brief

A driving school running on phone calls and cash

Lessons were booked by phone, paid in cash on the day, and tracked in nobody’s system. Two students could turn up for the same hour, and a slot could only be held once someone at the school confirmed it by hand.

Qeyadah gives students one place to book, pay a deposit, practise the theory exam, and request their certificate. Instructors get their own app for the week ahead, leave, attendance, and earnings. Both run in Arabic and English from the same codebase.

I was the only Flutter developer, building every feature module on top of the school’s shared platform layer. Each feature is a Cubit over a domain layer that returns typed Either<Failure, T> results, so a failed payment and a dropped connection reach the UI the same way: a clear, localized message with a retry.

Qeyadah student home with the next confirmed lesson and quick actions
Student home: the next lesson and every action one tap away.
Qeyadah slot picker showing an instructor's available times for the week
Only genuinely free slots are offered.

The hard part

A deposit that releases the slot on its own

A student holds a slot by sending half the lesson price through Sham Cash and entering the transaction number in the app. The school verifies it, and the booking is confirmed.

If the money never arrives, the hold has to expire without anyone stepping in — otherwise one unpaid booking can block an instructor’s calendar indefinitely. The deposit screen makes the deadline visible with a live countdown, and the amount calculator behind it is covered by unit tests, because a wrong number here is a real money problem.

Qeyadah Sham Cash deposit screen with amount, recipient, and a countdown
Exact amount, recipient, and a live countdown.
Qeyadah instructor day view with sessions and attendance status
The instructor side of the same booking.

What I’d change next

When two students race for the same slot, the backend answers with an HTTP 409 and a free-text message, and the app reads that text to choose the Arabic or English explanation. It works, but it breaks silently the day someone rewords the message.

The fix is a stable conflict_code from the API, mapped to copy on the client. Same experience for the student, with no dependency on wording neither side promised to keep.

Stack

FlutterDartCubit / BLoCfpdart Eithergo_routerget_it + injectableFirebase Cloud Messagingbloc_test / mocktailGitHub Actions CI

Booking and payment logic are unit-tested; the integration test suite is scaffolded but not yet complete.