From f6c477c9be27a9c7ec043c78c60f5884fc8d3fe4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E9=94=8B?= <2535831261@qq.com> Date: Tue, 7 Jul 2026 17:40:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0webview=5Fflutter=E5=8F=8A?= =?UTF-8?q?=E7=9B=B8=E5=85=B3=E4=BE=9D=E8=B5=96=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=85=AC=E5=85=B1=E7=9A=84=20webview=20=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scan_qrcode/pages/page_scan_qrcode.dart | 5 + lib/shared/widgets/app_webview.dart | 164 ++++++++++++++++++ pubspec.yaml | 3 + 3 files changed, 172 insertions(+) create mode 100644 lib/shared/widgets/app_webview.dart diff --git a/lib/features/scan_qrcode/pages/page_scan_qrcode.dart b/lib/features/scan_qrcode/pages/page_scan_qrcode.dart index f4f951b..192bca9 100644 --- a/lib/features/scan_qrcode/pages/page_scan_qrcode.dart +++ b/lib/features/scan_qrcode/pages/page_scan_qrcode.dart @@ -1,7 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:recording_tool/app/router/app_navigator.dart'; import 'package:recording_tool/shared/widgets/app_button.dart'; import 'package:recording_tool/shared/widgets/app_qr_scanner_dialog.dart'; +import 'package:recording_tool/shared/widgets/app_webview.dart'; class ScanQrCodePage extends StatefulWidget { const ScanQrCodePage({super.key}); @@ -47,6 +49,9 @@ class _AuthPageWidgetState extends State { final result = await AppQrScannerDialog.show(context); if (result == null || result.isEmpty) return; debugPrint('扫码结果: $result'); + AppNavigator.push( + const WebviewPage(url: 'https://www.dronex.cc/'), + ); }, variant: AppButtonVariant.secondary, ), diff --git a/lib/shared/widgets/app_webview.dart b/lib/shared/widgets/app_webview.dart new file mode 100644 index 0000000..0c724fb --- /dev/null +++ b/lib/shared/widgets/app_webview.dart @@ -0,0 +1,164 @@ +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()), + ], + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index 00eae6b..1cc1d6d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -48,6 +48,9 @@ dependencies: url_launcher: ^6.3.2 uuid: ^4.5.3 mobile_scanner: ^7.2.0 + webview_flutter_android: ^4.13.0 + webview_flutter_wkwebview: ^3.26.0 + webview_flutter: ^4.14.0 dev_dependencies: flutter_test: