266 lines
9.7 KiB
Markdown
266 lines
9.7 KiB
Markdown
|
|
# 🔗 Объяснение прямого вызова 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()` после обработки входящего сообщения.
|