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

486 lines
No EOL
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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<String, dynamic>? 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<String, Completer<BridgeMessage>> _pendingCallbacks = {};
final StreamController<BridgeMessage> _messageStream = StreamController.broadcast();
Future<void> initialize(String url);
Future<void> sendMessage(BridgeMessage message);
Stream<BridgeMessage> 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<void> _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
<!DOCTYPE html>
<html>
<head>
<script src="bridge.js"></script>
</head>
<body>
<div id="flutter_target"></div>
<script src="main.dart.js" type="application/javascript"></script>
</body>
</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<String, Completer<BridgeMessage>> _pendingCallbacks = {};
final StreamController<BridgeMessage> _messageStream = StreamController.broadcast();
void initialize() {
// Регистрация глобального объекта
setProperty(js.context, 'flutterApp', {
'receiveFromHost': allowInterop(_handleMessageFromHost),
});
}
Future<BridgeMessage> sendMessage(BridgeMessage message) async {
final completer = Completer<BridgeMessage>();
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<BridgeWebView> 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<String, dynamic> 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<String, dynamic> 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<String, Completer<BridgeMessage>> _callbacks = {};
final Duration _timeout;
CallbackManager({this._timeout = const Duration(seconds: 30)});
String registerCallback(Completer<BridgeMessage> 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<MyHomePage> {
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<MyHomePage> {
final _bridge = WebBridge();
Future<void> _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'ов
- Таймауты для предотвращения утечек памяти