web_view.dart 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import 'package:flutter/material.dart';
  2. import 'package:flutter_inappwebview/flutter_inappwebview.dart';
  3. import 'package:flutter_screenutil/flutter_screenutil.dart';
  4. import 'package:get/get.dart';
  5. import 'package:nomo/config/theme/theme_extensions/theme_extension.dart';
  6. import 'package:url_launcher/url_launcher.dart';
  7. import '../../../../utils/log/logger.dart';
  8. import '../../../base/base_view.dart';
  9. import '../../../widgets/ix_app_bar.dart';
  10. import '../controllers/web_controller.dart';
  11. class WebView extends BaseView<WebController> {
  12. const WebView({super.key});
  13. @override
  14. Widget buildContent(BuildContext context) {
  15. return Column(
  16. children: [
  17. IXAppBar(
  18. title: controller.title,
  19. // 不需要传递颜色参数,会自动使用响应式主题
  20. onBackPressed: () async {
  21. if (await controller.checkCanGoBack()) {
  22. controller.goBack();
  23. } else {
  24. Get.back();
  25. }
  26. },
  27. actions: [
  28. IconButton(
  29. onPressed: () {
  30. controller.reload();
  31. },
  32. icon: const Icon(Icons.refresh_rounded),
  33. ),
  34. Obx(
  35. () => controller.canGoBack.value
  36. ? IconButton(
  37. onPressed: () {
  38. Get.back();
  39. },
  40. icon: const Icon(Icons.close_rounded),
  41. )
  42. : const SizedBox.shrink(),
  43. ),
  44. ],
  45. ),
  46. Expanded(child: _buildWebContent()),
  47. ],
  48. );
  49. }
  50. Widget _buildWebContent() {
  51. return Stack(
  52. children: [
  53. InAppWebView(
  54. initialUrlRequest: URLRequest(url: WebUri(controller.url)),
  55. initialSettings: InAppWebViewSettings(
  56. userAgent: controller.userAgent,
  57. javaScriptEnabled: true,
  58. useShouldOverrideUrlLoading: true,
  59. useOnLoadResource: true,
  60. mediaPlaybackRequiresUserGesture: false,
  61. allowsInlineMediaPlayback: true,
  62. iframeAllow: "camera; microphone",
  63. iframeAllowFullscreen: true,
  64. ),
  65. onWebViewCreated: (InAppWebViewController webViewController) {
  66. controller.setWebViewController(webViewController);
  67. },
  68. onProgressChanged:
  69. (InAppWebViewController webViewController, int progress) {
  70. controller.loadingProgress.value = progress / 100;
  71. controller.isLoading.value = progress < 100;
  72. },
  73. onLoadStart: (InAppWebViewController webViewController, Uri? url) {
  74. controller.checkCanGoBack();
  75. },
  76. onLoadStop: (InAppWebViewController webViewController, Uri? url) {
  77. controller.isLoading.value = false;
  78. controller.loadingProgress.value = 0.0;
  79. controller.checkCanGoBack();
  80. },
  81. onReceivedError:
  82. (
  83. InAppWebViewController webViewController,
  84. WebResourceRequest request,
  85. WebResourceError error,
  86. ) {
  87. controller.isLoading.value = false;
  88. controller.loadingProgress.value = 0.0;
  89. },
  90. shouldOverrideUrlLoading:
  91. (
  92. InAppWebViewController webViewController,
  93. NavigationAction navigationAction,
  94. ) async {
  95. final url = navigationAction.request.url?.toString() ?? '';
  96. try {
  97. final uri = Uri.parse(url);
  98. // 处理 intent:// 链接
  99. if (url.startsWith("intent://")) {
  100. final parsedUrl = controller.parseIntentUrl(url);
  101. final fallbackUrl = controller.parseFallbackUrl(url);
  102. if (parsedUrl != null &&
  103. await canLaunchUrl(Uri.parse(parsedUrl))) {
  104. await launchUrl(
  105. Uri.parse(parsedUrl),
  106. mode: LaunchMode.externalApplication,
  107. );
  108. } else if (fallbackUrl != null &&
  109. await canLaunchUrl(Uri.parse(fallbackUrl))) {
  110. await launchUrl(
  111. Uri.parse(fallbackUrl),
  112. mode: LaunchMode.externalApplication,
  113. );
  114. }
  115. return NavigationActionPolicy.CANCEL;
  116. }
  117. // 处理下载链接
  118. else if (uri.path.endsWith(".apk") ||
  119. uri.path.endsWith(".pdf") ||
  120. uri.path.contains("download")) {
  121. if (await canLaunchUrl(uri)) {
  122. await launchUrl(
  123. uri,
  124. mode: LaunchMode.externalApplication,
  125. );
  126. }
  127. return NavigationActionPolicy.CANCEL;
  128. }
  129. // 处理 http/https 链接
  130. if (uri.scheme == 'http' || uri.scheme == 'https') {
  131. return NavigationActionPolicy.ALLOW;
  132. }
  133. // 处理其他 scheme
  134. if (await canLaunchUrl(uri)) {
  135. await launchUrl(uri, mode: LaunchMode.externalApplication);
  136. }
  137. } catch (e) {
  138. log("Error handling shouldOverrideUrlLoading: $e");
  139. }
  140. return NavigationActionPolicy.CANCEL;
  141. },
  142. ),
  143. Obx(
  144. () => controller.isLoading.value
  145. ? LinearProgressIndicator(
  146. value: controller.loadingProgress.value,
  147. backgroundColor: Get.reactiveTheme.scaffoldBackgroundColor,
  148. minHeight: 2.w,
  149. valueColor: AlwaysStoppedAnimation<Color>(
  150. Get.reactiveTheme.primaryColor,
  151. ),
  152. )
  153. : const SizedBox.shrink(),
  154. ),
  155. ],
  156. );
  157. }
  158. }