mnemo_cards/games/DART_HANDLER_EXPLANATION.md

266 lines
9.7 KiB
Markdown
Raw Permalink Normal View History

2025-11-10 23:55:41 +00:00
# 🔗 Объяснение прямого вызова Dart кода из JavaScript
## 📋 **Новый подход: Прямой вызов Dart функции**
### 🎯 **Принцип:**
Вместо создания заглушки, мы устанавливаем настоящий Dart обработчик **до** инициализации JavaScript bridge, обеспечивая его готовность к моменту первого вызова.
### 📜 **JavaScript код (hostBridgeScript):**
```javascript
window.flutterBridge = {
// Send message from Web to Host
sendMessage: function(message) {
console.log('[Flutter Bridge] Отправка сообщения в Flutter Host:', message);
window.flutter_inappwebview.callHandler('flutterBridge', message);
},
// Receive message from Host to Web - ПРЯМОЙ ВЫЗОВ DART
receiveMessage: function(message) {
console.log('[Flutter Bridge] Получение сообщения от Flutter Host:', message);
// Прямой вызов Dart функции
window.dartHandler(message);
}
};
```
### 🔄 **Порядок инициализации:**
#### 1. **Установка Dart обработчика** (WebBridge._setupDartHandler())
```dart
// В web_bridge.dart - ВЫПОЛНЯЕТСЯ ПЕРВЫМ
void _setupDartHandler() {
js.context['dartHandler'] = (String message) {
// Настоящий Dart обработчик
final bridgeMessage = BridgeMessage.fromJson(jsonDecode(message));
_bridgeManager!.handleIncomingMessage(bridgeMessage);
};
}
```
#### 2. **Инициализация BridgeManager** (WebBridge._initializeBridgeManager())
```dart
// В web_bridge.dart - ВЫПОЛНЯЕТСЯ ВТОРЫМ
_bridgeManager!.initialize(); // WebTransport.initialize() без заглушки
```
### ✅ **Преимущества нового подхода:**
- **Нет заглушки** - исключены ошибки "Dart обработчик не определен"
- **Прямой вызов** - JavaScript сразу вызывает настоящий Dart код
- **Простота** - меньше промежуточных звеньев
- **Надежность** - нет проблем с timing'ом
---
## 🎯 **Где устанавливаются настоящие Payload Handlers?**
### 📍 **Место установки:**
Настоящие payload handlers устанавливаются в **Dart коде** в методе `_registerHandlers()` класса `WebBridge`.
### 🔧 **Процесс установки:**
#### 1. **Создание BridgeManager**
```dart
// В web_bridge.dart
_bridgeManager = BridgeManagerFactory.createWebManager(
evaluateJavaScript: (script) { ... },
sendToHost: (jsonString) { ... },
);
```
#### 2. **Регистрация handlers**
```dart
// В web_bridge.dart -> _registerHandlers()
_bridgeManager!.registerHandler('ping', PingHandler(sendPayload));
_bridgeManager!.registerHandler('ping_response', PingResponseHandler());
_bridgeManager!.registerHandler('user_info_response', UserInfoResponseHandler());
_bridgeManager!.registerHandler('login_response', LoginResponseHandler());
_bridgeManager!.registerHandler('quiz_submission_response', QuizResponseHandler());
```
#### 3. **Настройка callback**
```dart
// В web_bridge.dart
_bridgeManager!.onPayloadReceived = (payload) {
debugPrint('📥 [WebBridge] Получен payload через callback: ${payload.runtimeType}');
onPayloadReceived?.call(payload);
};
```
---
## 🎯 **Где происходит вызов Dart кода из JS (payload handlers)?**
### 📍 **Точка входа:**
Вызов Dart кода из JavaScript происходит в методе `_handleIncomingMessage()` класса `BridgeManager`.
### 🔄 **Полная цепочка вызова:**
#### 1. **JavaScript → Dart** (в web_bridge.dart):
```dart
// Настоящий Dart обработчик, установленный в _setupDartHandler()
js.context['dartHandler'] = (String message) {
// 1. Парсим JSON
final json = jsonDecode(message);
// 2. Создаем BridgeMessage
final bridgeMessage = BridgeMessage.fromJson(json);
// 3. Передаем в BridgeManager
_bridgeManager!.handleIncomingMessage(bridgeMessage);
};
```
#### 2. **BridgeManager → Payload Handler** (в bridge_manager.dart):
```dart
// В BridgeManager._handleIncomingMessage()
void _handleIncomingMessage(BridgeMessage message) {
// 1. Находим handler по типу
final type = message.type;
final handler = _handlers[type]; // ← Здесь происходит поиск зарегистрированного handler
if (handler != null) {
// 2. Десериализуем payload
final payload = PayloadRegistry.deserialize(message);
if (payload != null) {
// 3. Вызываем callback
if (onPayloadReceived != null) {
onPayloadReceived!.call(payload);
}
// 4. ВЫЗЫВАЕМ НАСТОЯЩИЙ PAYLOAD HANDLER ← ЗДЕСЬ!
handler.handle(payload).then((response) {
if (response != null) {
sendPayload(response);
}
});
}
}
}
```
#### 3. **Payload Handler выполняется** (в payload_handlers.dart):
```dart
// Пример: PingHandler.handle()
class PingHandler implements PayloadHandler {
@override
Future<BridgePayload?> handle(BridgePayload payload) async {
if (payload is PingPayload) {
// ← ЗДЕСЬ ВЫПОЛНЯЕТСЯ БИЗНЕС-ЛОГИКА!
debugPrint('🏓 [PingHandler] Обработка ping: "${payload.message}"');
// Отправляем pong обратно
final pongPayload = payload.toPong();
_sendPayload(pongPayload);
}
return null;
}
}
```
---
## 🔄 **Полная цепочка обработки сообщений:**
### 📥 **Входящее сообщение (Host → Web):**
```
1. Flutter Host отправляет BridgeMessage
2. JavaScript: window.flutterBridge.receiveMessage(message)
3. JavaScript: window.dartHandler(message) ← ПРЯМОЙ ВЫЗОВ DART
4. Dart: js.context['dartHandler'](message) ← В web_bridge.dart
5. Dart: BridgeMessage.fromJson(jsonDecode(message))
6. Dart: _bridgeManager!.handleIncomingMessage(bridgeMessage) ← В bridge_manager.dart
7. Dart: BridgeManager находит handler по типу payload
8. Dart: handler.handle(payload) ← НАСТОЯЩИЙ PAYLOAD HANDLER! ← В payload_handlers.dart
```
### 📤 **Исходящее сообщение (Web → Host):**
```
1. Dart: sendPayload(payload)
2. Dart: _bridgeManager!.sendPayload(payload)
3. Dart: BridgeManager создает BridgeMessage
4. Dart: jsonEncode(bridgeMessage.toJson())
5. Dart: sendToHost(jsonString)
6. JavaScript: flutterBridge.callMethod('sendMessage', [jsonString])
7. Flutter Host получает сообщение
```
---
## 🎯 **Примеры Payload Handlers:**
### 🏓 **PingHandler** (обрабатывает входящие ping):
```dart
class PingHandler implements PayloadHandler {
final Function(BridgePayload) _sendPayload;
@override
Future<BridgePayload?> handle(BridgePayload payload) async {
if (payload is PingPayload) {
// Получили ping, отправляем pong обратно
final pongPayload = payload.toPong();
_sendPayload(pongPayload);
}
return null;
}
}
```
### 👤 **UserInfoResponseHandler** (обрабатывает ответы с данными пользователя):
```dart
class UserInfoResponseHandler implements PayloadHandler {
@override
Future<BridgePayload?> handle(BridgePayload payload) async {
if (payload is UserInfoResponsePayload) {
// Обрабатываем полученные данные пользователя
debugPrint('Данные пользователя: ${payload.userInfo}');
}
return null;
}
}
```
---
## 🔑 **Ключевые моменты:**
### ✅ **Новый подход:**
- **Нет заглушки** - исключены ошибки "Dart обработчик не определен"
- **Прямой вызов** - JavaScript сразу вызывает настоящий Dart код
- **Правильный порядок** - Dart обработчик устанавливается до инициализации bridge
### ✅ **Payload Handlers:**
- Устанавливаются в **Dart коде**
- Обрабатывают **типизированные payload'ы**
- Содержат **бизнес-логику** приложения
- Регистрируются в **BridgeManager**
- **Вызываются** в `BridgeManager._handleIncomingMessage()`
### ✅ **Порядок инициализации:**
1. `_setupDartHandler()` → Настоящий Dart обработчик установлен
2. `_bridgeManager!.initialize()` → JavaScript bridge готов
3. `_registerHandlers()` → Payload handlers зарегистрированы
### ✅ **Точка вызова Payload Handlers:**
- **Место:** `BridgeManager._handleIncomingMessage()`
- **Условие:** `handler.handle(payload)`
- **Контекст:** После десериализации payload и вызова callback
---
**Итог:** Убрана заглушка, теперь JavaScript напрямую вызывает Dart функцию. Dart обработчик устанавливается до инициализации bridge, что исключает ошибки timing'а. Настоящие payload handlers вызываются в `BridgeManager._handleIncomingMessage()` после обработки входящего сообщения.