Персональный дневник времени: трекер активности и дневник мыслей в одном приложении.
Pet project. Делаю для себя, в свободное время. Без roadmap'а на годы, без обещаний обратной совместимости, без поддержки в трекере. Если что-то полезно — забирайте; если что-то сломалось — pull request приветствуется, но ответ может прийти когда придёт.
Главный экран. Таймер идёт от конца последней зафиксированной записи — без старт/стоп.
Большинство трекеров времени требуют запускать таймер вручную. Я постоянно забывал — и переставал ими пользоваться через неделю.
time_diary устроен иначе: таймер идёт всегда. Он считается налету как now − endOfLastRecord. Когда фиксируешь блок — выбираешь активность и (опционально) добавляешь мысль/заметку. Сценарий «зафиксировать блок без заметки» занимает секунды.
Это превращает трекер времени в дневник: каждый блок — это и время, и контекст того, что в нём происходило.
- Android — работает.
- iOS — в планах.
- Таймер без старт/стоп. Идёт от конца последней записи; сбрасывается только фиксацией нового блока.
- Фиксация в два тапа. Активность + опциональная заметка.
- Активности. Создание, редактирование, архивация. Цвет и иконка на каждую.
- Привычки. Цели по длительности или количеству, периодичность (день/неделя/месяц/год), направление (
atLeast/atMost), дни недели, стрики. - Статистика. День / неделя / месяц / год; круговые диаграммы, графики, факты по активности.
- Дневник. Заметки из блоков агрегируются в отдельную ленту.
- PIN на дневник. SHA-256 + случайная соль, кулдаун после трёх неверных попыток.
- Локальные уведомления. Напоминания + persistent-таймер с тихими часами.
- Темы и акценты. Кастомизируемый акцентный цвет, анимированный фон главного экрана.
- Локализация. Русский, английский (через
slang). - Offline-first. Drift как source of truth; Firestore — необязательный sync.
- Sign-in. Google (Android).
- Экспорт. JSON-дамп всех данных в один файл.
- Сброс. Локальный (без потери облака) и облачный (без потери локального).
![]() Фиксация записи. Активность, время, мысль. |
![]() Активность. Иконка и цвет. |
![]() Статистика. Распределение времени по активностям. |
![]() Детали активности. Динамика, тренды, факты. |
![]() Настройки. Sync, темы, PIN, уведомления. |
| Слой | Что используется |
|---|---|
| UI | Flutter (Dart ^3.11.4) |
| State management | flutter_bloc (Cubit), get_it для DI |
| Навигация | auto_route |
| Локальная БД | drift + drift_flutter |
| Облако | firebase_core, firebase_auth, cloud_firestore |
| Auth | google_sign_in, sign_in_with_apple |
| i18n | slang + slang_flutter |
| Уведомления | flutter_local_notifications + timezone |
| Графики | fl_chart |
| Тесты | flutter_test, mocktail |
Modified Clean Architecture без канонических UseCase. Бизнес-логика живёт в интеракторах.
flowchart LR
UI["UI<br/>Widget / Screen"] <-->|state| Cubit
Cubit --> Interactor["Interactor<br/>бизнес-правила"]
Interactor --> Repo["Repository"]
Repo <-->|streams| Local[("Drift<br/>source of truth")]
Repo -.->|incremental sync| Remote[("Firestore<br/>опционально")]
LocalDataSource— Drift; для чтения возвращаетStream.RemoteDataSource— Firestore. Используется только из Repository.Repository— оркестрирует источники, остаётся в рамках одной сущности.Interactor— может зависеть от нескольких Repository, содержит бизнес-правила.Cubit— знает только про Interactor.- UI подписан на Cubit, не ходит в Data/Domain напрямую.
Подробнее — docs/architecture.md.
lib/
core/ # config, DI, router, theme, notifications, shared widgets
data/
database/ # Drift tables/DAOs
datasources/ # local + remote
repositories/
domain/
entities/ # Activity, TimeBlock, Habit, AppSettings, AuthUser
interactors/ # бизнес-правила
models/ # value objects
utils/
presentation/
activity/ auth/ habits/ home/ main_tabs/
record/ settings/ stats/ sync/ widgets/
i18n/ # slang JSON + сгенерированные файлы
test/
domain/interactors/ # unit-тесты бизнес-логики
helpers/ # test doubles, фабрики
widget_test.dart # smoke
Несколько правил, которые держит сам домен (а не UI):
- Записи не пересекаются по времени — гарантирует
TimeBlockInteractor. - Запись через полночь автоматически делится на части по дням;
noteкрепится к последней. - Удаление записи оставляет «дыру» — соседи не растягиваются.
- Активность с записями нельзя удалить, только архивировать.
- Таймер нельзя поставить на паузу. Сбрасывается только фиксацией нового блока.
- Offline-first: Drift — source of truth; Firestore — дополнительный слой.
Полный список — docs/product.md.
- Flutter SDK через
fvm(версия пиннится автоматически). - Android Studio для нативных билдов.
- Firebase-проект, если нужен sync (опционально).
# Зависимости
fvm flutter pub get
# Кодогенерация (Drift, AutoRoute, slang)
fvm dart run build_runner build --delete-conflicting-outputs
# Запуск
fvm flutter runБез Firebase приложение работает в полностью offline-режиме — sync и облачный sign-in недоступны, остальное на месте.
Для включения sync:
- Создать Firebase-проект, добавить Android и iOS apps.
- Положить
google-services.jsonвandroid/app/. - Положить
GoogleService-Info.plistв Xcode targetRunner. - Заполнить
config.local.json(см. docs/sync.md).
fvm flutter testЮнит-тесты интеракторов лежат в test/domain/interactors/. Моки репозиториев — через mocktail. Smoke widget-тест проверяет, что shell с 4 табами строится и Home рисуется.
Принципы качества тестов в проекте описаны в docs/tooling.md — короткая выжимка: тестируем поведение, а не реализацию; специфичные ассерты вместо throwsArgumentError; границы вместо середин интервалов; полный детерминизм (никаких DateTime.now() в тестах).
Внутренняя документация для разработки — в docs/:
- product.md — продуктовые инварианты.
- architecture.md — слои и потоки данных.
- domain.md — сущности и интеракторы.
- home.md, stats.md, habits.md, records.md, settings.md, sync.md, design.md — фичи.
- tooling.md — команды, тесты, локализация.





