添加 HTML 资源路径到 pubspec.yaml;更新 Auth 页面以使用新的 Webview 组件加载本地 HTML 文件;重构 WebviewPage 以支持加载本地资产和注入 JavaScript 通信桥接;清理不必要的导入和注释。

This commit is contained in:
2026-07-08 12:03:25 +08:00
parent b21857f364
commit bd5937f910
6 changed files with 172 additions and 32 deletions
+48
View File
@@ -0,0 +1,48 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.aa {
margin: 100px;
width: 200px;
height: 100px;
border-radius: 20px;
}
</style>
</head>
<body>
<button class="aa" onclick="postMsgToAPP()">向 APP 发送数据</button>
</body>
<script>
(function () {
if (window.__appInstallData) {
return;
}
window.__appInstallData = true;
window.AppBridge.token = 'xxx';
window.AppBridge.playId = 'xxx';
///TODO 后续业务
})();
function postMsgToAPP() {
window.AppBridge.postMessage(JSON.stringify({ type: 'navigator_pop', value: null }))
console.log(window.AppBridge.token)
console.log(window.AppBridge.playId)
}
</script>
</html>
+5 -2
View File
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:recording_tool/app/router/app_navigator.dart'; import 'package:recording_tool/app/router/app_navigator.dart';
import 'package:recording_tool/features/scan_qrcode/pages/page_scan_qrcode.dart'; import 'package:recording_tool/shared/widgets/app_webview.dart';
import 'package:recording_tool/shared/widgets/widgets.dart'; import 'package:recording_tool/shared/widgets/widgets.dart';
class AuthPageWidget extends StatefulWidget { class AuthPageWidget extends StatefulWidget {
@@ -35,7 +35,10 @@ class _AuthPageWidgetState extends State<AuthPageWidget> {
child: AppButton( child: AppButton(
label: '确定', label: '确定',
onPressed: () { onPressed: () {
AppNavigator.push(const ScanQrCodePage()); // AppNavigator.push(const ScanQrCodePage());
AppNavigator.push(
const WebviewPage(assetPath: 'assets/html/index.html'),
);
}, },
variant: AppButtonVariant.secondary, variant: AppButtonVariant.secondary,
), ),
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/material.dart';
import 'package:apivideo_live_stream/apivideo_live_stream.dart'; import 'package:apivideo_live_stream/apivideo_live_stream.dart';
import 'package:flutter/material.dart';
import 'package:recording_tool/shared/widgets/app_bar.dart'; import 'package:recording_tool/shared/widgets/app_bar.dart';
class PushSteamTestWidget extends StatefulWidget { class PushSteamTestWidget extends StatefulWidget {
+16
View File
@@ -11,6 +11,16 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
class $AssetsHtmlGen {
const $AssetsHtmlGen();
/// File path: assets/html/index.html
String get index => 'assets/html/index.html';
/// List of all assets
List<String> get values => [index];
}
class $AssetsImagesGen { class $AssetsImagesGen {
const $AssetsImagesGen(); const $AssetsImagesGen();
@@ -30,18 +40,24 @@ class $AssetsImagesGen {
AssetGenImage get imageLogo => AssetGenImage get imageLogo =>
const AssetGenImage('assets/images/image_logo.png'); const AssetGenImage('assets/images/image_logo.png');
/// File path: assets/images/image_vs.png
AssetGenImage get imageVs =>
const AssetGenImage('assets/images/image_vs.png');
/// List of all assets /// List of all assets
List<AssetGenImage> get values => [ List<AssetGenImage> get values => [
imageCopy, imageCopy,
imageDelete, imageDelete,
imageDialogBg, imageDialogBg,
imageLogo, imageLogo,
imageVs,
]; ];
} }
class Assets { class Assets {
const Assets._(); const Assets._();
static const $AssetsHtmlGen html = $AssetsHtmlGen();
static const $AssetsImagesGen images = $AssetsImagesGen(); static const $AssetsImagesGen images = $AssetsImagesGen();
} }
+99 -27
View File
@@ -5,16 +5,19 @@ import 'package:webview_flutter_android/webview_flutter_android.dart';
import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart'; import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart';
class WebviewPage extends StatefulWidget { class WebviewPage extends StatefulWidget {
final String url; final String? url;
final String? title; final String? title;
final String? assetPath;
const WebviewPage({super.key, required this.url, this.title}); const WebviewPage({super.key, this.url, this.title, this.assetPath});
@override @override
State<WebviewPage> createState() => WebviewPageState(); State<WebviewPage> createState() => WebviewPageState();
} }
class WebviewPageState extends State<WebviewPage> { class WebviewPageState extends State<WebviewPage> {
static const String _javaScriptChannelName = 'AppBridge';
WebViewController? _controller; WebViewController? _controller;
bool _isLoading = true; bool _isLoading = true;
String? _errorMessage; String? _errorMessage;
@@ -26,11 +29,12 @@ class WebviewPageState extends State<WebviewPage> {
} }
Future<void> _initController() async { Future<void> _initController() async {
if (widget.url?.isEmpty ?? true) { if ((widget.url?.isEmpty ?? true) && widget.assetPath == null) {
return; return;
} }
final params = _createPlatformParams(); final params = _createPlatformParams();
final controller = WebViewController.fromPlatformCreationParams(params) late final WebViewController controller;
controller = WebViewController.fromPlatformCreationParams(params)
..setJavaScriptMode(JavaScriptMode.unrestricted) ..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(Colors.transparent) ..setBackgroundColor(Colors.transparent)
..setNavigationDelegate( ..setNavigationDelegate(
@@ -46,8 +50,9 @@ class WebviewPageState extends State<WebviewPage> {
_errorMessage = null; _errorMessage = null;
}); });
}, },
onPageFinished: (String url) { onPageFinished: (String url) async {
debugPrint('webview - page finished: $url'); debugPrint('webview - page finished: $url');
await _injectPostMessageBridge(controller);
if (!mounted) return; if (!mounted) return;
setState(() => _isLoading = false); setState(() => _isLoading = false);
}, },
@@ -72,17 +77,28 @@ class WebviewPageState extends State<WebviewPage> {
), ),
); );
controller.addJavaScriptChannel(
_javaScriptChannelName,
onMessageReceived: (JavaScriptMessage message) {
_onMessageReceived(message);
},
);
if (controller.platform is AndroidWebViewController) { if (controller.platform is AndroidWebViewController) {
AndroidWebViewController.enableDebugging(kDebugMode); AndroidWebViewController.enableDebugging(kDebugMode);
await (controller.platform as AndroidWebViewController) await (controller.platform as AndroidWebViewController)
.setMediaPlaybackRequiresUserGesture(false); .setMediaPlaybackRequiresUserGesture(false);
} }
final targetUrl = widget.url ?? '';
try { try {
if (widget.assetPath != null) {
await controller.loadFlutterAsset(widget.assetPath!);
} else {
final targetUrl = widget.url ?? '';
await controller.loadRequest(Uri.parse(targetUrl)); await controller.loadRequest(Uri.parse(targetUrl));
}
} catch (e) { } catch (e) {
debugPrint('webview - loadRequest failed: $e'); debugPrint('webview - load failed: $e');
if (!mounted) return; if (!mounted) return;
setState(() { setState(() {
_isLoading = false; _isLoading = false;
@@ -95,6 +111,81 @@ class WebviewPageState extends State<WebviewPage> {
setState(() => _controller = controller); setState(() => _controller = controller);
} }
void _onMessageReceived(JavaScriptMessage message) {
debugPrint('收到来自 WebView 的消息: ${message.message}');
}
/// 脚本注入传输给h5 数据
Future<void> _injectH5NeedData(WebViewController controller) async {
try {
await controller.runJavaScript('''
(function () {
if (window.__appBridgePostMessageBridgeInstalled) {
return;
}
window.__appBridgePostMessageBridgeInstalled = true;
window.addEventListener('message', function (event) {
if (!window.AppBridge || !window.AppBridge.postMessage) {
return;
}
var payload = event.data;
if (typeof payload !== 'string') {
try {
payload = JSON.stringify(payload);
if (typeof payload === 'undefined') {
payload = String(event.data);
}
} catch (error) {
payload = String(payload);
}
}
window.AppBridge.postMessage(payload);
});
})();
''');
} catch (e) {
debugPrint('webview - inject postMessage bridge failed: $e');
}
}
Future<void> _injectPostMessageBridge(WebViewController controller) async {
// try {
// await controller.runJavaScript('''
// (function () {
// if (window.__appBridgePostMessageBridgeInstalled) {
// return;
// }
// window.__appBridgePostMessageBridgeInstalled = true;
// window.addEventListener('message', function (event) {
// if (!window.AppBridge || !window.AppBridge.postMessage) {
// return;
// }
// var payload = event.data;
// if (typeof payload !== 'string') {
// try {
// payload = JSON.stringify(payload);
// if (typeof payload === 'undefined') {
// payload = String(event.data);
// }
// } catch (error) {
// payload = String(payload);
// }
// }
// window.AppBridge.postMessage(payload);
// });
// })();
// ''');
// } catch (e) {
// debugPrint('webview - inject postMessage bridge failed: $e');
// }
}
PlatformWebViewControllerCreationParams _createPlatformParams() { PlatformWebViewControllerCreationParams _createPlatformParams() {
if (WebViewPlatform.instance is WebKitWebViewPlatform) { if (WebViewPlatform.instance is WebKitWebViewPlatform) {
return WebKitWebViewControllerCreationParams( return WebKitWebViewControllerCreationParams(
@@ -110,27 +201,8 @@ class WebviewPageState extends State<WebviewPage> {
return Scaffold( return Scaffold(
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
extendBodyBehindAppBar: true, extendBodyBehindAppBar: true,
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
title: Text(
widget.title ?? '',
style: const TextStyle(color: Colors.white),
),
leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.white),
onPressed: () async {
final controller = _controller;
if (controller != null && await controller.canGoBack()) {
await controller.goBack();
} else {
if (context.mounted) Navigator.of(context).maybePop();
}
},
),
iconTheme: const IconThemeData(color: Colors.white),
),
// appBar: myAppBar(context: context),
body: _buildBody(), body: _buildBody(),
); );
} }
+2
View File
@@ -80,6 +80,8 @@ flutter:
# To add assets to your application, add an assets section, like this: # To add assets to your application, add an assets section, like this:
assets: assets:
- assets/images/ - assets/images/
- assets/html/
# To add assets to your application, add an assets section, like this: # To add assets to your application, add an assets section, like this:
# assets: # assets:
# - images/a_dot_burr.jpeg # - images/a_dot_burr.jpeg