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.

5 to 6 min read • View on GitHub • More from Priyanshu-Rai01

A smartphone split into day and night halves, with the same hydration dashboard on both sides. A clock hovers above the phone while a thin stream of water moves through the interface like a metronome. The image explains Aquafit’s core idea: the app’s visual tone changes with the hour.
Aquafit’s UI is part of the wellness promise. The clock does not just time hydration, it reshapes the interface around it.
Key Takeaways

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.

Aquafit’s core behavior is not just hydration tracking. The app starts by checking the clock, then uses that state to decide how the rest of the interface should feel.

Design choiceAquafit’s approachTrade-offWhy it makes sense here
State managementLifted state through a central widgetMore props and callbacksKeeps the flow explicit in a prototype
File structureMost logic in one Dart fileHarder to scale cleanlyFaster to build, easier to demo
PersistenceIn-memory listsData disappears on restartGood enough for proving the interaction model
ThemeTime-based light and dark modesLess user control than manual togglesMatches 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.

A tall central tower labeled main.dart with small branches feeding out to onboarding cards, a login gate, hydration entries, reminders, and a theme switcher. The image explains how one file acts as the app’s control center without hiding the dependencies between screens and state.
The monolith is the point. Everything important hangs off one trunk, which keeps the prototype easy to inspect and change.

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.

QuestionAquafit todayA fuller production app
Where does data live?In memoryLocal database or cloud sync
How is state managed?Callbacks and lifted stateDedicated state management or app store
How modular is the code?One main fileSeparated models, views, and services
What is the goal?Demonstrate the experienceSupport 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.