Please Pay Me — Flutter mobile
Мобильный кабинет для бюджета «от зарплаты до зарплаты». UI построен на
iOS-ките (Cupertino, Apple HIG), данные — REST API из src/PleasePayMe.Api.
Запуск
cd mobile
copy .env.example .env # один раз
# отредактируй PPM_API_BASE_URL в .env
.\run.ps1
.\run.ps1 -Device chrome
.\run.ps1 -Target widgetbook
run.ps1 читает mobile/.env и передаёт его во Flutter как
--dart-define-from-file. Если файла нет — копирует .env.example.
export PATH="$HOME/flutter/bin:$PATH"
cd mobile
flutter pub get
flutter run -d windows --dart-define-from-file=.env
Конфигурация (mobile/.env)
| Переменная | Назначение |
|---|---|
PPM_API_BASE_URL |
адрес API; если пусто — https://please-pay-me.ru |
PPM_WEB_URL |
веб-кабинет; если пусто — тот же origin, что API |
PPM_DEMO |
true — принудительный in-memory backend |
Авторизация
На iOS и Android:
- Telegram — кабинет в WebView, JWT забирается из
localStorage.ppm_session_jwtчерез каналPpmAuth. - Яндекс — WebView на
oauth.yandex.ru. Код возвращается на{кабинет}/(как Callback URL в кабинете Яндекса), приложение шлёт его вPOST /api/auth/yandex. Client secret живёт только на API.
Приложение проверяет JWT через GET /api/me и кладёт сессию в
SharedPreferences.
На Windows / в браузере WebView нет: остаётся ручной ввод токена. Альтернатива
на любой платформе — «Демо-режим» (DemoBackend без сети).
В кабинете Яндекса должен быть Redirect URI https://<домен>/
(для продакшена — https://please-pay-me.ru/). BotFather → /setdomain
для Telegram — тот же домен.
Экраны
| Вкладка | Что делает |
|---|---|
| Обзор | остаток бюджета, дневной лимит, трата в один тап, ближайшая выплата |
| Журнал | операции по дням, фильтр «текущий / все бюджеты», подгрузка страниц |
| Бюджеты | выбор активного конверта, создание, редактирование, архив, удаление |
| Работа | оклад, дни выплат, правило выходных, график ближайших зарплат |
| Профиль | пользователь, режим подключения, выход |
Структура
mobile/
├── lib/
│ ├── app/ # CupertinoApp, session gate, таб-бар
│ ├── core/ # config, storage, AsyncValue, форматтеры
│ ├── data/
│ │ ├── api/ # ApiClient (http + маппинг ошибок)
│ │ ├── models/ # Budget/Expense/Job/AuthUser + JSON-хелперы
│ │ ├── repositories/ # интерфейсы + REST-реализации
│ │ └── demo/ # in-memory backend (превью, тесты, демо-режим)
│ ├── features/ # overview / journal / budgets / work / profile / auth
│ ├── theme/ # токены Apple HIG + CupertinoThemeData
│ └── ui/ # дизайн-система (atoms → molecules → navigation)
└── widgetbook/ # переносимый каталог компонентов и экранов
Слои связаны через интерфейсы репозиториев: экраны знают только
BudgetRepository / ExpenseRepository / JobRepository, а SessionController
подставляет REST- или demo-реализацию. Поэтому и Widgetbook, и виджет-тесты
гоняют настоящие экраны без сети.
Тесты
cd mobile && flutter test # модели, форматтеры, контроллеры, сквозной сценарий
cd mobile/widgetbook && flutter test # рендер каждого use-case в light и dark