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; const WebviewPage({super.key, required this.url, this.title}); @override State createState() => WebviewPageState(); } class WebviewPageState extends State { WebViewController? _controller; bool _isLoading = true; String? _errorMessage; @override void initState() { super.initState(); _initController(); } Future _initController() async { if (widget.url?.isEmpty ?? true) { return; } final params = _createPlatformParams(); final 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) { debugPrint('webview - page finished: $url'); 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; }, ), ); if (controller.platform is AndroidWebViewController) { AndroidWebViewController.enableDebugging(kDebugMode); await (controller.platform as AndroidWebViewController) .setMediaPlaybackRequiresUserGesture(false); } final targetUrl = widget.url ?? ''; try { await controller.loadRequest(Uri.parse(targetUrl)); } catch (e) { debugPrint('webview - loadRequest failed: $e'); if (!mounted) return; setState(() { _isLoading = false; _errorMessage = e.toString(); }); return; } if (!mounted) return; setState(() => _controller = controller); } 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: 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), ), 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()), ], ); } }