Indie Log #4 — I rewrote the entire UI and I don't regret it (yet)

Alright so it's been a minute since the last log. A LOT happened. Flowmodoro 2.0 is in closed beta with my testers, the public release is coming in the next few days, the whole app looks different, runs on tablets, doesn't crash on random Android devices anymore (we hope), and I started working on web support. Let's get into it.
Web support (work in progress)
OK let me rant for a second. Shipping a mobile app is pain. Pure, uncut pain. You've got Apple's review process that rejects you because your screenshot has the wrong corner radius. You've got Google Play asking you to fill out 47 forms about data safety. Provisioning profiles, signing certificates, App Store Connect, Google Play Console, TestFlight, internal tracks, release tracks — bro I just want people to use my app, not file taxes in two different countries.
And right now I'm living this nightmare in real time. The app is done. 2.0 is built, tested, running in closed beta. My testers are using it. But can I ship it? No. Because I'm drowning in App Store Connect legal requirements, payment setup, screenshot specs for 47 different device sizes, marketing copy, Google Play data safety declarations... I've spent more time on store compliance than I did on some actual features. The code has been ready for days. The paperwork is what's holding me back.
So yeah, I'm bringing Flowmodoro to the web. Partly because it makes sense for the product. But mostly because I am SO done with the mobile shipping experience. Honestly? My next product will be web-first. No app stores, no review queues, no certificates. Just deploy and it's live. What a concept.
Anyway. Step one was a commit called "Remove dart:io and platform guards for web compilation." Sounds innocent right? Turns out half the codebase casually imports dart:io like it's free. File paths, platform checks, notification APIs — everything just assumes you're on a phone. Spoiler: the browser is not a phone.
I built this thing called ResponsiveShell that figures out if you're on a phone, tablet, or desktop, and gives you the right layout. Bottom tab bar on phones, floating sidebar on tablets, full sidebar with a mini timer on desktop. The mini timer sits in the sidebar so you can see your session running without switching tabs. That one was actually fun to build.
Notifications were... less fun. Web doesn't have local notifications the way mobile does, so I had to split the notification service into platform-specific implementations. Mobile keeps its existing system, web gets nothing for now. But the abstraction is clean enough that plugging in the Web Notifications API later should be straightforward.
The app compiles and runs on web but there's still a bunch of web-specific stuff to handle before I ship it — hosting, data management, all that good stuff. Soon though.
The glass UI makeover (AKA "I'll just tweak some colors real quick")
You know how it goes. "Let me just adjust this one color." Three days later you've rewritten the entire visual layer of the app.
I did it in phases because touching every screen at once is how you end up mass deleting your lib/ folder at 3am:
Phase 1 — Centralized theme. FlowColors, FlowRadius, FlowSpacing. No more random hex codes sprinkled across 40 files like confetti. Brand purple #5F1DF9 and a deep dark base #0D0B1A. That's the palette. Done. Move on.
Phase 2 — Navigation. Killed the default Material bottom bar (sorry Google) and replaced it with a floating glass tab bar. BackdropFilter for the frosted blur, wrapped in a GlassContainer widget I made reusable so I wouldn't have to copy-paste blur logic into 15 files. On tablets it becomes a glass sidebar. Same component, different layout. This is the one time abstraction actually paid off immediately.
Phase 3 — Timer screen. The old circular progress indicator looked like a loading spinner from 2019. Replaced it with a thin glow ring and a glass start/stop button. Added a mesh gradient background that subtly animates — gives the glass something to actually blur against. The gradient intensity ramps up when you're in a session. Nobody will consciously notice this. I spent four hours on it anyway.
Phases 4-9 — Went full glassmorphism on literally every other screen. Stats, history, achievements, settings, onboarding, auth, paywall, tags. Purple-tinted glass in light mode, white-frosted glass in dark mode. By phase 7 I was mass replacing containers in my sleep.
Phase 10 — The "stare at every pixel" pass. Button glows looking weird in light mode? Gone. Content scrolling under the nav bar? Fixed. Achievement grid not responsive? Fixed. Equal-height stat cards? Fixed. The kind of stuff nobody notices unless it's broken, and then it's ALL they notice.
Then iPad happened. Portrait mode was trying to render the desktop sidebar in a narrow screen. The mini timer didn't work in a horizontal bottom bar. The mesh gradient was glitching on certain iPad models. I spent days fixing all of this. iPads, man.
Sentry and PostHog — or, "I should have done this 2 years ago"
I was flying completely blind. No crash reports. No analytics. Just vibes and "works on my machine."
Added Sentry for crash tracking. Within the FIRST DAY it flagged three crashes I had no idea about. One was a crash at launch on certain Android devices. A crash. At launch. And I had zero clue. Fixed all three in a few hours. Two years without error tracking. What was I thinking.
PostHog for analytics. Now I can actually see which features people use, where they drop off, how long sessions last. The setup was fine but Apple's privacy plist requirements... listen, if you enjoy filling out forms about why your app needs to know what time it is, iOS development is for you.
Tablet support
Flowmodoro runs on iPads and Android tablets now. This sounds like a one-liner checkbox feature. It was not.
The responsive shell handles the layout switching, but there were edge cases everywhere: stats period selector hiding behind the app bar, settings spacing breaking at certain widths, timer not centering in landscape. Death by a thousand papercuts.
The iPad portrait fix was the worst. I had to detect portrait mode and fall back to the phone layout instead of cramming the desktop sidebar into a portrait screen. Because apparently a 768px wide iPad in portrait mode is NOT the same as a 768px wide laptop screen. Who knew. (Everyone knew. I didn't.)
The small stuff
In-app feedback — Added a FeatureBase WebView in settings. Users can submit bugs and feature requests without leaving the app. Took like an hour to implement. Probably the best ROI feature I've shipped.
Font bundling — Was loading Google Fonts at runtime. Works great until your user has bad wifi and the app looks like a ransom note in system fallback fonts. Fonts now ship with the binary. Heavier app, no more ugly surprises.
Auth fix — Flowmodoro lets you use the app without signing up (anonymous-first). But when anonymous users finally created an account, the sign-up flow was making a NEW user instead of linking to the existing one. Translation: all your data? Gone. Poof. Fixed that real quick and wrote an integration test against real Supabase so it never happens again.
Tag validation — You could create duplicate tag names. That broke stats. Now you can't. Also added a delete confirmation because deleting tags should require at least one extra click of intentionality.
Version 2.0
After all of this — full UI rewrite, crash tracking, analytics, tablet support, web groundwork, and roughly 80 bug fixes — I bumped to 2.0.0.
Then immediately pushed 2.0.1, 2.0.2, 2.0.3, and 2.0.4 to my beta testers because nothing says "stable release" like four hotfixes in 24 hours.
574 commits total. The app is in closed beta right now, and the public release is dropping in the next few days. Just need to finish wrestling with Apple and Google's store requirements. You know, the fun part where you spend three days uploading screenshots and filling out legal forms instead of writing code.
The mobile app is polished, stable, and ready. Web is on its way. Now the hard part: getting people to actually use it. Building the thing was the easy part apparently.
Follow the chaos @the_nomad_code on X. Building in public, one commit (and four hotfixes) at a time.



