添加webview_flutter及相关依赖

添加公共的 webview 组件
This commit is contained in:
2026-07-07 17:40:24 +08:00
parent 1246e41e4b
commit f6c477c9be
3 changed files with 172 additions and 0 deletions
@@ -1,7 +1,9 @@
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/shared/widgets/app_button.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_qr_scanner_dialog.dart';
import 'package:recording_tool/shared/widgets/app_webview.dart';
class ScanQrCodePage extends StatefulWidget { class ScanQrCodePage extends StatefulWidget {
const ScanQrCodePage({super.key}); const ScanQrCodePage({super.key});
@@ -47,6 +49,9 @@ class _AuthPageWidgetState extends State<ScanQrCodePage> {
final result = await AppQrScannerDialog.show(context); final result = await AppQrScannerDialog.show(context);
if (result == null || result.isEmpty) return; if (result == null || result.isEmpty) return;
debugPrint('扫码结果: $result'); debugPrint('扫码结果: $result');
AppNavigator.push(
const WebviewPage(url: 'https://www.dronex.cc/'),
);
}, },
variant: AppButtonVariant.secondary, variant: AppButtonVariant.secondary,
), ),
+164
View File
@@ -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<WebviewPage> createState() => WebviewPageState();
}
class WebviewPageState extends State<WebviewPage> {
WebViewController? _controller;
bool _isLoading = true;
String? _errorMessage;
@override
void initState() {
super.initState();
_initController();
}
Future<void> _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 <PlaybackMediaTypes>{},
);
}
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()),
],
);
}
}
+3
View File
@@ -48,6 +48,9 @@ dependencies:
url_launcher: ^6.3.2 url_launcher: ^6.3.2
uuid: ^4.5.3 uuid: ^4.5.3
mobile_scanner: ^7.2.0 mobile_scanner: ^7.2.0
webview_flutter_android: ^4.13.0
webview_flutter_wkwebview: ^3.26.0
webview_flutter: ^4.14.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: