Files
BalanceMono/BalanceFlutter/README.md
T
wt a2aa714a83
Android CI / Build and Test (pull_request) Failing after 11m39s
Flutter / build (BalanceFlutter/build/ios/iphoneos/*.app, macos-latest, ios) (push) Canceled after 0s
Flutter / build (BalanceFlutter/build/macos/Build/Products/Release/*.app, macos-latest, macos) (push) Canceled after 0s
Flutter / build (BalanceFlutter/build/windows/x64/runner/Release, windows-latest, windows) (push) Canceled after 0s
Flutter / watchos (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build Unsigned iOS IPA (push) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build macOS ZIP (push) Canceled after 0s
Flutter / checks (push) Successful in 25m8s
Flutter / build (BalanceFlutter/build/app/outputs/flutter-apk/*.apk, ubuntu-latest, android) (push) Failing after 38m6s
Flutter / build (BalanceFlutter/build/linux/x64/release/bundle, ubuntu-latest, linux) (push) Successful in 32m37s
Flutter / build (BalanceFlutter/build/web, ubuntu-latest, web) (push) Successful in 31m39s
iOS CI / Build and Test SwiftUI App (pull_request) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build Unsigned iOS IPA (pull_request) Canceled after 0s
Build Unsigned iOS and macOS Apps / Build macOS ZIP (pull_request) Canceled after 0s
Flutter / build (BalanceFlutter/build/ios/iphoneos/*.app, macos-latest, ios) (pull_request) Canceled after 0s
Flutter / build (BalanceFlutter/build/macos/Build/Products/Release/*.app, macos-latest, macos) (pull_request) Canceled after 0s
Flutter / build (BalanceFlutter/build/windows/x64/runner/Release, windows-latest, windows) (pull_request) Canceled after 0s
Flutter / watchos (pull_request) Canceled after 0s
Flutter / checks (pull_request) Successful in 24m59s
Flutter / build (BalanceFlutter/build/app/outputs/flutter-apk/*.apk, ubuntu-latest, android) (pull_request) Failing after 31m24s
Flutter / build (BalanceFlutter/build/linux/x64/release/bundle, ubuntu-latest, linux) (pull_request) Successful in 32m34s
Flutter / build (BalanceFlutter/build/web, ubuntu-latest, web) (pull_request) Successful in 24m54s
added oled theme and theme presets
2026-10-28 16:57:34 +07:00

14 KiB
Raw Blame History

Баланс — Flutter / Material 3

Личный бюджет с локальным хранилищем и синхронизацией с существующим BalanceServer. Сервер и его протокол не переписаны. CloudKit удалён по запросу: данные синхронизируются только через Go-сервер.

Возможности

  • Доходы и расходы: создание, редактирование, удаление, дата/время, заметки, поиск, фильтр типа.
  • Общий баланс, ручная корректировка отдельной операцией без влияния на аналитику.
  • Аналитика за 1, 3 и 12 календарных месяцев, доходы/расходы, результат, сбережения, средний расход, крупнейшая категория, динамика и структура расходов.
  • Месячные бюджеты по категориям, остаток и превышение; выбор месяца.
  • Встроенные и собственные категории, 50 значков Material с совместимыми серверными именами, 30 эмодзи и собственный эмодзи, 24 цвета. Переименование обновляет связанные записи; удаление сохраняет историю.
  • RUB, EUR, USD, SEK; системная, светлая и тёмная тема; пресеты Лес, Индиго, Океан, Роза и OLED; CSV-экспорт.
  • OLED использует чистый чёрный фон #000000 в тёмном режиме (включая карточки, навигацию и диалоги). При выборе OLED включается тёмная тема; затем можно выбрать системный режим. Режим и пресет сохраняются локально.
  • Локальная работа без аккаунта; сервер, регистрация, вход, выход, refresh, ручная и автоматическая синхронизация, полная пересинхронизация.
  • Транзакционное сохранение данных и курсора, очередь изменений и удалений, повтор после сетевого сбоя; отдельные хранилища аккаунтов.
  • Адаптивная навигация: нижняя панель на телефоне, боковая на широком экране.

Интерфейс на компьютере

OLED-пресет:

OLED настройки

Платформы

Платформа Реализация / сборка
Android Flutter, Android SDK и Java; APK/AAB
iPhone, iPad Один iOS Flutter target; сборка на macOS с Xcode
macOS Flutter runner; сборка на macOS с Xcode
Windows Flutter runner; сборка на Windows с Visual Studio C++ Desktop
Linux Flutter runner; GTK3, libsecret, jsoncpp, Clang, CMake, Ninja
Web Flutter JavaScript release; отдельная статическая раздача
Apple Watch Сохранён полноценный нативный SwiftUI target BalanceWatch в ../Balance/Balance.xcodeproj

Flutter не предоставляет официального watchOS runner. Часы используют исходный независимый клиент и напрямую тот же Go-сервер: обзор, аналитика, операции, категории, бюджеты, корректировка и настройки сохранены. Интерфейс Material 3 реализован во Flutter; часы используют SwiftUI. Не добавлен фиктивный flutter build watchos.

Проверенный SDK: Flutter 3.47.8, Dart 3.13.7. Зависимости закреплены в pubspec.lock. Точные выполненные проверки и ограничения среды перечислены в VALIDATION.md.

Запуск

cd BalanceFlutter
flutter pub get
flutter run -d chrome       # или устройство из flutter devices
flutter analyze
flutter test

Linux зависимости (Ubuntu):

sudo apt-get install clang cmake ninja-build pkg-config libgtk-3-dev libsecret-1-dev libjsoncpp-dev

Сборка из корня Flutter-проекта:

bash scripts/build.sh web
bash scripts/build.sh android
bash scripts/build.sh ios       # macOS; без подписи, для проверки сборки
bash scripts/build.sh macos     # macOS
bash scripts/build.sh windows   # Windows, Git Bash
bash scripts/build.sh linux
bash scripts/build.sh watchos   # macOS; исходный native Watch target, simulator
flutter build appbundle        # Android публикация

iPad использует тот же iOS target. В Xcode откройте ios/Runner.xcworkspace, выберите Team, свой Bundle ID и устройство. Для macOS — macos/Runner.xcworkspace. Для часов — ../Balance/Balance.xcodeproj, схема BalanceWatch, Team и уникальный Bundle ID. Сборка на симулятор без подписи не заменяет проверку на устройстве и настройку distribution signing.

Перед публикацией настройте собственные идентификаторы пакетов и Android release keystore. Пример настроек подписи находится в android/key.properties.example; реальные ключи и пароли не коммитьте. Подпись и публикация в магазины не выполнялись.

CI .github/workflows/flutter.yml содержит анализатор, тесты, отдельные сборки шести Flutter targets и нативного Watch. Это конфигурация для запуска в GitHub Actions; на Gitea её потребуется запускать совместимым runner либо перенести шаги в Gitea Actions.

Подключение существующего сервера и перенос данных

  1. Синхронизируйте старое приложение со своим работающим Go-сервером.
  2. В новом приложении откройте Настройки → Адрес сервера, сохраните HTTPS-адрес.
  3. Войдите с тем же аккаунтом и выполните синхронизацию.
  4. Сравните общий баланс, операции, категории, бюджеты и корректировки, затем переходите на новый клиент.

Сохраняются UUID, даты обновления, типы, снимки оформления категорий и флаг корректировки. Flutter не читает напрямую старые SwiftData/Room sandbox. CSV — отчёт, а не полный резервный формат, через него нельзя без потерь перенести бюджеты и корректировки.

При первом подключении нового аккаунта текущие локальные записи переносятся в него. Последующие аккаунты и серверы имеют отдельные базы; выход не удаляет их данные. Валюта и тема остаются настройками устройства. Валюта меняет формат, обменных курсов и пересчёта нет.

Внутренние суммы Flutter хранятся целыми сотыми долями валюты, серверный формат amount/limit остаётся числовым. Для старых записей с более чем двумя десятичными знаками используется округление до сотых. Интервалы везде [начало, конец), чтобы полночь следующего месяца не учитывалась дважды.

Web и обратный прокси

flutter build web создаёт build/web. Раздавайте его отдельной статикой, например Nginx, с fallback на index.html. Go-сервер продолжает отдавать свой встроенный старый Web-интерфейс; новая Flutter-сборка не заменяет его автоматически.

Укажите origin Flutter-сайта в существующей настройке сервера BALANCE_CORS_ORIGINS. Например: https://balance.example.org. API можно проксировать под /api/ с удалением префикса; тогда адрес сервера в приложении — https://balance.example.org/api.

server {
    listen 443 ssl;
    server_name balance.example.org;
    # ssl_certificate /...; ssl_certificate_key /...;
    root /var/www/balance;
    location / { try_files $uri $uri/ /index.html; }
    location /api/ {
        proxy_pass http://127.0.0.1:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Не переносите CSP заголовок встроенного Go Web-клиента на Flutter-статику: это другой frontend. На Web используйте HTTPS; browser storage доступен тому же origin и не обладает изоляцией системного Keychain/Keystore. При недоступном защищённом хранилище сессия сохраняется только в памяти, приложение показывает предупреждение. Финансовые записи остаются локально в IndexedDB.

Автоматическая синхронизация

На всех платформах синхронизация запускается при входе/открытии, возвращении в приложение и раз в две минуты, пока оно активно. Android дополнительно регистрирует WorkManager-задачу раз в 15 минут при наличии сети. ОС может откладывать её по энергосбережению или после принудительной остановки приложения.

Фоновая задача выполняет полный push/pull и обновление токенов. Общая нативная блокировка защищает сессию и хранилище между открытым приложением и фоновым Flutter engine. Проверены компиляция и тесты блокировки; выполнение задачи после закрытия приложения ещё нужно проверить на Android-устройстве.

Web использует Web Locks API для защиты общей базы и refresh-токенов между вкладками. Требуется современный браузер и HTTPS (или localhost). На desktop рекомендуется один запущенный экземпляр приложения: межпроцессная координация desktop-клиентов отдельно не реализована.

Roboto и CanvasKit раздаются вместе с Web-сборкой: сборочный скрипт использует --no-web-resources-cdn. Стандартный сгенерированный Flutter service worker кэширует оболочку; после первого открытия Web может работать с локальными данными без API, а синхронизация требует связи с сервером. Реальная установка PWA и восстановление без сети в браузере в этой среде не проверялись.

Устройство проекта

  • lib/domain/finance.dart: модели, расчёты, категории, CSV.
  • lib/services/app_store.dart: CRUD, очередь, сессии, атомарное применение страниц.
  • lib/services/sync_api.dart: существующий wire-протокол сервера.
  • lib/services/storage*.dart: Sembast / IndexedDB.
  • lib/ui/: интерфейс и формы Material 3.
  • test/: расчёты, хранилище, HTTP-протокол, формы и адаптивные экраны.
  • tool/integration_sync.dart: проверка с настоящим Go HTTP-сервером.
  • SYNC_PROTOCOL.md, FEATURE_PARITY.md: протокол и аудит переноса.

Для Linux в пользовательской сессии нужен работающий Secret Service, например GNOME Keyring или KWallet с поддержкой Secret Service, и библиотека libsecret-1-0: через него flutter_secure_storage сохраняет токены. Если служба недоступна или связка ключей заблокирована, приложение сохраняет сессию только в памяти и показывает предупреждение; локальные финансовые записи сохраняются отдельно в Sembast. Для сборки нужны clang cmake ninja-build pkg-config libgtk-3-dev libsecret-1-dev libjsoncpp-dev.