Backend was already V1-complete for these paths (moveFlexibleToBacklog,
breakUpTask); this closes the UI-wiring gap identified in the V1
feature-complete audit.
- Add createBacklogController() to SchedulerAppComposition and implement it
in PersistentSchedulerComposition (only the demo composition had one).
- Sidebar now switches the main content area between Today and Backlog via
a real SidebarScreen enum instead of hardcoded onTap: () {} stubs.
- Task modal's "Backlog" quick-action reuses the existing onPushToBacklog
wiring (same backend command as the Push menu's "Push to backlog").
- Add a break-up dialog (row-level title/priority/reward/duration per
MVP-AC-11) and SchedulerCommandController.breakUpTask(), wired to the
modal's "Break up" button.
- Fix a latent bug where the active sidebar nav item's key was hardcoded
to nav-today-active regardless of which item was active.
Closes: #9
Closes: #12
Closes: #13
5.4 KiB
FocusFlow Flutter
Flutter desktop app target for the FocusFlow UI work.
Current Scope
The app launches into the compact Today timeline. Normal startup now opens an on-disk SQLite database and bootstraps owner settings plus the default Home project when they are missing.
The compact Today screen can toggle completion for currently rendered task cards. The sidebar switches between the Today and Backlog screens, and the Backlog screen supports quick capture and scheduling backlog items.
Runtime Data
Normal runtime uses lib/app/persistent_scheduler_composition.dart, which reads
SCHEDULER_SQLITE_PATH from a Dart define. If no define is present, it uses the
local dev convention:
~/ADHD_Scheduler/scheduler.sqlite
Static demo data still lives in lib/app/demo_scheduler_composition.dart, but
it is for deterministic widget/visual tests and explicit demo composition only.
It is not inserted during normal startup.
Widgets consume TodayScreenData from lib/models/today_screen_models.dart.
Scheduler core remains the source of truth for task state and Today ordering.
Runtime Config
The app optionally reads a JSON config file from:
~/ADHD_Scheduler/config.json
Override the config path with:
flutter run -d linux --dart-define=FOCUS_FLOW_CONFIG_PATH=/tmp/focus_flow_config.json
Supported optional keys:
{
"Timezone": "UTC",
"LogLevel": "fine",
"LogfileLocation": "~/ADHD_Scheduler/debug/"
}
Timezone accepts a three-letter time-zone code without regard to character
case, such as UTC, GMT, PST, or PDT. Common daylight-saving pairs such
as PST/PDT, MST/MDT, CST/CDT, and EST/EDT are resolved once when
the config loads, so either seasonal abbreviation uses the correct current
offset for that zone during the app session. The setting is an app/UI reference
only: it controls wall-clock timeline display, selected-day startup, and
time-relative actions such as pushing a task to the next slot or tomorrow. Task
instants and all persistence data remain stored as UTC/GMT values.
LogLevel accepts finest, finer, fine, debug, info, warn, or
error.
LogfileLocation is treated as a directory, and the app writes
focus_flow.log inside it. If either key is absent or invalid, file logging is
not enabled and the app ignores that setting.
Verbose log levels can include automatic caller information without requiring
each call site to pass it manually. finest includes caller frames for every
written message. fine, finer, and finest include caller frames for
warnings and errors. This caller lookup is skipped when the configured level
does not require it.
For expensive debug/detail messages, pass a lazy closure so work is skipped when the configured level would not write the log:
logger.debug(() => 'state=${expensiveStateDump()}');
Package Boundary
Allowed app imports include public scheduler APIs such as:
import 'package:scheduler_core/scheduler_core.dart';
The persistent composition root may import
package:scheduler_persistence_sqlite/sqlite.dart and dart:io for runtime
path resolution. Widgets/controllers must not import scheduler src/ files,
Drift, SQLite adapters, desktop notification implementations, backup/export
packages, or direct OS APIs. test/forbidden_imports_test.dart enforces that
boundary.
Intentionally No-op
- Compact/Normal toggle.
- Settings button.
- Timeline card action icons.
Persistence Proof
Current focused tests prove:
- first run opens an empty SQLite file without seeded task rows,
- quick capture writes a backlog task,
- scheduling writes planned Today placement,
- done and not-done state persists,
- closing and reopening the same SQLite file reloads the task state.
Manual verification:
scripts/bootstrap_dev.sh
scripts/dev.sh --sqlite /tmp/focus_flow_persistence_plan_1.sqlite
Use the same --sqlite path after closing the app to confirm state persists. To
reset local dev data, close the app and delete the chosen .sqlite file. This is
only a dev reset; backup/restore UI remains out of scope.
Commands
Run scripts/bootstrap_dev.sh once from the repository root to create the
default SQLite directory.
Run the desktop UI from this directory:
flutter pub get
flutter run -d linux
Replace linux with macos or windows on those hosts. The default database
path is ~/ADHD_Scheduler/scheduler.sqlite. If Flutter reports that the
desktop project is not configured for that platform, generate the missing
runner from this directory:
flutter create --platforms=linux .
Use --platforms=macos or --platforms=windows for those hosts.
Use a disposable dev database when needed:
flutter run -d linux --dart-define=SCHEDULER_SQLITE_PATH=/tmp/focus_flow_dev.sqlite
Check the app from this directory:
flutter analyze
flutter test
Run these from apps/focus_flow_flutter/.
Next Plans
- Give the Compact/Normal toggle real backing state (the live Today pipeline has no compact-mode concept yet; see Forgejo issue #11).
- Add a real Settings screen bound to existing
OwnerSettings/BacklogStalenessSettingspersistence. - Wire timeline card quick-action icons to the same commands the task modal already uses.
- Add Shield/Recovery only in a later scope.