mnemo_cards/games/flutter_webview_bridge_plan.md

486 lines
14 KiB
Markdown
Raw Permalink Normal View History

2025-11-10 23:55:41 +00:00
# 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'ов
- Таймауты для предотвращения утечек памяти