import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:webview_flutter_android/webview_flutter_android.dart'; import 'package:webview_flutter_wkwebview/webview_flutter_wkwebview.dart'; class WebviewPage extends StatefulWidget { final String? url; final String? title; final String? assetPath; const WebviewPage({super.key, this.url, this.title, this.assetPath}); @override State createState() => WebviewPageState(); } class WebviewPageState extends State { static const String _javaScriptChannelName = 'AppBridge'; WebViewController? _controller; bool _isLoading = true; String? _errorMessage; @override void initState() { super.initState(); _initController(); } Future _initController() async { if ((widget.url?.isEmpty ?? true) && widget.assetPath == null) { return; } final params = _createPlatformParams(); late final WebViewController controller; controller = WebViewController.fromPlatformCreationParams(params) ..setJavaScriptMode(JavaScriptMode.unrestricted) ..setBackgroundColor(Colors.transparent) ..setNavigationDelegate( NavigationDelegate( onProgress: (int progress) { debugPrint('webview - progress: $progress'); }, onPageStarted: (String url) { debugPrint('webview - page started: $url'); if (!mounted) return; setState(() { _isLoading = true; _errorMessage = null; }); }, onPageFinished: (String url) async { debugPrint('webview - page finished: $url'); await _injectPostMessageBridge(controller); if (!mounted) return; setState(() => _isLoading = false); }, onHttpError: (HttpResponseError error) { debugPrint( 'webview - http error: ${error.response?.uri}, code: ${error.response?.statusCode}', ); }, onWebResourceError: (WebResourceError error) { debugPrint( 'webview error: ${error.description}, code: ${error.errorCode}, url: ${error.url}', ); if (!mounted || error.isForMainFrame != true) return; setState(() { _isLoading = false; _errorMessage = error.description; }); }, onNavigationRequest: (NavigationRequest request) { return NavigationDecision.navigate; }, ), ); controller.addJavaScriptChannel( _javaScriptChannelName, onMessageReceived: (JavaScriptMessage message) { _onMessageReceived(message); }, ); if (controller.platform is AndroidWebViewController) { AndroidWebViewController.enableDebugging(kDebugMode); await (controller.platform as AndroidWebViewController) .setMediaPlaybackRequiresUserGesture(false); } try { if (widget.assetPath != null) { await controller.loadFlutterAsset(widget.assetPath!); } else { final targetUrl = widget.url ?? ''; await controller.loadRequest(Uri.parse(targetUrl)); } } catch (e) { debugPrint('webview - load failed: $e'); if (!mounted) return; setState(() { _isLoading = false; _errorMessage = e.toString(); }); return; } if (!mounted) return; setState(() => _controller = controller); } void _onMessageReceived(JavaScriptMessage message) { debugPrint('收到来自 WebView 的消息: ${message.message}'); } /// 脚本注入传输给h5 数据 Future _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 _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() { if (WebViewPlatform.instance is WebKitWebViewPlatform) { return WebKitWebViewControllerCreationParams( allowsInlineMediaPlayback: true, mediaTypesRequiringUserAction: const {}, ); } return const PlatformWebViewControllerCreationParams(); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.transparent, extendBodyBehindAppBar: true, // appBar: myAppBar(context: context), body: _buildBody(), ); } Widget _buildBody() { if (_errorMessage != null) { return Center( child: Padding( padding: const EdgeInsets.all(24), child: Text( '页面加载失败\n$_errorMessage', textAlign: TextAlign.center, style: const TextStyle(color: Colors.white, fontSize: 14), ), ), ); } final controller = _controller; if (controller == null) { return const Center(child: CircularProgressIndicator()); } return Stack( children: [ Positioned.fill(child: WebViewWidget(controller: controller)), if (_isLoading) const Center(child: CircularProgressIndicator()), ], ); } }