English | 中文
A Flutter habit tracker with calendar-based check-ins. Runs in the browser and as a native macOS desktop app.
- Records habit completion by date through a calendar-based UI, with both month and year-at-a-glance views per habit.
- Each habit card carries an inline mini-month calendar and adapts its layout to wide or narrow windows.
- Runs as a web app in Chrome and as a native macOS desktop app from one codebase.
- Multiple themes (dark 活力橙, light 浅色主题, and more) with live switching.
- Local JSON persistence, chosen per platform:
- macOS desktop reads and writes the JSON data file directly.
- Web syncs to a local JSON bridge, falling back to browser-local storage when the bridge is offline.
- Auto-refresh keeps the view current: external check-ins (e.g. from the Hermes bridge) show up within ~30s, the date rolls over at midnight, and a refresh never clobbers an in-progress local check-in.
- Import and export for local JSON backups.
- macOS desktop
- Web (Chrome, or any browser via
web-server)
lib/main.dart— app entry and UIlib/models/habit.dart— habit model and per-day completion logiclib/services/habit_storage.dart— bridge-first persistence with fallbacklib/services/habit_bridge*.dart— platform-conditional bridge clientshabit_bridge_io.dart— native file I/O (macOS desktop)habit_bridge_web.dart— HTTP client for the local JSON bridge (web)habit_bridge_stub.dart— no-op fallback
lib/services/backup_io*.dart— import/export helpers, selected per platformbin/daily_track_bridge.dart— localhost JSON bridge for the web app and Hermesdata/daily-track.example.json— sample seed data; the app writes real data todata/daily-track.json(gitignored) at runtimetest/widget_test.dart— widget smoke testweb/— web runner and manifestmacos/— macOS desktop runner
- Flutter SDK on the
stablechannel, Dart^3.10.1 - Git
- Web: Chrome
- macOS desktop: Xcode and CocoaPods
flutter doctor -vflutter pub getflutter run -d macosThe desktop app reads and writes its data file directly. The path is resolved in this order:
DAILY_TRACK_DATA_PATHenvironment variable~/.daily-trackpointer file — a plain text file whose only content is the absolute path of the data file (supports~/prefix)- The relative path
data/daily-track.json(resolved against the working directory)
Apps launched from Finder or the Dock get neither shell environment variables
nor a useful working directory, so the pointer file is the reliable way to point
an installed .app at your data file:
echo "/path/to/your/daily-track.json" > ~/.daily-trackWithout it, a Finder-launched app silently falls back to an internal local cache and stops syncing with the JSON file.
Start the local JSON bridge first (optional, enables shared sync):
dart run bin/daily_track_bridge.dartThen run the web app:
flutter run -d chrome
# or, if Chrome is unavailable:
flutter run -d web-serverThe bridge listens on http://127.0.0.1:8765 and writes data/daily-track.json.
Record today's fitness check-in from another local tool such as Hermes:
curl -sS -X POST http://127.0.0.1:8765/checkins \
-H 'Content-Type: application/json' \
-d '{"habitId":"fitness","title":"健身","date":"2026-05-28","completed":true,"source":"telegram"}'flutter analyze
flutter test
flutter build web
flutter build macos- On macOS the app owns the JSON file directly; with the web bridge running, the browser and Telegram/Hermes can share the same file.
- The app polls its store about every 30s, so external writes (Hermes/Telegram) surface automatically. The poll only rebuilds the UI when the data actually changed, and it yields while a local save is in flight, so it never drops a check-in you just made.
- Without the bridge, web data is stored in the browser and survives refreshes only on the same profile and origin.
- The storage layer keeps a fallback reader for the old
daily_routinekey so existing local data still loads.