webview_page.dart 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. import 'dart:convert';
  2. import 'package:flutter/material.dart';
  3. import 'package:plugin_platform/engine/image/image_nine_grid.dart';
  4. import 'package:shared/utils/log_utils.dart';
  5. import 'package:shared/utils/screen_util.dart';
  6. import 'package:url_launcher/url_launcher.dart';
  7. import 'dart:io';
  8. import 'package:webview_flutter/webview_flutter.dart';
  9. import 'package:widgets/ext/ex_widget.dart';
  10. import 'package:widgets/my_appbar.dart';
  11. import 'package:cs_resources/constants/color_constants.dart';
  12. import 'package:widgets/my_button.dart';
  13. /// webview 封装
  14. // ignore: must_be_immutable
  15. class WebViewPage extends StatefulWidget {
  16. final String? initialUrl;
  17. bool? showAppbar = true;
  18. Map? arguments = {'title': '', 'initialUrl': ''};
  19. String? bottomBtnTxt; //是否有底部按钮,底部按钮的文本
  20. VoidCallback? bottomBtnAction; //底部按钮的回调
  21. List<Widget>? actions = [];
  22. WebViewPage({Key? key, this.showAppbar, this.initialUrl, this.arguments, this.actions, this.bottomBtnTxt, this.bottomBtnAction}) : super(key: key);
  23. @override
  24. _WebViewPageState createState() => _WebViewPageState();
  25. }
  26. class _WebViewPageState extends State<WebViewPage> {
  27. WebViewController? webViewController;
  28. final _key = UniqueKey();
  29. String? title;
  30. bool _showAppbar = true;
  31. int _stackToView = 1;
  32. String? _initialUrl;
  33. double _webViewHeight = 200;
  34. @override
  35. void initState() {
  36. super.initState();
  37. title = widget.arguments != null ? widget.arguments!['title'] : null;
  38. _showAppbar = widget.showAppbar ?? true;
  39. Log.d("传入的initialUrl:${widget.initialUrl}");
  40. _initialUrl = widget.initialUrl ?? widget.arguments!['initialUrl'];
  41. _initializeController();
  42. }
  43. void _initializeController() {
  44. webViewController = WebViewController()
  45. ..setJavaScriptMode(JavaScriptMode.unrestricted)
  46. ..setBackgroundColor(const Color(0x00000000))
  47. ..setNavigationDelegate(
  48. NavigationDelegate(
  49. onProgress: (int progress) {
  50. if (progress == 100) {
  51. Future.delayed(const Duration(milliseconds: 500)).then((value) => {
  52. // 获取页面高度
  53. _getWebViewHeight()
  54. });
  55. }
  56. },
  57. onPageStarted: (String url) {
  58. if (mounted) {
  59. setState(() {
  60. _stackToView = 1;
  61. });
  62. }
  63. },
  64. onPageFinished: (String url) {
  65. if (mounted) {
  66. setState(() {
  67. _stackToView = 0;
  68. });
  69. }
  70. },
  71. onWebResourceError: (WebResourceError error) {},
  72. onNavigationRequest: (NavigationRequest request) async {
  73. if (request.url.startsWith('tel:')) {
  74. // 拦截tel链接
  75. if (!await launchUrl(Uri.parse(request.url))) throw 'Unable to activate the call function';
  76. return NavigationDecision.prevent; // 阻止WebView导航到该链接
  77. }
  78. return NavigationDecision.navigate;
  79. },
  80. ),
  81. )
  82. ..loadRequest(Uri.parse(_initialUrl ?? ""));
  83. }
  84. _getWebViewHeight() async {
  85. // try {
  86. // final result = await webViewController?.runJavaScriptReturningResult('''
  87. // new Promise((resolve) => {
  88. // const scrollHeight = document.documentElement.scrollHeight;
  89. // resolve(scrollHeight);
  90. // });
  91. // ''');
  92. // return double.parse(result.toString());
  93. // } catch (e) {
  94. // return 300.0; // 默认高度
  95. // }
  96. // 方式二:
  97. var originalHeight = await webViewController?.runJavaScriptReturningResult("document.body.offsetHeight;");
  98. _webViewHeight = double.parse(originalHeight.toString());
  99. if (mounted) {
  100. setState(() {
  101. _webViewHeight = _webViewHeight <= 0 ? 300 : _webViewHeight;
  102. });
  103. }
  104. }
  105. @override
  106. void dispose() async {
  107. super.dispose();
  108. // 销毁 WebView 实例
  109. webViewController?.loadHtmlString('about:blank');
  110. webViewController?.clearCache();
  111. }
  112. @override
  113. void didUpdateWidget(covariant WebViewPage oldWidget) {
  114. super.didUpdateWidget(oldWidget);
  115. if (widget.initialUrl != oldWidget.initialUrl) {
  116. super.didUpdateWidget(oldWidget);
  117. }
  118. }
  119. // 返回与后退的处理
  120. Future<bool> _onWillPop() async {
  121. if (webViewController == null) {
  122. Log.d("WebView都没有加载成功,直接返回退出即可");
  123. //WebView都没有加载成功,可以直接退出
  124. Navigator.of(context).pop();
  125. return false;
  126. } else {
  127. //如果点击了内部链接之后,可以返回,则直接返回
  128. if (await webViewController!.canGoBack()) {
  129. Log.d("点击内部链接,可以后台,调用后退");
  130. await webViewController!.goBack();
  131. return false; // 防止退出页面
  132. } else {
  133. Log.d("点击内部链接,无法后退,直接返回");
  134. Navigator.of(context).pop(); // 不能后退则退出当前页面
  135. return true;
  136. }
  137. }
  138. }
  139. @override
  140. Widget build(BuildContext context) {
  141. return SafeArea(
  142. bottom: true,
  143. top: false,
  144. child: Container(
  145. width: double.infinity,
  146. height: double.infinity,
  147. padding: EdgeInsets.only(top: ScreenUtil.getStatusBarH(context)),
  148. decoration: const BoxDecoration(
  149. gradient: LinearGradient(
  150. colors: [
  151. Color(0xFF091D44),
  152. Color(0xFF245A8A),
  153. Color(0xFF7F7CEC),
  154. ],
  155. begin: Alignment.topCenter,
  156. end: Alignment.bottomCenter,
  157. ),
  158. ),
  159. child: Column(
  160. children: [
  161. MyAppBar.titleBar(context, title ?? "Title"),
  162. IndexedStack(
  163. index: _stackToView,
  164. children: [
  165. //WebView控件
  166. Column(
  167. children: [
  168. Expanded(
  169. child: WillPopScope(
  170. onWillPop: _onWillPop,
  171. child: WebViewWidget(
  172. key: _key,
  173. controller: webViewController!,
  174. ),
  175. ),
  176. )
  177. ],
  178. ),
  179. //Loading加载控件
  180. Container(
  181. color: Colors.white,
  182. child: const Center(
  183. child: CircularProgressIndicator(
  184. strokeWidth: 3,
  185. valueColor: AlwaysStoppedAnimation(ColorConstants.appBlue),
  186. ),
  187. ),
  188. ),
  189. ],
  190. ).expanded(),
  191. //底部按钮
  192. if (widget.bottomBtnTxt != null)
  193. MyButton(
  194. onPressed: widget.bottomBtnAction,
  195. text: widget.bottomBtnTxt ?? "",
  196. textColor: Colors.white,
  197. backgroundColor: ColorConstants.blue1578fe,
  198. fontWeight: FontWeight.w500,
  199. type: ClickType.throttle,
  200. fontSize: 16,
  201. minHeight: 50,
  202. radius: 0,
  203. )
  204. ],
  205. ),
  206. ),
  207. );
  208. }
  209. }