Compare commits
20 Commits
0.0.2
...
e6c63982a0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e6c63982a0 | ||
|
|
b57f043875 | ||
|
|
9007b717ce | ||
|
|
f51c64c9a2 | ||
|
|
c8924ee3ed | ||
|
|
9545928a35 | ||
|
|
95e5c65709 | ||
|
|
d6d83fbc46 | ||
|
|
63b38704dc | ||
|
|
587af2c22a | ||
|
|
cbd3d8eff8 | ||
|
|
20f557c668 | ||
|
|
e54e11fef1 | ||
|
|
d71234da78 | ||
|
|
7b38d2c835 | ||
|
|
7350940500 | ||
|
|
54a522b3f9 | ||
|
|
ae48890a1c | ||
|
|
ee927acf63 | ||
|
|
c1ef4d3b26 |
@@ -1,3 +1,8 @@
|
|||||||
## 0.0.1
|
## 0.0.1
|
||||||
|
* Initial release
|
||||||
|
|
||||||
* TODO: Describe initial release.
|
## 0.0.2
|
||||||
|
* 优化通信
|
||||||
|
|
||||||
|
## 0.0.3
|
||||||
|
* 添加iOS/Android下载地址
|
||||||
|
|||||||
146
README.md
146
README.md
@@ -51,18 +51,17 @@ bridge.share(
|
|||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
### 监听来自 Flutter 的消息
|
## 示例页面
|
||||||
|
|
||||||
```dart
|
包中包含了一个完整的示例页面 `WebToolsExamplePage`,展示了所有交互方法的使用示例,按功能模块分类组织。
|
||||||
// 注册监听器
|
|
||||||
FlutterBridge.instance.on(FromFlutterAppEnum.translateResult.code, (data) {
|
|
||||||
print("收到翻译结果: ${data['data']}");
|
|
||||||
// 处理翻译结果
|
|
||||||
});
|
|
||||||
|
|
||||||
// 使用完毕后记得取消监听
|
### 使用示例页面
|
||||||
FlutterBridge.instance.off(FromFlutterAppEnum.translateResult.code);
|
```agsl
|
||||||
|
在自己的web项目中 添加测试跳转连接
|
||||||
|
Get.to(WebToolsExamplePage());
|
||||||
```
|
```
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
## API 文档
|
## API 文档
|
||||||
|
|
||||||
@@ -76,28 +75,42 @@ FlutterBridge.instance.off(FromFlutterAppEnum.translateResult.code);
|
|||||||
final bridge = FlutterBridge.instance;
|
final bridge = FlutterBridge.instance;
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 消息监听方法
|
### 监听来自 Flutter 的消息 (支持一对多监听)
|
||||||
|
### 不走队列。这是“广播”模式
|
||||||
##### `on(String type, Function(Map<String, dynamic>) callback)`
|
##### `on(String type, Function(Map<String, dynamic>) callback)`
|
||||||
|
|
||||||
注册一个消息监听器。
|
注册一个消息监听器。现在支持为同一 `type` 注册多个监听器。
|
||||||
|
|
||||||
- `type`: 消息类型(使用 `FromFlutterAppEnum` 中的 code)
|
- `type`: 消息类型(使用 `FromFlutterAppEnum` 中的 code)
|
||||||
- `callback`: 回调函数,接收消息数据
|
- `callback`: 回调函数,接收消息数据
|
||||||
|
|
||||||
|
**返回** `VoidCallback`: 调用此函数可以精确地移除当前注册的这个监听器。
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
bridge.on(FromFlutterAppEnum.translateResult.code, (data) {
|
// 注册第一个监听器
|
||||||
// 处理消息
|
final cancelListener1 = bridge.on(FromFlutterAppEnum.translateResult.code, (data) {
|
||||||
|
print("监听器1收到翻译结果: ${data['data']}");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 注册第二个监听器
|
||||||
|
final cancelListener2 = bridge.on(FromFlutterAppEnum.translateResult.code, (data) {
|
||||||
|
print("监听器2收到翻译结果: ${data['data']}");
|
||||||
|
});
|
||||||
|
|
||||||
|
// 当不再需要第一个监听器时,调用其返回的函数
|
||||||
|
cancelListener1.call();// 仅移除监听器1
|
||||||
|
// 当不再需要第二个监听器时,调用其返回的函数
|
||||||
|
cancelListener2.call();// 仅移除监听器2
|
||||||
```
|
```
|
||||||
|
|
||||||
##### `off(String type)`
|
##### `off(String type)`
|
||||||
|
|
||||||
取消注册消息监听器。
|
取消注册某个 `type` 的【所有】消息监听器。此方法主要用于需要一次性清空所有同类型监听的场景。
|
||||||
|
|
||||||
- `type`: 要取消监听的消息类型
|
- `type`: 要取消监听的【所有】消息类型
|
||||||
|
|
||||||
```dart
|
```dart
|
||||||
|
// 警告:这将移除 FromFlutterAppEnum.translateResult.code 对应的所有监听器
|
||||||
bridge.off(FromFlutterAppEnum.translateResult.code);
|
bridge.off(FromFlutterAppEnum.translateResult.code);
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -178,14 +191,22 @@ bridge.share(
|
|||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
##### 翻译功能
|
##### 翻译功能(发送翻译消息+监听返回结果)
|
||||||
|
##### 只有当你调用 sendRequest 方法时,才会走队列
|
||||||
```dart
|
```dart
|
||||||
bridge.translateRequest({
|
bridge.sendRequest(
|
||||||
'text': 'Hello',
|
sendType: ToFlutterAppEnum.translateRequest, // 1. 发送类型
|
||||||
'from': 'en',
|
listenType: FromFlutterAppEnum.translateResult, // 2. 监听类型
|
||||||
'to': 'zh',
|
params: {
|
||||||
});
|
'text00001': 'text00001',
|
||||||
|
'text00002': 'text00002',
|
||||||
|
'text00003': 'text00003',
|
||||||
|
}, // 3. 参数
|
||||||
|
onSuccess: (data) {
|
||||||
|
// 4. 成功回调 (自动排队,安全)
|
||||||
|
print("翻译结果: $data");
|
||||||
|
},
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
##### 直播间相关
|
##### 直播间相关
|
||||||
@@ -274,27 +295,28 @@ Flutter 向 H5 发送的消息类型枚举。包含:
|
|||||||
```dart
|
```dart
|
||||||
import 'package:web_tools/web_tools.dart';
|
import 'package:web_tools/web_tools.dart';
|
||||||
|
|
||||||
|
//需要翻译的map
|
||||||
|
var textMap = <String, String>{};
|
||||||
|
|
||||||
void setupTranslation() {
|
void setupTranslation() {
|
||||||
final bridge = FlutterBridge.instance;
|
// 使用 sendRequest (发送请求并队列监听回调,内部自动释放监听回调),该方法只监听一次,
|
||||||
|
// 如果需要多次监听,请使用 FlutterBridge.instance.on,并且手动释放监听回调
|
||||||
|
FlutterBridge.instance.sendRequest(
|
||||||
|
sendType: ToFlutterAppEnum.translateRequest, // 1. 发送类型
|
||||||
|
listenType: FromFlutterAppEnum.translateResult, // 2. 监听类型
|
||||||
|
params: textMap, // 3. 参数
|
||||||
|
onSuccess: (data) {
|
||||||
|
// 4. 成功回调 (自动排队,安全)
|
||||||
|
print("翻译结果: $data");
|
||||||
|
|
||||||
// 注册翻译结果监听器
|
final Map<String, String> result =
|
||||||
bridge.on(FromFlutterAppEnum.translateResult.code, (data) {
|
data.map((key, value) => MapEntry(key, value.toString()));
|
||||||
final translatedText = data['data']['text'];
|
|
||||||
print('翻译结果: $translatedText');
|
|
||||||
// 更新 UI 显示翻译结果
|
|
||||||
});
|
|
||||||
|
|
||||||
// 发送翻译请求
|
textMap.clear();// 清空临时变量
|
||||||
bridge.translateRequest({
|
FlutterBridge.instance.textMap.addAll(translated);//全局变量追加翻译结果
|
||||||
'text': 'Hello World',
|
update(['your_id']);
|
||||||
'from': 'en',
|
},
|
||||||
'to': 'zh',
|
);
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void cleanup() {
|
|
||||||
// 清理监听器
|
|
||||||
FlutterBridge.instance.off(FromFlutterAppEnum.translateResult.code);
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -303,29 +325,51 @@ void cleanup() {
|
|||||||
```dart
|
```dart
|
||||||
import 'package:web_tools/web_tools.dart';
|
import 'package:web_tools/web_tools.dart';
|
||||||
|
|
||||||
void setupShare() {
|
class SantaLogic extends GetxController {
|
||||||
final bridge = FlutterBridge.instance;
|
|
||||||
|
|
||||||
// 注册分享完成监听器
|
// 1. 定义一个变量来持有取消函数
|
||||||
bridge.on(FromFlutterAppEnum.shareFinished.code, (data) {
|
VoidCallback? _cancelShareListener;
|
||||||
print('分享完成');
|
|
||||||
// 处理分享完成后的逻辑
|
|
||||||
});
|
|
||||||
|
|
||||||
// 触发分享
|
@override
|
||||||
bridge.share(
|
void onReady() {
|
||||||
|
super.onReady();
|
||||||
|
|
||||||
|
// 2. 注册监听(需要监听分享回调时)
|
||||||
|
// on 方法现在会返回一个取消函数,专门用来取消“这一个”监听
|
||||||
|
_cancelShareListener = FlutterBridge.instance.on(
|
||||||
|
FromFlutterAppEnum.shareFinished.code,
|
||||||
|
(data) {
|
||||||
|
print("SantaLogic: 收到分享成功的广播");
|
||||||
|
// 执行业务,比如刷新任务列表
|
||||||
|
requestTaskInfo();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// 1. 触发分享(有些分享不需要监听,只需要触发分享)
|
||||||
|
FlutterBridge.instance.share(
|
||||||
activityId: 'activity_123',
|
activityId: 'activity_123',
|
||||||
needShareReport: true,
|
needShareReport: true,
|
||||||
extraParams: {'source': 'web'},
|
extraParams: {'source': 'web'},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void onClose() {
|
||||||
|
// 3. ⚠️⚠️⚠️ 如果注册了监听,必须在页面销毁时调用取消函数
|
||||||
|
// 如果不调用,SantaLogic 即使退出了,这个闭包还在 Bridge 里,
|
||||||
|
// 下次分享成功时,代码还会跑,且 SantaLogic 无法被垃圾回收!
|
||||||
|
_cancelShareListener?.call();
|
||||||
|
|
||||||
|
super.onClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 注意事项
|
## 注意事项
|
||||||
|
|
||||||
1. **单例模式**:`FlutterBridge` 使用单例模式,在整个应用中只有一个实例。
|
1. **单例模式**:`FlutterBridge` 使用单例模式,在整个应用中只有一个实例。
|
||||||
|
|
||||||
2. **监听器管理**:使用 `on()` 注册的监听器在使用完毕后应该使用 `off()` 取消注册,避免内存泄漏。
|
2. **监听器管理**:使用 `on()` 注册的监听器在使用完毕后必须使用`_listener1.call()` 取消注册,避免内存泄漏, `off()` 慎用,这将移除对应type的所有监听器。`sendRequest()`方法是融合了发送消息+监听回调,不用手动取消监听,内部自动清理监听。并且只监听一次,防止多次回调。
|
||||||
|
|
||||||
3. **消息格式**:所有消息都遵循 `{'type': '消息类型', 'data': {...}}` 的格式。
|
3. **消息格式**:所有消息都遵循 `{'type': '消息类型', 'data': {...}}` 的格式。
|
||||||
|
|
||||||
|
|||||||
695
lib/example_page.dart
Normal file
695
lib/example_page.dart
Normal file
@@ -0,0 +1,695 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:web_tools/web_tools.dart';
|
||||||
|
|
||||||
|
class WebToolsExamplePage extends StatefulWidget {
|
||||||
|
const WebToolsExamplePage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<WebToolsExamplePage> createState() => _WebToolsExamplePageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _WebToolsExamplePageState extends State<WebToolsExamplePage> {
|
||||||
|
final FlutterBridge bridge = FlutterBridge.instance;
|
||||||
|
|
||||||
|
// 控制器用于输入参数
|
||||||
|
final TextEditingController gameIdController = TextEditingController()
|
||||||
|
..text = '311';
|
||||||
|
final TextEditingController userIdController = TextEditingController()
|
||||||
|
..text = 'f894609cbab2440c8b2c5161049ec881';
|
||||||
|
final TextEditingController googleProductIdController =
|
||||||
|
TextEditingController()..text = '70055';
|
||||||
|
final TextEditingController iosProductIdController = TextEditingController();
|
||||||
|
final TextEditingController pathController = TextEditingController();
|
||||||
|
final TextEditingController titleController = TextEditingController();
|
||||||
|
final TextEditingController gameCodeController = TextEditingController()
|
||||||
|
..text = '310';
|
||||||
|
final TextEditingController activityIdController = TextEditingController()
|
||||||
|
..text = '83';
|
||||||
|
final TextEditingController schemeController = TextEditingController();
|
||||||
|
final TextEditingController linkUrlController = TextEditingController();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
gameIdController.dispose();
|
||||||
|
userIdController.dispose();
|
||||||
|
googleProductIdController.dispose();
|
||||||
|
iosProductIdController.dispose();
|
||||||
|
pathController.dispose();
|
||||||
|
titleController.dispose();
|
||||||
|
gameCodeController.dispose();
|
||||||
|
activityIdController.dispose();
|
||||||
|
schemeController.dispose();
|
||||||
|
linkUrlController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildMethodTile(
|
||||||
|
String title, String description, VoidCallback onPressed) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
subtitle: Text(description),
|
||||||
|
trailing: ElevatedButton(
|
||||||
|
onPressed: onPressed,
|
||||||
|
child: const Text('调用'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildMethodWithInputTile(
|
||||||
|
String title,
|
||||||
|
String description,
|
||||||
|
TextEditingController controller,
|
||||||
|
String hint,
|
||||||
|
VoidCallback onPressed,
|
||||||
|
) {
|
||||||
|
return ListTile(
|
||||||
|
title: Text(title, style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Text(description),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
controller: controller,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
hintText: hint,
|
||||||
|
border: const OutlineInputBorder(),
|
||||||
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
trailing: ElevatedButton(
|
||||||
|
onPressed: onPressed,
|
||||||
|
child: const Text('调用'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Web Tools 交互方法示例'),
|
||||||
|
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
|
||||||
|
),
|
||||||
|
body: ListView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
// 游戏相关模块
|
||||||
|
ExpansionTile(
|
||||||
|
title: const Text('游戏相关方法'),
|
||||||
|
children: [
|
||||||
|
_buildMethodTile(
|
||||||
|
'关闭页面',
|
||||||
|
'关闭当前网页',
|
||||||
|
() => bridge.close(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'关闭游戏页面',
|
||||||
|
'关闭游戏页面左上角返回',
|
||||||
|
() => bridge.sendToFlutter(ToFlutterAppEnum.gameExit.code, {}),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'游戏结束',
|
||||||
|
'通知原生游戏已结束',
|
||||||
|
() => bridge.gameOver(),
|
||||||
|
),
|
||||||
|
_buildMethodWithInputTile(
|
||||||
|
'创建游戏',
|
||||||
|
'创建指定ID的游戏',
|
||||||
|
gameIdController,
|
||||||
|
'输入游戏ID',
|
||||||
|
() {
|
||||||
|
if (gameIdController.text.isNotEmpty) {
|
||||||
|
bridge.createGame(gameIdController.text);
|
||||||
|
} else {
|
||||||
|
bridge.createGame("311");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildMethodWithInputTile(
|
||||||
|
'想玩游戏',
|
||||||
|
'表达想玩指定游戏的意愿',
|
||||||
|
gameIdController,
|
||||||
|
'输入游戏ID',
|
||||||
|
() {
|
||||||
|
if (gameIdController.text.isNotEmpty) {
|
||||||
|
bridge.wantToPlay(gameIdController.text);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildMethodWithInputTile(
|
||||||
|
'跳转到游戏详情页',
|
||||||
|
'不同的值代表不同的游戏',
|
||||||
|
gameIdController,
|
||||||
|
'gameType',
|
||||||
|
() {
|
||||||
|
if (gameIdController.text.isNotEmpty) {
|
||||||
|
bridge.sendToFlutter(ToFlutterAppEnum.gameType.code,
|
||||||
|
{"gameType": gameIdController.text});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildMethodWithInputTile(
|
||||||
|
'检查游戏状态',
|
||||||
|
'检查指定游戏的状态',
|
||||||
|
gameCodeController,
|
||||||
|
'输入游戏代码',
|
||||||
|
() {
|
||||||
|
if (gameCodeController.text.isNotEmpty) {
|
||||||
|
bridge.checkGameState(gameCodeController.text);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
// 充值支付模块
|
||||||
|
ExpansionTile(
|
||||||
|
title: const Text('充值支付方法'),
|
||||||
|
children: [
|
||||||
|
_buildMethodTile(
|
||||||
|
'前往充值',
|
||||||
|
'跳转到充值页面',
|
||||||
|
() => bridge.toRecharge(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'前往红钻充值',
|
||||||
|
'跳转到红钻充值页面',
|
||||||
|
() => bridge.toRedDiamond(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'定向充值',
|
||||||
|
'直接唤起内购付费框',
|
||||||
|
() => bridge.sendToFlutter(
|
||||||
|
ToFlutterAppEnum.rechargeItem.code, {"productId": "70055"}),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: const Text('月卡支付',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text('跳转到月卡支付页面'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
controller: googleProductIdController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: 'Google产品ID',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
contentPadding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 12, vertical: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(
|
||||||
|
child: TextField(
|
||||||
|
controller: iosProductIdController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: 'iOS产品ID',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
contentPadding: EdgeInsets.symmetric(
|
||||||
|
horizontal: 12, vertical: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
controller: userIdController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: '其他用户ID (可选)',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
trailing: ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (googleProductIdController.text.isNotEmpty &&
|
||||||
|
iosProductIdController.text.isNotEmpty) {
|
||||||
|
bridge.toMonthCardPay(
|
||||||
|
googleProductIdController.text,
|
||||||
|
iosProductIdController.text,
|
||||||
|
otherUserId: userIdController.text,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Text('调用'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
// 页面导航模块
|
||||||
|
ExpansionTile(
|
||||||
|
title: const Text('页面导航方法'),
|
||||||
|
children: [
|
||||||
|
ListTile(
|
||||||
|
title: const Text('跳转到H5页面',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text('在原生App中跳转到H5页面'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
controller: pathController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: '页面路径',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
controller: titleController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: '页面标题',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
trailing: ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (pathController.text.isNotEmpty &&
|
||||||
|
titleController.text.isNotEmpty) {
|
||||||
|
bridge.jumpToH5(
|
||||||
|
pathController.text, titleController.text);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Text('调用'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildMethodWithInputTile(
|
||||||
|
'前往用户主页',
|
||||||
|
'跳转到指定用户的主页',
|
||||||
|
userIdController,
|
||||||
|
'输入用户ID',
|
||||||
|
() {
|
||||||
|
if (userIdController.text.isNotEmpty) {
|
||||||
|
bridge.toHomepage(userIdController.text);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildMethodTile("恩爱节活动", "恩爱节活动跳转选择好友 1:cp 2:告白 3:组队", () {
|
||||||
|
bridge.sendToFlutter(
|
||||||
|
ToFlutterAppEnum.lovingDayChooseFriend.code, {});
|
||||||
|
}),
|
||||||
|
ListTile(
|
||||||
|
title: const Text('跳转到网页',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text('在原生App中打开网页'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
controller: linkUrlController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: '网页链接',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
controller: activityIdController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: '活动ID',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
trailing: ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (linkUrlController.text.isNotEmpty &&
|
||||||
|
activityIdController.text.isNotEmpty) {
|
||||||
|
bridge.taskToWebViewPage(
|
||||||
|
linkUrl: linkUrlController.text,
|
||||||
|
activityId: activityIdController.text,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Text('调用'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: const Text('解析URL并跳转',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text('解析scheme URL并跳转'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
controller: schemeController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: 'Scheme URL',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
TextField(
|
||||||
|
controller: activityIdController,
|
||||||
|
decoration: const InputDecoration(
|
||||||
|
hintText: '活动ID',
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
contentPadding:
|
||||||
|
EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
trailing: ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (schemeController.text.isNotEmpty &&
|
||||||
|
activityIdController.text.isNotEmpty) {
|
||||||
|
bridge.taskCommandJump(
|
||||||
|
scheme: schemeController.text,
|
||||||
|
activityId: activityIdController.text,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Text('调用'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
// 分享功能模块
|
||||||
|
ExpansionTile(
|
||||||
|
title: const Text('分享功能'),
|
||||||
|
children: [
|
||||||
|
_buildMethodWithInputTile(
|
||||||
|
'分享(通用分享)',
|
||||||
|
'分享活动,需要活动ID',
|
||||||
|
activityIdController,
|
||||||
|
'输入活动ID',
|
||||||
|
() {
|
||||||
|
if (activityIdController.text.isNotEmpty) {
|
||||||
|
bridge.share(
|
||||||
|
activityId: activityIdController.text,
|
||||||
|
needShareReport: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
_buildMethodWithInputTile(
|
||||||
|
'分享(带卡片分享)',
|
||||||
|
'分享活动,需要活动ID',
|
||||||
|
activityIdController,
|
||||||
|
'输入活动ID',
|
||||||
|
() {
|
||||||
|
if (activityIdController.text.isNotEmpty) {
|
||||||
|
bridge.share(
|
||||||
|
activityId: activityIdController.text,
|
||||||
|
needShareReport: true,
|
||||||
|
shareCardModel: ShareCardModel(
|
||||||
|
activityIcon:
|
||||||
|
"app/img/local/new_year_share_content_bg_en_th.webp",
|
||||||
|
activityDesc: "这只是一个示例"));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
// 直播互动模块
|
||||||
|
ExpansionTile(
|
||||||
|
title: const Text('直播互动方法'),
|
||||||
|
children: [
|
||||||
|
_buildMethodTile(
|
||||||
|
'检查开播',
|
||||||
|
'检查是否可以开始直播',
|
||||||
|
() => bridge.checkStartBroadcaster(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'直播间发言任务',
|
||||||
|
'触发直播间发言任务',
|
||||||
|
() => bridge.taskLiveRoomChat(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'直播间送礼任务',
|
||||||
|
'触发直播间送礼任务',
|
||||||
|
() => bridge.taskLiveRoomGift(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'直播间其他任务',
|
||||||
|
'触发直播间其他任务',
|
||||||
|
() => bridge.taskLiveRoomOther(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
// 任务系统模块
|
||||||
|
ExpansionTile(
|
||||||
|
title: const Text('任务系统方法'),
|
||||||
|
children: [
|
||||||
|
_buildMethodTile(
|
||||||
|
'完善个人信息',
|
||||||
|
'跳转到完善个人信息页面',
|
||||||
|
() => bridge.shouldCompleteProfile(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'观看时长任务',
|
||||||
|
'触发观看时长任务',
|
||||||
|
() => bridge.shouldWatchDuration(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'发送公屏消息任务',
|
||||||
|
'触发发送公屏消息任务',
|
||||||
|
() => bridge.shouldSendPublicMessage(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'上麦互动任务',
|
||||||
|
'触发上麦互动任务',
|
||||||
|
() => bridge.shouldMicInteraction(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'发送私聊消息任务',
|
||||||
|
'触发发送私聊消息任务',
|
||||||
|
() => bridge.shouldSendPrivateMessage(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'发布动态任务',
|
||||||
|
'触发发布动态任务',
|
||||||
|
() => bridge.shouldPostFeed(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'分享房间任务',
|
||||||
|
'触发分享房间任务',
|
||||||
|
() => bridge.shouldShareRoom(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'佩戴装扮任务',
|
||||||
|
'触发佩戴装扮任务',
|
||||||
|
() => bridge.shouldWearDecoration(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'前往语音房任务',
|
||||||
|
'触发前往语音房任务',
|
||||||
|
() => bridge.shouldGoToVoiceRoom(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'观看直播',
|
||||||
|
'触发前往语音房任务有-》 收藏房间 | 关注主播 | 发送房间消息 ',
|
||||||
|
() => bridge.commonInteraction({
|
||||||
|
"interactionType": WebInteractionType.taskKeyWatchLive.code,
|
||||||
|
// taskKeyCollectRoom | taskKeyFollowUser | taskKeySendRoomMessage
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'赠送礼物',
|
||||||
|
'触发前往语音房任务 && (打开礼物面板 or 赠送指定礼物 or 打开我的背包) ',
|
||||||
|
() => bridge.commonInteraction({
|
||||||
|
"interactionType": WebInteractionType.taskKeySendGift
|
||||||
|
.code, // taskKeySendGiftId | taskKeySendBackpackGift
|
||||||
|
...{"targetId": ""}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'我想玩 XX游戏',
|
||||||
|
'触发前往语音房任务 && 发送我想玩 XX游戏',
|
||||||
|
() => bridge.commonInteraction({
|
||||||
|
"interactionType": WebInteractionType
|
||||||
|
.taskKeyPlayGame.code, // taskKeySendGiftId
|
||||||
|
...{"targetId": ""}
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'发红包',
|
||||||
|
'触发前往语音房任务 && 发红包',
|
||||||
|
() => bridge.commonInteraction({
|
||||||
|
"interactionType": "TaskKeySendRedPack",
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'发送私聊消息',
|
||||||
|
'聊天 Tab ',
|
||||||
|
() => bridge.commonInteraction({
|
||||||
|
"interactionType": WebInteractionType
|
||||||
|
.taskKeySendPrivateMessage.code, // taskKeySendGiftId
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'参与动态话题',
|
||||||
|
'比传参数 topicTitle',
|
||||||
|
() => bridge.commonInteraction({
|
||||||
|
"interactionType":
|
||||||
|
WebInteractionType.taskKeyTimelineTopic.code,
|
||||||
|
...{
|
||||||
|
"topicTitle": "",
|
||||||
|
"targetId": "",
|
||||||
|
"content": "",
|
||||||
|
"image": "",
|
||||||
|
"textId": ""
|
||||||
|
}
|
||||||
|
// taskKeySendGiftId
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'点赞动态 or 评论动态',
|
||||||
|
'切换 圈子 Tab',
|
||||||
|
() => bridge.commonInteraction({
|
||||||
|
"interactionType": WebInteractionType
|
||||||
|
.taskKeyLikeTimeline.code, // taskKeyReplayTimeline
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'签到',
|
||||||
|
'首页签到弹框',
|
||||||
|
() => bridge.commonInteraction({
|
||||||
|
"interactionType": WebInteractionType
|
||||||
|
.taskKeySignIn.code, // taskKeyReplayTimeline
|
||||||
|
|
||||||
|
// taskKeySendGiftId
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'其他页面',
|
||||||
|
'进入其他h5页面',
|
||||||
|
() => bridge.commonInteraction({
|
||||||
|
"interactionType": WebInteractionType
|
||||||
|
.taskKeyOtherPage.code, // taskKeyReplayTimeline
|
||||||
|
|
||||||
|
// taskKeySendGiftId
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'我的钱包页面',
|
||||||
|
'我的钱包页面',
|
||||||
|
() => bridge.commonInteraction({
|
||||||
|
"interactionType": WebInteractionType
|
||||||
|
.taskKeyMineWallet.code, // taskKeyReplayTimeline
|
||||||
|
|
||||||
|
// taskKeySendGiftId
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'我的背包',
|
||||||
|
'我的装扮背包页面(或者是称号)',
|
||||||
|
() => bridge.commonInteraction({
|
||||||
|
"interactionType": WebInteractionType
|
||||||
|
.taskKeyMineBackpack.code, // taskKeyReplayTimeline
|
||||||
|
|
||||||
|
// taskKeySendGiftId
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
|
||||||
|
// 其他功能模块
|
||||||
|
ExpansionTile(
|
||||||
|
title: const Text('其他功能方法'),
|
||||||
|
children: [
|
||||||
|
_buildMethodTile(
|
||||||
|
'专属见面礼',
|
||||||
|
'触发专属见面礼任务',
|
||||||
|
() => bridge.taskInviteCodeGift(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'跳转从业者申请',
|
||||||
|
'跳转到从业者申请页面',
|
||||||
|
() => bridge.toApplyAdmissionPage(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'直播预约设置',
|
||||||
|
'打开直播预约设置',
|
||||||
|
() => bridge.shouLiveBookingPicker(),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'观时礼刷新宝箱数据',
|
||||||
|
'观时礼刷新宝箱数据',
|
||||||
|
() =>
|
||||||
|
bridge.sendToFlutter(ToFlutterAppEnum.closeObserving.code, {
|
||||||
|
"updateType": "1" // 1 刷新整个挂件数据,2刷新观时礼的宝箱列表数据
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
_buildMethodTile(
|
||||||
|
'翻译',
|
||||||
|
'多语言翻译',
|
||||||
|
() => bridge.sendRequest(
|
||||||
|
sendType: ToFlutterAppEnum.translateRequest,
|
||||||
|
listenType: FromFlutterAppEnum.translateResult,
|
||||||
|
params: {"text0001": "text0001"},
|
||||||
|
onSuccess: (dic) {}),
|
||||||
|
),
|
||||||
|
ListTile(
|
||||||
|
title: const Text('通用交互',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.bold)),
|
||||||
|
subtitle: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const Text('通用交互方法,可以传递自定义参数'),
|
||||||
|
const SizedBox(height: 8),
|
||||||
|
Container(
|
||||||
|
padding: const EdgeInsets.all(12),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: Colors.grey),
|
||||||
|
borderRadius: BorderRadius.circular(8),
|
||||||
|
),
|
||||||
|
child: const Text(
|
||||||
|
'示例参数:{"action": "custom_action", "data": "value"}',
|
||||||
|
style: TextStyle(fontSize: 12, color: Colors.grey),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
trailing: ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
bridge.commonInteraction({
|
||||||
|
"action": "custom_action",
|
||||||
|
"data": "example_value",
|
||||||
|
"timestamp": DateTime.now().toIso8601String(),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
child: const Text('调用'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,10 +3,13 @@ import 'dart:convert';
|
|||||||
import 'dart:html' as html;
|
import 'dart:html' as html;
|
||||||
|
|
||||||
import 'package:js/js.dart';
|
import 'package:js/js.dart';
|
||||||
|
import 'package:web_tools/utils/model/model.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
// h5交互通知 原生 {type:'enum',data:'所需参数,可无'}
|
// h5交互通知 原生 {type:'enum',data:'所需参数,可无'}
|
||||||
enum ToFlutterAppEnum {
|
enum ToFlutterAppEnum {
|
||||||
close('close'),
|
close('close'),
|
||||||
|
reload('reload'), // 刷新
|
||||||
gameExit('gameExit'),
|
gameExit('gameExit'),
|
||||||
createGame('createGame'),
|
createGame('createGame'),
|
||||||
wantToPlay('wantToPlay'),
|
wantToPlay('wantToPlay'),
|
||||||
@@ -56,8 +59,21 @@ enum ToFlutterAppEnum {
|
|||||||
//前往语音房
|
//前往语音房
|
||||||
shouldGoToVoiceRoom('shouldGoToVoiceRoom'),
|
shouldGoToVoiceRoom('shouldGoToVoiceRoom'),
|
||||||
|
|
||||||
|
// 恩爱节活动跳转选择好友
|
||||||
|
lovingDayChooseFriend('lovingDayChooseFriend'),
|
||||||
|
// 恩爱节我的邀请页面
|
||||||
|
lovingDayMyInvite('lovingDayMyInvite'),
|
||||||
|
// 常驻cp活动规则页面
|
||||||
|
taskKeyToCPRule('taskKeyToCPRule'),
|
||||||
|
|
||||||
// 定向充值
|
// 定向充值
|
||||||
rechargeItem('rechargeItem'),
|
rechargeItem('rechargeItem'),
|
||||||
|
//专属见面礼
|
||||||
|
shouldInviteCodeGift('InviteCodeGiftPackageDialogPage'),
|
||||||
|
//解析 URL 并跳转
|
||||||
|
showCommandJump('commandJump'),
|
||||||
|
//跳转至网页
|
||||||
|
showToWebViewPage('toWebViewPage'),
|
||||||
|
|
||||||
///通用交互 别往这下面加,👆🏻加
|
///通用交互 别往这下面加,👆🏻加
|
||||||
commonInteraction('commonInteraction'),
|
commonInteraction('commonInteraction'),
|
||||||
@@ -73,6 +89,7 @@ enum ToFlutterAppEnum {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//通用交互
|
//通用交互
|
||||||
enum WebInteractionType {
|
enum WebInteractionType {
|
||||||
taskKeyWatchLive('TaskKeyWatchLive'), // 观看直播
|
taskKeyWatchLive('TaskKeyWatchLive'), // 观看直播
|
||||||
@@ -97,12 +114,9 @@ enum WebInteractionType {
|
|||||||
taskKeyOtherPage('TaskKeyOtherPage'), // 其他页面
|
taskKeyOtherPage('TaskKeyOtherPage'), // 其他页面
|
||||||
taskKeyMineBackpack('TaskKeyMineBackpack'), // 我的装扮背包页面(或者是称号)
|
taskKeyMineBackpack('TaskKeyMineBackpack'), // 我的装扮背包页面(或者是称号)
|
||||||
taskKeyMineWallet('TaskKeyMineWallet'), // 我的钱包页面
|
taskKeyMineWallet('TaskKeyMineWallet'), // 我的钱包页面
|
||||||
unknown('unknown'),
|
unknown('unknown');
|
||||||
;
|
|
||||||
|
|
||||||
const WebInteractionType(
|
const WebInteractionType(this.code);
|
||||||
this.code,
|
|
||||||
);
|
|
||||||
final String code; //服务端 key
|
final String code; //服务端 key
|
||||||
|
|
||||||
factory WebInteractionType.fromCode(String? code) => values.firstWhere(
|
factory WebInteractionType.fromCode(String? code) => values.firstWhere(
|
||||||
@@ -116,6 +130,9 @@ enum FromFlutterAppEnum {
|
|||||||
translateResult('translateResult'), //翻译
|
translateResult('translateResult'), //翻译
|
||||||
redDiamondRecharge('diamond_recharge'), //钻石充值
|
redDiamondRecharge('diamond_recharge'), //钻石充值
|
||||||
shareFinished('share_finished'), //分享完成
|
shareFinished('share_finished'), //分享完成
|
||||||
|
//送戒指后回调,传入type参数
|
||||||
|
// type: 1 cp戒指赠送成功,2恩爱节告白礼物赠送成功
|
||||||
|
cpSendRingCallBack('cp_send_ring_callback'),
|
||||||
defaultCode('');
|
defaultCode('');
|
||||||
|
|
||||||
const FromFlutterAppEnum(this.code);
|
const FromFlutterAppEnum(this.code);
|
||||||
@@ -125,22 +142,29 @@ enum FromFlutterAppEnum {
|
|||||||
@JS()
|
@JS()
|
||||||
external void sendMessageToNative(String data);
|
external void sendMessageToNative(String data);
|
||||||
|
|
||||||
|
/// 🆕 [新增] 通用请求任务模型
|
||||||
|
class _RequestTask {
|
||||||
|
final String sendType; // 发送给 Native 的类型
|
||||||
|
final Map<String, dynamic> params; // 发送的参数
|
||||||
|
final Function(Map<String, dynamic>) callback; // 成功回调
|
||||||
|
|
||||||
|
_RequestTask(this.sendType, this.params, this.callback);
|
||||||
|
}
|
||||||
|
|
||||||
class FlutterBridge {
|
class FlutterBridge {
|
||||||
static final FlutterBridge instance = FlutterBridge._internal();
|
static final FlutterBridge instance = FlutterBridge._internal();
|
||||||
|
|
||||||
/**
|
// final _messageListeners = <String, Function(Map<String, dynamic>)>{};
|
||||||
* 在需要处理 WebView 消息的地方注册监听器:
|
// 1. 修改这里:Value 从 Function 变成 List<Function>
|
||||||
* FlutterBridge.instance.on(FromJsEnum.translateResult.code, (data) {
|
final _messageListeners = <String, List<Function(Map<String, dynamic>)>>{};
|
||||||
print("收到来自 WebView 的消息: $data");
|
|
||||||
// 执行你需要的逻辑
|
|
||||||
});
|
|
||||||
使用过需要释放
|
|
||||||
// 取消监听 'translateResult' 类型的消息
|
|
||||||
FlutterBridge.instance.off(FromJsEnum.translateResult.code);
|
|
||||||
* */
|
|
||||||
final _messageListeners = <String, Function(Map<String, dynamic>)>{};
|
|
||||||
var textMap = <String, String>{}; //多语言翻译
|
var textMap = <String, String>{}; //多语言翻译
|
||||||
|
|
||||||
|
/// 🆕 [新增] 通用请求队列池 (Key: listenType, Value: 任务列表)
|
||||||
|
final Map<String, List<_RequestTask>> _requestQueues = {};
|
||||||
|
|
||||||
|
/// 🆕 [新增] 忙碌状态池 (记录哪些 listenType 当前正在等待 Native 回复)
|
||||||
|
final Set<String> _activeResponseTypes = {};
|
||||||
|
|
||||||
FlutterBridge._internal() {
|
FlutterBridge._internal() {
|
||||||
_initListener();
|
_initListener();
|
||||||
}
|
}
|
||||||
@@ -152,8 +176,19 @@ class FlutterBridge {
|
|||||||
try {
|
try {
|
||||||
final Map<String, dynamic> data = jsonDecode(event.data);
|
final Map<String, dynamic> data = jsonDecode(event.data);
|
||||||
final String? type = data['type'];
|
final String? type = data['type'];
|
||||||
|
// 找到监听列表,遍历调用
|
||||||
if (type != null && _messageListeners.containsKey(type)) {
|
if (type != null && _messageListeners.containsKey(type)) {
|
||||||
_messageListeners[type]?.call(data);
|
final listeners = _messageListeners[type];
|
||||||
|
if (listeners != null && listeners.isNotEmpty) {
|
||||||
|
// 使用 List.from 浅拷贝一份进行遍历,防止在回调中移除监听导致并发修改异常
|
||||||
|
for (final callback in List.from(listeners)) {
|
||||||
|
try {
|
||||||
|
callback(data); // 这里的 data 是已经解析好的 Map
|
||||||
|
} catch (e) {
|
||||||
|
print("Error in listener callback: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
print('Invalid message format from Flutter: $type');
|
print('Invalid message format from Flutter: $type');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -162,8 +197,19 @@ class FlutterBridge {
|
|||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
final String? type = event.data['type'];
|
final String? type = event.data['type'];
|
||||||
|
// 找到监听列表,遍历调用
|
||||||
if (type != null && _messageListeners.containsKey(type)) {
|
if (type != null && _messageListeners.containsKey(type)) {
|
||||||
_messageListeners[type]?.call(event.data);
|
final listeners = _messageListeners[type];
|
||||||
|
if (listeners != null && listeners.isNotEmpty) {
|
||||||
|
// 使用 List.from 浅拷贝一份进行遍历,防止在回调中移除监听导致并发修改异常
|
||||||
|
for (final callback in List.from(listeners)) {
|
||||||
|
try {
|
||||||
|
callback(event.data); // 这里的 data 是已经解析好的 Map
|
||||||
|
} catch (e) {
|
||||||
|
print("Error in listener callback: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
print('Invalid message format from Flutter: $type');
|
print('Invalid message format from Flutter: $type');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -173,24 +219,113 @@ class FlutterBridge {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注册监听某个 type 的消息
|
/// 3. 核心修改:on 方法
|
||||||
void on(String type, Function(Map<String, dynamic>) callback) {
|
/// 返回一个 VoidCallback,调用它即可取消本次监听
|
||||||
if (_messageListeners.containsKey(type)) {
|
VoidCallback on(String type, Function(Map<String, dynamic>) callback) {
|
||||||
print("Listener for '$type' is already registered.");
|
if (!_messageListeners.containsKey(type)) {
|
||||||
return; // 如果已经注册了这个类型的监听器,就不再重复添加
|
_messageListeners[type] = [];
|
||||||
}
|
|
||||||
_messageListeners[type] = callback;
|
|
||||||
print("Listener for '$type' has been registered.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 取消监听某个 type 的消息
|
_messageListeners[type]!.add(callback);
|
||||||
void off(String type) {
|
print(
|
||||||
if (!_messageListeners.containsKey(type)) {
|
"Listener added for '$type'. Total listeners: ${_messageListeners[type]!.length}",
|
||||||
print("No listener found for '$type'.");
|
);
|
||||||
return; // 如果没有找到监听器,直接返回
|
|
||||||
}
|
// 返回一个闭包,用于精确移除当前的 callback
|
||||||
|
return () {
|
||||||
|
if (_messageListeners.containsKey(type)) {
|
||||||
|
_messageListeners[type]?.remove(callback);
|
||||||
|
print(
|
||||||
|
"One listener removed for '$type'. Remaining: ${_messageListeners[type]?.length}",
|
||||||
|
);
|
||||||
|
|
||||||
|
// 如果该类型没有监听者了,可以选择清理 key
|
||||||
|
if (_messageListeners[type]!.isEmpty) {
|
||||||
_messageListeners.remove(type);
|
_messageListeners.remove(type);
|
||||||
print("Listener for '$type' has been removed.");
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 警告:这会移除该类型下的【所有】监听器 (慎用,通常用于全局重置,一旦调用,该 type 别的地方监听器将失效)
|
||||||
|
void off(String type) {
|
||||||
|
if (_messageListeners.containsKey(type)) {
|
||||||
|
_messageListeners.remove(type);
|
||||||
|
print("All listeners for '$type' have been removed.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// =========================================================
|
||||||
|
// 👇 🆕 [新增] 核心通用请求方法 (替代了旧的 requestTranslate)
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
/// 发送请求并等待回调 (自动排队,防并发)
|
||||||
|
/// [sendType] 发送给 Native 的类型
|
||||||
|
/// [listenType] 等待 Native 回复的类型
|
||||||
|
/// [params] 参数
|
||||||
|
/// [onSuccess] 成功回调
|
||||||
|
void sendRequest({
|
||||||
|
required ToFlutterAppEnum sendType,
|
||||||
|
required FromFlutterAppEnum listenType,
|
||||||
|
required Map<String, dynamic> params,
|
||||||
|
required Function(Map<String, dynamic>) onSuccess,
|
||||||
|
}) {
|
||||||
|
final responseKey = listenType.code;
|
||||||
|
|
||||||
|
// 1. 初始化该类型的队列
|
||||||
|
if (!_requestQueues.containsKey(responseKey)) {
|
||||||
|
_requestQueues[responseKey] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 入队
|
||||||
|
_requestQueues[responseKey]!.add(_RequestTask(
|
||||||
|
sendType.code,
|
||||||
|
params,
|
||||||
|
onSuccess,
|
||||||
|
));
|
||||||
|
|
||||||
|
// 3. 调度执行
|
||||||
|
_processRequestQueue(responseKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 🆕 [新增] 内部队列调度方法
|
||||||
|
void _processRequestQueue(String responseKey) {
|
||||||
|
// 如果该类型正在忙,或者队列空了,直接返回
|
||||||
|
if (_activeResponseTypes.contains(responseKey) ||
|
||||||
|
(_requestQueues[responseKey]?.isEmpty ?? true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 标记忙碌
|
||||||
|
_activeResponseTypes.add(responseKey);
|
||||||
|
|
||||||
|
// 2. 取出队首任务
|
||||||
|
final task = _requestQueues[responseKey]!.removeAt(0);
|
||||||
|
|
||||||
|
// 3. 注册临时监听 (使用 on 的多播特性)
|
||||||
|
VoidCallback? cancelRef;
|
||||||
|
|
||||||
|
cancelRef = on(responseKey, (data) {
|
||||||
|
try {
|
||||||
|
final innerData = data['data'] ?? {};
|
||||||
|
// 执行业务回调
|
||||||
|
task.callback(innerData);
|
||||||
|
} catch (e) {
|
||||||
|
print("Queue processing error: $e");
|
||||||
|
} finally {
|
||||||
|
// --- 任务闭环 ---
|
||||||
|
// A. 移除当前临时监听 (不影响全局监听)
|
||||||
|
cancelRef?.call();
|
||||||
|
|
||||||
|
// B. 解除忙碌状态
|
||||||
|
_activeResponseTypes.remove(responseKey);
|
||||||
|
|
||||||
|
// C. 递归处理下一个任务
|
||||||
|
_processRequestQueue(responseKey);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 4. 发送 Native 消息
|
||||||
|
sendToFlutter(task.sendType, task.params);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 发送消息给 App(通过 WebView 调用 JS 方法)
|
// 发送消息给 App(通过 WebView 调用 JS 方法)
|
||||||
@@ -220,37 +355,67 @@ class FlutterBridge {
|
|||||||
|
|
||||||
// 具体封装的常用方法,直接发送消息给 Flutter Web
|
// 具体封装的常用方法,直接发送消息给 Flutter Web
|
||||||
void close() => sendToFlutter(ToFlutterAppEnum.close.code, {});
|
void close() => sendToFlutter(ToFlutterAppEnum.close.code, {});
|
||||||
|
void reload() => sendToFlutter(ToFlutterAppEnum.reload.code, {});
|
||||||
void gameOver() => sendToFlutter(ToFlutterAppEnum.gameOver.code, {});
|
void gameOver() => sendToFlutter(ToFlutterAppEnum.gameOver.code, {});
|
||||||
void createGame(String gameId) =>
|
void createGame(String gameId) =>
|
||||||
sendToFlutter(ToFlutterAppEnum.createGame.code, {'gameId': gameId});
|
sendToFlutter(ToFlutterAppEnum.createGame.code, {'gameId': gameId});
|
||||||
void wantToPlay(String gameId) =>
|
void wantToPlay(String gameId) =>
|
||||||
sendToFlutter(ToFlutterAppEnum.wantToPlay.code, {'gameId': gameId});
|
sendToFlutter(ToFlutterAppEnum.wantToPlay.code, {'gameId': gameId});
|
||||||
void translateRequest(Map<String, String> data) =>
|
// void translateRequest(Map<String, String> data) =>
|
||||||
sendToFlutter(ToFlutterAppEnum.translateRequest.code, data);
|
// sendToFlutter(ToFlutterAppEnum.translateRequest.code, data);//移除,建议使用sendRequest方法
|
||||||
void toRecharge() => sendToFlutter(ToFlutterAppEnum.toRecharge.code, {});
|
void toRecharge() => sendToFlutter(ToFlutterAppEnum.toRecharge.code, {});
|
||||||
void toRedDiamond() => sendToFlutter(ToFlutterAppEnum.toRedDiamond.code, {});
|
void toRedDiamond() => sendToFlutter(ToFlutterAppEnum.toRedDiamond.code, {});
|
||||||
void jumpToH5(String path, String title) => sendToFlutter(
|
void jumpToH5(String path, String title) => sendToFlutter(
|
||||||
ToFlutterAppEnum.jumpToH5.code, {'path': path, 'title': title});
|
ToFlutterAppEnum.jumpToH5.code,
|
||||||
|
{'path': path, 'title': title},
|
||||||
|
);
|
||||||
void toHomepage(String userId) =>
|
void toHomepage(String userId) =>
|
||||||
sendToFlutter(ToFlutterAppEnum.toHomepage.code, {'userId': userId});
|
sendToFlutter(ToFlutterAppEnum.toHomepage.code, {'userId': userId});
|
||||||
void toMonthCardPay(String googleProductId, String iosProductId,
|
void toMonthCardPay(
|
||||||
{otherUserId = ''}) =>
|
String googleProductId,
|
||||||
|
String iosProductId, {
|
||||||
|
otherUserId = '',
|
||||||
|
}) =>
|
||||||
sendToFlutter(ToFlutterAppEnum.toMonthCardPay.code, {
|
sendToFlutter(ToFlutterAppEnum.toMonthCardPay.code, {
|
||||||
'googleProductId': googleProductId,
|
'googleProductId': googleProductId,
|
||||||
'iosProductId': iosProductId,
|
'iosProductId': iosProductId,
|
||||||
'otherUserId': otherUserId,
|
'otherUserId': otherUserId,
|
||||||
});
|
});
|
||||||
void checkGameState(String gameCode) => sendToFlutter(
|
void checkGameState(String gameCode) => sendToFlutter(
|
||||||
ToFlutterAppEnum.checkGameState.code, {'gameCode': gameCode});
|
ToFlutterAppEnum.checkGameState.code,
|
||||||
|
{'gameCode': gameCode},
|
||||||
|
);
|
||||||
|
|
||||||
void share({
|
void share({
|
||||||
required String activityId,
|
required String activityId, // 活动id
|
||||||
required bool needShareReport,
|
required bool needShareReport, // 是否需要上报分享数据
|
||||||
Map<String, dynamic> extraParams = const {},
|
ShareCardModel? shareCardModel, // 分享上方卡片 数据,参考客户端参数
|
||||||
}) =>
|
}) =>
|
||||||
sendToFlutter(ToFlutterAppEnum.share.code, {
|
sendToFlutter(ToFlutterAppEnum.share.code, {
|
||||||
"activityId": activityId,
|
"activityId": activityId,
|
||||||
"shareReportKey": needShareReport ? "ActivityShared" : "",
|
"shareReportKey": needShareReport ? "ActivityShared" : "",
|
||||||
...extraParams
|
...shareCardModel?.toJson() ?? {},
|
||||||
|
});
|
||||||
|
|
||||||
|
//专属见面礼
|
||||||
|
void taskInviteCodeGift() =>
|
||||||
|
sendToFlutter(ToFlutterAppEnum.shouldInviteCodeGift.code, {});
|
||||||
|
|
||||||
|
//解析 URL 并跳转
|
||||||
|
void taskCommandJump({required String scheme, required String activityId}) =>
|
||||||
|
sendToFlutter(ToFlutterAppEnum.showCommandJump.code, {
|
||||||
|
"scheme": scheme,
|
||||||
|
"activityId": activityId,
|
||||||
|
});
|
||||||
|
|
||||||
|
//跳转至网页
|
||||||
|
void taskToWebViewPage({
|
||||||
|
required String linkUrl,
|
||||||
|
required String activityId,
|
||||||
|
}) =>
|
||||||
|
sendToFlutter(ToFlutterAppEnum.showToWebViewPage.code, {
|
||||||
|
"linkUrl": linkUrl,
|
||||||
|
"activityId": activityId,
|
||||||
});
|
});
|
||||||
|
|
||||||
void checkStartBroadcaster() =>
|
void checkStartBroadcaster() =>
|
||||||
|
|||||||
6
lib/utils/config.dart
Normal file
6
lib/utils/config.dart
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
class WebConfig {
|
||||||
|
static const String iOSDownloadUrl =
|
||||||
|
"https://apps.apple.com/sg/app/sugarvibe/id6737873037";
|
||||||
|
static const String androidDownloadUrl =
|
||||||
|
"https://play.google.com/store/apps/details?id=com.sugarvibe.chat";
|
||||||
|
}
|
||||||
30
lib/utils/model/model.dart
Normal file
30
lib/utils/model/model.dart
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
class ShareCardModel {
|
||||||
|
/**
|
||||||
|
* 分享弹框 上方卡片模型
|
||||||
|
* */
|
||||||
|
|
||||||
|
final String activityIcon; //卡片中间的icon
|
||||||
|
final String activityDesc; // 卡片描述
|
||||||
|
final String themeTextColor; //主题色
|
||||||
|
final String activityBgIcon; //卡片背景icon
|
||||||
|
final String userNameSub; //用户名 下方文案
|
||||||
|
final String qrImageLink; //二维码链接
|
||||||
|
|
||||||
|
ShareCardModel({
|
||||||
|
required this.activityIcon,
|
||||||
|
required this.activityDesc,
|
||||||
|
this.qrImageLink = "", // 客户端已有能力获取,无需网页端传
|
||||||
|
this.userNameSub = "",
|
||||||
|
this.themeTextColor = "#FFE499",
|
||||||
|
this.activityBgIcon = "app/img/local/christmas_share_bg_th.webp",
|
||||||
|
});
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() => {
|
||||||
|
"activityIcon": activityIcon,
|
||||||
|
"activityDesc": activityDesc,
|
||||||
|
"themeTextColor": themeTextColor,
|
||||||
|
"activityBgIcon": activityBgIcon,
|
||||||
|
"userNameSub": userNameSub,
|
||||||
|
"activityUrl": qrImageLink,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
library;
|
library;
|
||||||
|
|
||||||
export 'package:web_tools/utils/app_bridge.dart';
|
export 'package:web_tools/utils/app_bridge.dart';
|
||||||
|
export 'package:web_tools/utils/config.dart';
|
||||||
|
export 'package:web_tools/utils/model/model.dart';
|
||||||
|
export 'package:web_tools/example_page.dart';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
name: web_tools
|
name: web_tools
|
||||||
description: "A new Flutter package project."
|
description: "A new Flutter package project."
|
||||||
version: 0.0.1
|
version: 0.0.5
|
||||||
publish_to: 'none' # 不发布到pub.dev
|
publish_to: 'none' # 不发布到pub.dev
|
||||||
homepage: https://gitea.sdws.shop/xim/web_tools.git
|
homepage: https://gitea.sdws.shop/xim/web_tools.git
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user