mnemo_cards/games/DART_HANDLER_EXPLANATION.md
2025-11-11 02:55:41 +03:00

9.7 KiB
Raw Permalink Blame History

🔗 Объяснение прямого вызова Dart кода из JavaScript

📋 Новый подход: Прямой вызов Dart функции

🎯 Принцип:

Вместо создания заглушки, мы устанавливаем настоящий Dart обработчик до инициализации JavaScript bridge, обеспечивая его готовность к моменту первого вызова.

📜 JavaScript код (hostBridgeScript):

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())

// В web_bridge.dart - ВЫПОЛНЯЕТСЯ ПЕРВЫМ
void _setupDartHandler() {
  js.context['dartHandler'] = (String message) {
    // Настоящий Dart обработчик
    final bridgeMessage = BridgeMessage.fromJson(jsonDecode(message));
    _bridgeManager!.handleIncomingMessage(bridgeMessage);
  };
}

2. Инициализация BridgeManager (WebBridge._initializeBridgeManager())

// В web_bridge.dart - ВЫПОЛНЯЕТСЯ ВТОРЫМ
_bridgeManager!.initialize(); // WebTransport.initialize() без заглушки

Преимущества нового подхода:

  • Нет заглушки - исключены ошибки "Dart обработчик не определен"
  • Прямой вызов - JavaScript сразу вызывает настоящий Dart код
  • Простота - меньше промежуточных звеньев
  • Надежность - нет проблем с timing'ом

🎯 Где устанавливаются настоящие Payload Handlers?

📍 Место установки:

Настоящие payload handlers устанавливаются в Dart коде в методе _registerHandlers() класса WebBridge.

🔧 Процесс установки:

1. Создание BridgeManager

// В web_bridge.dart
_bridgeManager = BridgeManagerFactory.createWebManager(
  evaluateJavaScript: (script) { ... },
  sendToHost: (jsonString) { ... },
);

2. Регистрация handlers

// В 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

// В 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 обработчик, установленный в _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):

// В 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):

// Пример: 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):

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 (обрабатывает ответы с данными пользователя):

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() после обработки входящего сообщения.