Water_Tracking_App: Aquafit: The Single-File Flutter App That Changes With the Clock
A lean hydration tracker that uses time-aware theming, lifted state, and a monolithic Dart core to show how far an MVP can go before it needs a database.
- Aquafit treats the interface itself as part of the product, using time-aware theming to make hydration feel synchronized with the day.
- The app’s single-file Dart structure is a prototype strategy, not an accident, because it minimizes setup and keeps the full flow easy to reason about.
- Lifted state does the heavy lifting here, with one central widget coordinating login, onboarding, entries, reminders, and theme changes.
- The biggest trade-off is persistence, since the in-memory model proves the experience but does not yet survive a restart.
A hydration app that wakes up and winds down on its own
Aquafit’s sharpest idea is simple: the app changes its visual mode based on the hour. In the research snapshot, light mode runs from 07:00 to 18:59, and dark mode takes over at night. That makes the UI feel less like chrome and more like part of the wellness ritual.
That is a strong move for a hydration tracker. It turns a basic utility into something that reacts to context, which is exactly the kind of polish that makes a prototype feel intentional instead of unfinished.
Why a single Dart file can still be a serious MVP
Most of Aquafit lives in main.dart. That file holds the app shell, onboarding, login gate, hydration entries, reminders, and the theme logic. In a larger product, that would be a code smell. In an MVP, it is often the fastest way to keep the whole experience visible and editable.
| Design choice | Aquafit’s approach | Trade-off | Why it makes sense here |
|---|---|---|---|
| State management | Lifted state through a central widget | More props and callbacks | Keeps the flow explicit in a prototype |
| File structure | Most logic in one Dart file | Harder to scale cleanly | Faster to build, easier to demo |
| Persistence | In-memory lists | Data disappears on restart | Good enough for proving the interaction model |
| Theme | Time-based light and dark modes | Less user control than manual toggles | Matches the wellness angle and adds personality |
The app’s real control center is lifted state
The research points to MainEntry as the hub. It holds username, login state, hydration entries, reminders, and the screen switcher, then passes callbacks downward instead of outsourcing everything to a heavier state library. That is classic Flutter pragmatism: one source of truth, many child widgets.
class MainEntry extends StatefulWidget {
const MainEntry({super.key});
@override
State<MainEntry> createState() => _MainEntryState();
}
class _MainEntryState extends State<MainEntry> {
String? _username;
bool _loggedIn = false;
final List<WaterEntry> _allEntries = [];
final List<Reminder> _reminders = [];
void _onLogin(String username) {
setState(() {
_username = username;
_loggedIn = true;
});
}
void _addEntry(WaterEntry entry) {
setState(() => _allEntries.add(entry));
}
}
The upside is clarity. You can trace how state moves without jumping across files or abstractions. The downside is that the file gets crowded fast, which is exactly why this architecture reads like a prototype that knows its own limits.
Onboarding, login, and tracking are one happy path
Aquafit seems built around a narrow path: show onboarding once, move through login, then land in the home shell. The onboarding flag is runtime-only, which means the tutorial appears during the current app session without becoming permanent baggage. That is a clean first-run experience for a demo or capstone.
That happy path matters. A prototype does not need every edge case at once. It needs one believable route through the product, and Aquafit appears to have spent its effort exactly there.
What Aquafit is not doing yet
The largest limitation is persistence. The app stores entries in memory, so a restart wipes the state. That is not a flaw if the goal is to prove product shape, but it would be a hard stop in anything meant for everyday use.
| Question | Aquafit today | A fuller production app |
|---|---|---|
| Where does data live? | In memory | Local database or cloud sync |
| How is state managed? | Callbacks and lifted state | Dedicated state management or app store |
| How modular is the code? | One main file | Separated models, views, and services |
| What is the goal? | Demonstrate the experience | Support durable usage |
That trade-off is what makes the project legible. Aquafit is not pretending to be infrastructure. It is showing the shape of the experience first, then leaving the plumbing for later.
What this architecture optimizes for
Everything in Aquafit points to the same priorities: speed, comprehension, and demoability. The minimal dependency footprint, the single-file core, and the time-aware theme all suggest a team that wants to tell a clear story quickly. The assets hinted at in pubspec.yaml also read like presentation matters as much as raw mechanics.
That is why the app feels coherent despite being small. It is not a half-finished product. It is a constrained prototype with a very specific thesis: even a hydration tracker can feel alive if the interface responds to the clock.