DEV Community

Abidi Ahmed
Abidi Ahmed

Posted on

I open sourced a Flutter recreation of Uber's rider app UI

Home

Just wrapped up a side project I've been chipping away at: uber-ui-practice, a Flutter recreation of Uber's rider app interface. It covers 8 screens, all built from scratch with mock data only.

Quick disclaimer: personal learning project, not affiliated with Uber in any way. No backend, no real APIs, all design rights belong to them.

What's inside

Splash, welcome, login, home, services, activity, an activity filter sheet, and the account tab. Here are four of them:

Login Home Account

The rest are in the repo README.

Structure: feature-first

I went with features instead of layers this time, and I'm never going back to layer-first for UI projects:

lib/
├── core/
│   ├── constants/            # auth strings (app is in French)
│   ├── theme/                # colors, type scale
│   └── widgets/              # buttons, inputs, dividers
├── features/
│   ├── auth/
│   │   ├── controller/
│   │   ├── data/
│   │   ├── models/
│   │   ├── pages/
│   │   └── widgets/
│   └── tabs/
└── main.dart
Enter fullscreen mode Exit fullscreen mode

When a screen looks wrong, I know exactly which folder owns the problem. No jumping between a global widgets/ dump and three screens folders trying to trace who renders what.

Details that took real effort

Onboarding as a state machine. Splash, welcome and login are states inside a single AuthFlow widget rather than separate routes. After login, one pushAndRemoveUntil replaces the stack with the home shell, so back navigation can't leak into the auth flow:

void _goHome() {
  Navigator.of(context).pushAndRemoveUntil(
    MaterialPageRoute(builder: (_) => HomePage(onLogout: _logout)),
    (route) => route.isFirst,
  );
}
Enter fullscreen mode Exit fullscreen mode

The floating pill nav. This little bar ate more time than any full screen. Tabs live in a PageView locked with NeverScrollableScrollPhysics, so only the pill switches pages. The pill waits ~250ms before sliding so it lands after the page transition instead of racing it, and the whole bar auto-hides on scroll down through UserScrollNotification.

Flag emojis with no assets. The country picker generates every flag at runtime from ISO codes:

final base = 0x1F1E6; // regional indicator A
String get flagEmoji => String.fromCharCodes(
    isoCode.runes.map((r) => base + (r - 65)));
Enter fullscreen mode Exit fullscreen mode

One theme file to rule the surfaces. Every color token sits in core/theme. When your goal is matching someone else's exact near-black, centralizing tokens is what keeps you sane.

The part nobody warns you about

Cloning a production design is stricter than designing your own. With your own app, close enough is fine. Here, every spacing value had a correct answer, and mine was often not it. Annoying sometimes, but honestly great training for design attention.

Links

Repo, with tests passing and setup docs:

👉 github.com/abidiahmedcom/uber-ui-practice

If this kind of UI study interests you, I applied the same approach to Glovo's delivery app as well: glovo-ui-practice.

Feedback welcome, and if you notice anywhere my version drifts from the original, issues are open.


Enter fullscreen mode Exit fullscreen mode

Top comments (0)