Flutter Client / Design Patterns
Архитектура Flutter клиента
Слоистая структура приложения, управление состоянием и оффлайн-синхронизация.
Архитектурные слои
Приложение разделено на 4 четких слоя по принципам Clean Architecture:
| Слой | Папка | Назначение |
|---|---|---|
| Presentation | lib/presentation | UI Виджеты, экраны, анимации и стили темы. Рендеринг интерфейса. |
| State Management (Logic) | lib/logic | Провайдеры (ChangeNotifier) — бизнес-логика интерфейса, связующее звено между UI и сервисами. |
| Domain | lib/domain | Интерфейсы и независимые сервисы (API, Crypto, WebRTC), выполняющие операции ввода-вывода и криптографию. |
| Data | lib/data | Drift SQLite база данных, сетевые источники (WS), репозитории и модели данных. |
Подробное устройство каталогов
📁 lib/
📁 core/ — Системные константы, ThemeManager конфигурация
📁 data/ — Реализация данных
📁 datasources/ — Drift база данных (local_db_service.dart), WebSocket клиент (ws_client.dart)
📁 models/ — Dart-классы для парсинга JSON: contact_model, message_model, session_model
📁 repositories/ — Реализация репозиториев (contact_repository.dart) для работы с кэшем
📁 domain/ — Логика предметной области
📁 services/ — api_service (HTTP REST), crypto_service (E2EE), webrtc_service (Звонки)
📁 logic/ — ChangeNotifier провайдеры
📄 auth_provider.dart — Контроль сессий, FaceID/PIN блокировка, приватный ключ в ОЗУ
📄 contact_provider.dart — Списки чатов, WebSocket обработка, квитанции ACK
📁 presentation/ — UI элементы
📁 screens/ — Все экраны интерфейса (25 штук)
📁 widgets/ — Компоненты UI (пузыри сообщений, карточки чатов, поля ввода)
Реактивная оффлайн-синхронизация
Клиент работает по принципу Offline-First. Для этого используется Drift SQLite. Поток сообщений рендерится с помощью StreamBuilder, подписанного на Drift-запрос:
dartРеактивный стрим сообщений
Stream<List<Message>> watchMessagesForChat(int contactId) {
return (select(messages)
..where((t) => (t.senderId.equals(contactId) & t.receiverId.equals(myId)) |
(t.senderId.equals(myId) & t.receiverId.equals(contactId)))
..orderBy([(t) => OrderingTerm(expression: t.id, mode: OrderingMode.desc)]))
.watch();
}
Когда с WebSocket сервера приходят новые данные через ws_client.dart, они парсятся и сразу записываются в Drift БД. Благодаря Stream-подписке интерфейс обновляется автоматически со скоростью 60/120 кадров в секунду, даже если в этот момент у пользователя прервалось сетевое подключение.