import 'package:cs_resources/theme/app_colors_theme.dart'; import 'package:flutter/material.dart'; import 'package:plugin_platform/engine/image/image_nine_grid.dart'; import 'package:shared/utils/log_utils.dart'; import 'package:url_launcher/url_launcher.dart'; import 'dart:io'; import 'package:webview_flutter/webview_flutter.dart'; import 'package:widgets/ext/ex_widget.dart'; import 'package:widgets/my_appbar.dart'; import 'package:cs_resources/constants/color_constants.dart'; import 'package:widgets/my_button.dart'; /// webview 封装 // ignore: must_be_immutable class WebViewPage extends StatefulWidget { final String? initialUrl; bool? showAppbar = true; Map? arguments = {'title': '', 'initialUrl': ''}; String? bottomBtnTxt; //是否有底部按钮,底部按钮的文本 VoidCallback? bottomBtnAction; //底部按钮的回调 List? actions = []; WebViewPage({Key? key, this.showAppbar, this.initialUrl, this.arguments, this.actions, this.bottomBtnTxt, this.bottomBtnAction}) : super(key: key); @override _WebViewPageState createState() => _WebViewPageState(); } class _WebViewPageState extends State { WebViewController? webViewController; final _key = UniqueKey(); String title = ''; bool _showAppbar = true; int _stackToView = 1; String? _initialUrl; double _webViewHeight = 200; @override void initState() { super.initState(); title = widget.arguments != null ? widget.arguments!['title'] : null; _showAppbar = widget.showAppbar ?? true; _initialUrl = widget.initialUrl ?? widget.arguments!['initialUrl']; if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView(); } @override void dispose() async { super.dispose(); // 销毁 WebView 实例 webViewController?.loadUrl('about:blank'); webViewController?.clearCache(); } @override void didUpdateWidget(covariant WebViewPage oldWidget) { super.didUpdateWidget(oldWidget); if (widget.initialUrl != oldWidget.initialUrl) { super.didUpdateWidget(oldWidget); } } // 注册js回调 _invokeJavascriptChannel(BuildContext context) { return [ JavascriptChannel( name: 'Invoke', onMessageReceived: (JavascriptMessage message) { // var webHeight = double.parse(message.message); // setState(() { // print('打印webHeight:$_webViewHeight'); // _webViewHeight = webHeight; // }); }, ), JavascriptChannel( name: 'Invoke1', onMessageReceived: (JavascriptMessage message) { // var devicePixelRatio = double.parse(message.message); // setState(() { // _webViewHeight = devicePixelRatio; // }); }, ), JavascriptChannel( name: "integral", onMessageReceived: (JavascriptMessage message) { // print("交互"); // print("参数: ${message.message}"); // Map res = changeStringToJsonMap(message.message); // print(res["operation"]); // _controller?.evaluateJavascript("getAddressBook('sdad')"); }, ), JavascriptChannel( name: "MessageDeal", onMessageReceived: (JavascriptMessage message) async { // print("交互"); // print("参数: ${message.message}"); // print(webViewController); // webViewController // ?.runJavascriptReturningResult("showMessage('我(Flutter)收到了你的消息)"); // _controller?.evaluateJavascript("document.title"); }, ), JavascriptChannel( name: "callWithDict", onMessageReceived: (JavascriptMessage message) { // print("交互"); // print("参数: ${message.message}"); }, ), ]; } // 获取页面高度 _getWebViewHeight() async { // 方式一: // await webViewController?.runJavascriptReturningResult(''' // try { // // Invoke.postMessage([document.body.clientHeight,document.documentElement.clientHeight,document.documentElement.scrollHeight]); // let scrollHeight = document.documentElement.scrollHeight; // if (scrollHeight) { // Invoke.postMessage(scrollHeight); // } // } catch {} // '''); // 方式二: var originalHeight = await webViewController!.runJavascriptReturningResult("document.body.offsetHeight;"); _webViewHeight = double.parse(originalHeight); setState(() { _webViewHeight = _webViewHeight <= 0 ? 300 : _webViewHeight; }); // print('网页高度-----' + _webViewHeight.toString()); } // 返回与后退的处理 Future _onWillPop() async { if (webViewController == null) { Log.d("WebView都没有加载成功,直接返回退出即可"); //WebView都没有加载成功,可以直接退出 Navigator.of(context).pop(); return false; } else { //如果点击了内部链接之后,可以返回,则直接返回 if (await webViewController!.canGoBack()) { Log.d("点击内部链接,可以后台,调用后退"); await webViewController!.goBack(); return false; // 防止退出页面 } else { Log.d("点击内部链接,无法后退,直接返回"); Navigator.of(context).pop(); // 不能后退则退出当前页面 return true; } } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.white, appBar: _showAppbar ? MyAppBar.appBar(context, title, backgroundColor: Colors.white, actions: widget.actions, backCallback: _onWillPop) : null, body: SafeArea( bottom: true, top: false, child: Column( children: [ IndexedStack( index: _stackToView, children: [ Column( children: [ Expanded( child: WillPopScope( onWillPop: _onWillPop, child: WebView( key: _key, initialUrl: _initialUrl, userAgent: '', //JS执行模式 是否允许JS执行 javascriptMode: JavascriptMode.unrestricted, //webview创建好 onWebViewCreated: (WebViewController controller) { webViewController = controller; }, onProgress: (int progress) { // print('progress=====>$progress'); if (progress == 100) { Future.delayed(const Duration(milliseconds: 500)).then((value) => { // 获取页面高度 _getWebViewHeight() }); } }, onPageStarted: (String url) { // print('onPageStarted=====>$url'); }, onPageFinished: (String url) { Log.d('onPageFinished=====>$url'); if (mounted) { setState(() { _stackToView = 0; }); } }, onWebResourceError: (WebResourceError error) { // print('error=====>$error'); }, gestureNavigationEnabled: true, // 注册js 回调 javascriptChannels: _invokeJavascriptChannel(context).toSet(), navigationDelegate: (NavigationRequest request) async { //如果是tel标签需要阻止并调用 launchUrl 的方法调用拨打电话 if (request.url.startsWith('tel:')) { // 拦截tel链接 if (!await launchUrl(Uri.parse(request.url))) throw '无法启动拨打电话功能'; return NavigationDecision.prevent; // 阻止WebView导航到该链接 } // 可以继续WebView的内部跳转 return NavigationDecision.navigate; }, ), ), ) ], ), Container( color: Colors.white, child: const Center( child: CircularProgressIndicator( strokeWidth: 3, valueColor: AlwaysStoppedAnimation(ColorConstants.appBlue), ), ), ), ], ).expanded(), //底部按钮 if (widget.bottomBtnTxt != null) MyButton( onPressed: widget.bottomBtnAction, text: widget.bottomBtnTxt??"", textColor: Colors.white, backgroundColor: context.appColors.btnBgDefault, fontWeight: FontWeight.w500, type: ClickType.throttle, fontSize: 16, minHeight: 50, radius: 0, ) ], ), )); } }