# Flutter WebView Bridge Package - План реализации ## 📦 Обзор пакета **Название**: `flutter_webview_bridge` **Цель**: Создать Flutter пакет для двустороннего взаимодействия между Flutter-хостом и Flutter Web-приложениями через WebView ## 🏗️ Архитектура пакета ``` flutter_webview_bridge/ ├── lib/ │ ├── src/ │ │ ├── bridge/ │ │ │ ├── webview_bridge.dart # Основной класс моста │ │ │ ├── message_handler.dart # Обработка сообщений │ │ │ ├── message_serializer.dart # Сериализация сообщений │ │ │ └── callback_manager.dart # Управление callback'ами │ │ ├── web/ │ │ │ ├── web_bridge.dart # Flutter Web сторона │ │ │ └── web_utils.dart # Утилиты для Web │ │ ├── host/ │ │ │ ├── host_bridge.dart # Flutter Host сторона │ │ │ └── webview_controller.dart # Контроллер WebView │ │ └── models/ │ │ ├── bridge_message.dart # Модель сообщения │ │ └── bridge_config.dart # Конфигурация │ ├── flutter_webview_bridge.dart # Основной экспорт │ └── bridge_webview.dart # WebView виджет ├── web/ │ └── bridge.js # JavaScript мост ├── example/ │ ├── lib/ │ │ ├── main.dart # Пример Flutter Host │ │ └── web_app.dart # Пример Flutter Web │ └── web/ │ └── index.html # HTML оболочка └── test/ └── bridge_test.dart # Тесты ``` ## 📋 Структура сообщений ### Модель сообщения ```dart class BridgeMessage { final String type; // Тип события/запроса final String? id; // UUID для request-response final Map? data; // Полезная нагрузка final DateTime? timestamp; // Временная метка final String? source; // Источник (host/web) } ``` ### Типы сообщений - **Request-Response**: `id` обязателен, ожидается ответ - **Event-Notify**: `id` отсутствует, одностороннее уведомление - **Stream-Subscription**: для подписки на обновления ## 🔧 Этапы разработки ### Этап 1: Базовая инфраструктура (1-2 дня) #### 1.1 Создание структуры пакета ```bash flutter create --template=package flutter_webview_bridge ``` #### 1.2 Основные модели - `BridgeMessage` - модель сообщения - `BridgeConfig` - конфигурация моста - `BridgeException` - исключения моста #### 1.3 Зависимости в pubspec.yaml ```yaml dependencies: flutter: sdk: flutter flutter_inappwebview: ^6.0.0 uuid: ^4.0.0 js: ^0.6.7 dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^3.0.0 ``` ### Этап 2: Flutter Host сторона (2-3 дня) #### 2.1 WebView контроллер ```dart class BridgeWebViewController { InAppWebViewController? _webViewController; final Map> _pendingCallbacks = {}; final StreamController _messageStream = StreamController.broadcast(); Future initialize(String url); Future sendMessage(BridgeMessage message); Stream get messageStream; } ``` #### 2.2 JavaScript Handler ```dart void _setupJavaScriptHandler() { _webViewController?.addJavaScriptHandler( callbackName: 'fromWebApp', callback: (args) { final message = BridgeMessage.fromJson(args[0]); _handleIncomingMessage(message); }, ); } ``` #### 2.3 Отправка сообщений в WebView ```dart Future _sendToWebView(BridgeMessage message) async { final jsCode = ''' window.dispatchEvent(new CustomEvent("fromFlutterHost", { detail: ${jsonEncode(message.toJson())} })); '''; await _webViewController?.evaluateJavascript(source: jsCode); } ``` ### Этап 3: JavaScript Bridge (1-2 дня) #### 3.1 Создание bridge.js ```javascript // Подписка на события от Flutter Host window.addEventListener("fromFlutterHost", (e) => { if (window.flutterApp && window.flutterApp.receiveFromHost) { window.flutterApp.receiveFromHost(e.detail); } }); // Отправка сообщений в Flutter Host window.sendToFlutterHost = function(message) { if (window.flutter_inappwebview) { window.flutter_inappwebview.callHandler('fromWebApp', message); } }; ``` #### 3.2 Интеграция в HTML ```html
``` ### Этап 4: Flutter Web сторона (2-3 дня) #### 4.1 Web Bridge класс ```dart class WebBridge { static final WebBridge _instance = WebBridge._internal(); factory WebBridge() => _instance; WebBridge._internal(); final Map> _pendingCallbacks = {}; final StreamController _messageStream = StreamController.broadcast(); void initialize() { // Регистрация глобального объекта setProperty(js.context, 'flutterApp', { 'receiveFromHost': allowInterop(_handleMessageFromHost), }); } Future sendMessage(BridgeMessage message) async { final completer = Completer(); if (message.id != null) { _pendingCallbacks[message.id!] = completer; } callMethod(js.context, 'sendToFlutterHost', [message.toJson()]); return completer.future; } } ``` #### 4.2 Обработка сообщений ```dart void _handleMessageFromHost(dynamic data) { final message = BridgeMessage.fromJson(data); // Проверяем, есть ли pending callback if (message.id != null && _pendingCallbacks.containsKey(message.id)) { _pendingCallbacks[message.id]!.complete(message); _pendingCallbacks.remove(message.id); } else { // Отправляем в стрим для event-notify сообщений _messageStream.add(message); } } ``` ### Этап 5: Основной API пакета (1-2 дня) #### 5.1 BridgeWebView виджет ```dart class BridgeWebView extends StatefulWidget { final String url; final BridgeConfig? config; final Function(BridgeMessage)? onMessage; final Widget? loadingWidget; const BridgeWebView({ required this.url, this.config, this.onMessage, this.loadingWidget, super.key, }); @override State createState() => _BridgeWebViewState(); } ``` #### 5.2 Основной экспорт ```dart // flutter_webview_bridge.dart export 'src/bridge/webview_bridge.dart'; export 'src/models/bridge_message.dart'; export 'src/models/bridge_config.dart'; export 'bridge_webview.dart'; ``` ### Этап 6: Утилиты и хелперы (1 день) #### 6.1 Message Serializer ```dart class MessageSerializer { static Map toJson(BridgeMessage message) { return { if (message.type != null) 'type': message.type, if (message.id != null) 'id': message.id, if (message.data != null) 'data': message.data, 'timestamp': message.timestamp.toIso8601String(), 'source': message.source, }; } static BridgeMessage fromJson(Map json) { return BridgeMessage( type: json['type'], id: json['id'], data: json['data'], timestamp: DateTime.parse(json['timestamp']), source: json['source'], ); } } ``` #### 6.2 Callback Manager ```dart class CallbackManager { final Map> _callbacks = {}; final Duration _timeout; CallbackManager({this._timeout = const Duration(seconds: 30)}); String registerCallback(Completer completer) { final id = const Uuid().v4(); _callbacks[id] = completer; // Автоматический таймаут Timer(_timeout, () { if (_callbacks.containsKey(id)) { _callbacks[id]!.completeError( BridgeException('Request timeout: $id') ); _callbacks.remove(id); } }); return id; } void completeCallback(String id, BridgeMessage response) { if (_callbacks.containsKey(id)) { _callbacks[id]!.complete(response); _callbacks.remove(id); } } } ``` ### Этап 7: Примеры использования (1-2 дня) #### 7.1 Flutter Host пример ```dart class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { BridgeWebViewController? _bridgeController; @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Flutter WebView Bridge')), body: BridgeWebView( url: 'http://localhost:8080', onMessage: _handleMessage, onBridgeReady: (controller) { _bridgeController = controller; }, ), ); } void _handleMessage(BridgeMessage message) { switch (message.type) { case 'getAppVersion': _sendResponse(message.id!, {'version': '1.0.0'}); break; case 'vibrate': HapticFeedback.vibrate(); break; } } } ``` #### 7.2 Flutter Web пример ```dart void main() { WebBridge().initialize(); runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State { final _bridge = WebBridge(); Future _getAppVersion() async { try { final response = await _bridge.sendMessage( BridgeMessage( type: 'getAppVersion', id: const Uuid().v4(), source: 'web', ), ); print('App version: ${response.data?['version']}'); } catch (e) { print('Error: $e'); } } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text('Flutter Web App')), body: Center( child: ElevatedButton( onPressed: _getAppVersion, child: Text('Get App Version'), ), ), ); } } ``` ### Этап 8: Тестирование (2-3 дня) #### 8.1 Unit тесты ```dart // test/bridge_test.dart void main() { group('BridgeMessage', () { test('should serialize and deserialize correctly', () { final message = BridgeMessage( type: 'test', id: '123', data: {'key': 'value'}, source: 'host', ); final json = MessageSerializer.toJson(message); final deserialized = MessageSerializer.fromJson(json); expect(deserialized.type, equals(message.type)); expect(deserialized.id, equals(message.id)); expect(deserialized.data, equals(message.data)); }); }); } ``` #### 8.2 Integration тесты - Тест полного цикла сообщений - Тест таймаутов - Тест обработки ошибок ### Этап 9: Документация (1-2 дня) #### 9.1 README.md - Описание пакета - Установка и настройка - Примеры использования - API документация #### 9.2 API документация - Документирование всех публичных классов и методов - Примеры кода - Troubleshooting ## 🚀 План релиза ### v0.1.0 - Alpha - Базовая функциональность - Request-response модель - Простые примеры ### v0.2.0 - Beta - Event-notify модель - Стримы для подписок - Улучшенная обработка ошибок ### v1.0.0 - Stable - Полная документация - Тесты покрытие >80% - Примеры для всех сценариев ## 📝 Чек-лист готовности - [ ] Структура пакета создана - [ ] Основные модели реализованы - [ ] Flutter Host сторона работает - [ ] JavaScript bridge интегрирован - [ ] Flutter Web сторона работает - [ ] Примеры созданы и работают - [ ] Тесты написаны и проходят - [ ] Документация готова - [ ] pubspec.yaml настроен - [ ] README.md написан ## 🔧 Технические детали ### Зависимости - `flutter_inappwebview`: для WebView функциональности - `uuid`: для генерации уникальных ID - `js`: для взаимодействия с JavaScript в Flutter Web ### Поддерживаемые платформы - ✅ Android - ✅ iOS - ✅ Web (Flutter Web в WebView) - ❌ Desktop (не планируется) - ❌ Linux (не планируется) ### Производительность - Минимальная задержка сообщений - Автоматическая очистка callback'ов - Таймауты для предотвращения утечек памяти