|
@@ -1,10 +1,16 @@
|
|
|
+import 'dart:convert';
|
|
|
+
|
|
|
import 'package:flutter/material.dart';
|
|
|
+import 'package:plugin_platform/engine/image/image_nine_grid.dart';
|
|
|
import 'package:shared/utils/log_utils.dart';
|
|
|
+import 'package:shared/utils/screen_util.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
|
|
@@ -12,10 +18,12 @@ class WebViewPage extends StatefulWidget {
|
|
|
final String? initialUrl;
|
|
|
bool? showAppbar = true;
|
|
|
Map? arguments = {'title': '', 'initialUrl': ''};
|
|
|
+ String? bottomBtnTxt; //是否有底部按钮,底部按钮的文本
|
|
|
+ VoidCallback? bottomBtnAction; //底部按钮的回调
|
|
|
|
|
|
List<Widget>? actions = [];
|
|
|
|
|
|
- WebViewPage({Key? key, this.showAppbar, this.initialUrl, this.arguments, this.actions}) : super(key: key);
|
|
|
+ WebViewPage({Key? key, this.showAppbar, this.initialUrl, this.arguments, this.actions, this.bottomBtnTxt, this.bottomBtnAction}) : super(key: key);
|
|
|
|
|
|
@override
|
|
|
_WebViewPageState createState() => _WebViewPageState();
|
|
@@ -24,7 +32,7 @@ class WebViewPage extends StatefulWidget {
|
|
|
class _WebViewPageState extends State<WebViewPage> {
|
|
|
WebViewController? webViewController;
|
|
|
final _key = UniqueKey();
|
|
|
- String title = '';
|
|
|
+ String? title;
|
|
|
bool _showAppbar = true;
|
|
|
int _stackToView = 1;
|
|
|
String? _initialUrl;
|
|
@@ -35,15 +43,82 @@ class _WebViewPageState extends State<WebViewPage> {
|
|
|
super.initState();
|
|
|
title = widget.arguments != null ? widget.arguments!['title'] : null;
|
|
|
_showAppbar = widget.showAppbar ?? true;
|
|
|
+ Log.d("传入的initialUrl:${widget.initialUrl}");
|
|
|
_initialUrl = widget.initialUrl ?? widget.arguments!['initialUrl'];
|
|
|
- if (Platform.isAndroid) WebView.platform = SurfaceAndroidWebView();
|
|
|
+
|
|
|
+ _initializeController();
|
|
|
+ }
|
|
|
+
|
|
|
+ void _initializeController() {
|
|
|
+ webViewController = WebViewController()
|
|
|
+ ..setJavaScriptMode(JavaScriptMode.unrestricted)
|
|
|
+ ..setBackgroundColor(const Color(0x00000000))
|
|
|
+ ..setNavigationDelegate(
|
|
|
+ NavigationDelegate(
|
|
|
+ onProgress: (int progress) {
|
|
|
+ if (progress == 100) {
|
|
|
+ Future.delayed(const Duration(milliseconds: 500)).then((value) => {
|
|
|
+ // 获取页面高度
|
|
|
+ _getWebViewHeight()
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onPageStarted: (String url) {
|
|
|
+ if (mounted) {
|
|
|
+ setState(() {
|
|
|
+ _stackToView = 1;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onPageFinished: (String url) {
|
|
|
+ if (mounted) {
|
|
|
+ setState(() {
|
|
|
+ _stackToView = 0;
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onWebResourceError: (WebResourceError error) {},
|
|
|
+ onNavigationRequest: (NavigationRequest request) async {
|
|
|
+ if (request.url.startsWith('tel:')) {
|
|
|
+ // 拦截tel链接
|
|
|
+ if (!await launchUrl(Uri.parse(request.url))) throw 'Unable to activate the call function';
|
|
|
+ return NavigationDecision.prevent; // 阻止WebView导航到该链接
|
|
|
+ }
|
|
|
+ return NavigationDecision.navigate;
|
|
|
+ },
|
|
|
+ ),
|
|
|
+ )
|
|
|
+ ..loadRequest(Uri.parse(_initialUrl ?? ""));
|
|
|
+ }
|
|
|
+
|
|
|
+ _getWebViewHeight() async {
|
|
|
+ // try {
|
|
|
+ // final result = await webViewController?.runJavaScriptReturningResult('''
|
|
|
+ // new Promise((resolve) => {
|
|
|
+ // const scrollHeight = document.documentElement.scrollHeight;
|
|
|
+ // resolve(scrollHeight);
|
|
|
+ // });
|
|
|
+ // ''');
|
|
|
+ // return double.parse(result.toString());
|
|
|
+ // } catch (e) {
|
|
|
+ // return 300.0; // 默认高度
|
|
|
+ // }
|
|
|
+
|
|
|
+ // 方式二:
|
|
|
+ var originalHeight = await webViewController?.runJavaScriptReturningResult("document.body.offsetHeight;");
|
|
|
+ _webViewHeight = double.parse(originalHeight.toString());
|
|
|
+ if (mounted) {
|
|
|
+ setState(() {
|
|
|
+ _webViewHeight = _webViewHeight <= 0 ? 300 : _webViewHeight;
|
|
|
+ });
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
@override
|
|
|
void dispose() async {
|
|
|
super.dispose();
|
|
|
// 销毁 WebView 实例
|
|
|
- webViewController?.loadUrl('about:blank');
|
|
|
+ webViewController?.loadHtmlString('about:blank');
|
|
|
webViewController?.clearCache();
|
|
|
}
|
|
|
|
|
@@ -55,80 +130,6 @@ class _WebViewPageState extends State<WebViewPage> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // 注册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<bool> _onWillPop() async {
|
|
|
if (webViewController == null) {
|
|
@@ -152,84 +153,74 @@ class _WebViewPageState extends State<WebViewPage> {
|
|
|
|
|
|
@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: 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;
|
|
|
- },
|
|
|
+ return SafeArea(
|
|
|
+ bottom: true,
|
|
|
+ top: false,
|
|
|
+ child: Container(
|
|
|
+ width: double.infinity,
|
|
|
+ height: double.infinity,
|
|
|
+ padding: EdgeInsets.only(top: ScreenUtil.getStatusBarH(context)),
|
|
|
+ decoration: const BoxDecoration(
|
|
|
+ gradient: LinearGradient(
|
|
|
+ colors: [
|
|
|
+ Color(0xFF091D44),
|
|
|
+ Color(0xFF245A8A),
|
|
|
+ Color(0xFF7F7CEC),
|
|
|
+ ],
|
|
|
+ begin: Alignment.topCenter,
|
|
|
+ end: Alignment.bottomCenter,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ child: Column(
|
|
|
+ children: [
|
|
|
+ MyAppBar.titleBar(context, title ?? "Title"),
|
|
|
+
|
|
|
+ IndexedStack(
|
|
|
+ index: _stackToView,
|
|
|
+ children: [
|
|
|
+ //WebView控件
|
|
|
+ Column(
|
|
|
+ children: [
|
|
|
+ Expanded(
|
|
|
+ child: WillPopScope(
|
|
|
+ onWillPop: _onWillPop,
|
|
|
+ child: WebViewWidget(
|
|
|
+ key: _key,
|
|
|
+ controller: webViewController!,
|
|
|
+ ),
|
|
|
),
|
|
|
+ )
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ //Loading加载控件
|
|
|
+ Container(
|
|
|
+ color: Colors.white,
|
|
|
+ child: const Center(
|
|
|
+ child: CircularProgressIndicator(
|
|
|
+ strokeWidth: 3,
|
|
|
+ valueColor: AlwaysStoppedAnimation(ColorConstants.appBlue),
|
|
|
),
|
|
|
- )
|
|
|
- ],
|
|
|
- ),
|
|
|
- Container(
|
|
|
- color: Colors.white,
|
|
|
- child: 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: ColorConstants.blue1578fe,
|
|
|
+ fontWeight: FontWeight.w500,
|
|
|
+ type: ClickType.throttle,
|
|
|
+ fontSize: 16,
|
|
|
+ minHeight: 50,
|
|
|
+ radius: 0,
|
|
|
+ )
|
|
|
+ ],
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ );
|
|
|
}
|
|
|
}
|