Commit 4b858f67533b5f1a7472cc27c4861f37b4630e4d

Authored by 吴启风
1 parent 2d6bce99

feat:webview库替换

lib/common/pages/wow_web_page.dart
  1 +import 'dart:collection';
  2 +
1 3 import 'package:flutter/material.dart';
2   -import 'package:flutter_easyloading/flutter_easyloading.dart';
3   -import 'package:webview_flutter/webview_flutter.dart';
  4 +import 'package:flutter_inappwebview/flutter_inappwebview.dart';
  5 +import 'package:flutter_screenutil/flutter_screenutil.dart';
  6 +import 'package:url_launcher/url_launcher.dart';
  7 +import 'package:wow_english/common/extension/string_extension.dart';
4 8 import 'package:wow_english/common/widgets/we_app_bar.dart';
5 9  
  10 +import '../../utils/log_util.dart';
  11 +
6 12 class WowWebViewPage extends StatefulWidget {
7   - const WowWebViewPage({super.key, required this.urlStr, required this.webViewTitle});
  13 + WowWebViewPage({super.key, required this.urlStr, required this.webViewTitle});
8 14  
9 15 final String urlStr;
10   - final String webViewTitle;
  16 + String webViewTitle;
11 17  
12 18 @override
13 19 State<StatefulWidget> createState() {
... ... @@ -15,9 +21,60 @@ class WowWebViewPage extends StatefulWidget {
15 21 }
16 22 }
17 23  
  24 +/// 生成进度条组件,进度从0 ~ 1
  25 +_createProgressBar(double progress, BuildContext context) {
  26 + return LinearProgressIndicator(
  27 + backgroundColor: Colors.white70.withOpacity(0),
  28 + value: progress == 1.0 ? 0 : progress,
  29 + valueColor: const AlwaysStoppedAnimation<Color>(Colors.blue),
  30 + );
  31 +}
  32 +
18 33 class _WowWebViewPageState extends State<WowWebViewPage> {
  34 + late InAppWebViewController? _inAppWebViewController;
  35 + final GlobalKey webViewKey = GlobalKey();
  36 + double _progress = 0;
  37 + bool isCanGoBack = false;
  38 + bool isCanForward = false;
  39 + late final String defaultWebViewTitle = widget.webViewTitle;
  40 + final String TAG = "WowWebViewPage";
19 41  
20   - late WebViewController _controller;
  42 + // InAppWebViewSettings webViewSettings = InAppWebViewSettings(
  43 + // useShouldOverrideUrlLoading: true,
  44 + // mediaPlaybackRequiresUserGesture: true,
  45 + //
  46 + // /// android 支持HybridComposition
  47 + // useHybridComposition: true,
  48 + // allowsInlineMediaPlayback: true,
  49 + // );
  50 + InAppWebViewGroupOptions webViewSettings = InAppWebViewGroupOptions(
  51 + crossPlatform: InAppWebViewOptions(
  52 + useShouldOverrideUrlLoading: true, // 是否需要跳转
  53 + mediaPlaybackRequiresUserGesture: false, // 设置为true,防止H5的音频自动播放
  54 + transparentBackground: true,
  55 + ),
  56 + android: AndroidInAppWebViewOptions(
  57 + useHybridComposition: true,
  58 + mixedContentMode: AndroidMixedContentMode.MIXED_CONTENT_ALWAYS_ALLOW),
  59 + ios: IOSInAppWebViewOptions(
  60 + allowsInlineMediaPlayback: true,
  61 + ),
  62 + );
  63 +
  64 + Future<String?> getUrl() {
  65 + if (_inAppWebViewController == null) {
  66 + return Future.sync(() => null);
  67 + }
  68 + return _inAppWebViewController!.getUrl().then((uri) => uri.toString());
  69 + }
  70 +
  71 + Future<void> loadUrl(String url) {
  72 + if (_inAppWebViewController == null) {
  73 + return Future.sync(() => null);
  74 + }
  75 + return _inAppWebViewController!
  76 + .loadUrl(urlRequest: URLRequest(url: Uri.parse(url)));
  77 + }
21 78  
22 79 @override
23 80 void initState() {
... ... @@ -28,44 +85,214 @@ class _WowWebViewPageState extends State&lt;WowWebViewPage&gt; {
28 85 SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]);
29 86 }*/
30 87  
31   - _controller =WebViewController()
32   - ..setJavaScriptMode(JavaScriptMode.unrestricted)
33   - ..setBackgroundColor(const Color(0x00000000))
34   - ..setNavigationDelegate(
35   - NavigationDelegate(
36   - onProgress: (int progress) {
37   - // Update loading bar.
38   - },
39   - onPageStarted: (String url) {
40   - EasyLoading.show();
41   - },
42   - onPageFinished: (String url) {
43   - EasyLoading.dismiss();
44   - },
45   - onWebResourceError: (WebResourceError error) {
46   - EasyLoading.showError(error.description);
47   - },
48   - onNavigationRequest: (NavigationRequest request) {
49   - return NavigationDecision.navigate;
50   - },
51   - ),
52   - )
53   - ..loadRequest(Uri.parse(widget.urlStr));
  88 + // _controller = WebViewController()
  89 + // ..setJavaScriptMode(JavaScriptMode.unrestricted)
  90 + // ..setBackgroundColor(const Color(0x00000000))
  91 + // ..setNavigationDelegate(
  92 + // NavigationDelegate(
  93 + // onProgress: (int progress) {
  94 + // // Update loading bar.
  95 + // },
  96 + // onPageStarted: (String url) {
  97 + // Log.d("$TAG onPageStarted $url");
  98 + // EasyLoading.show();
  99 + // },
  100 + // onPageFinished: (String url) {
  101 + // Log.d("$TAG onPageFinished $url");
  102 + // EasyLoading.dismiss();
  103 + // },
  104 + // onWebResourceError: (WebResourceError error) {
  105 + // Log.d("$TAG onWebResourceError ${error.description}");
  106 + // EasyLoading.showError(error.description);
  107 + // },
  108 + // onUrlChange: (UrlChange change) {
  109 + // Log.d("$TAG onUrlChange ${change.url}");
  110 + // },
  111 + // onNavigationRequest: (NavigationRequest request) async {
  112 + // var url = request.url;
  113 + // Log.d("$TAG onNavigationRequest $url");
  114 + //
  115 + // /// Allow the navigation
  116 + // // return NavigationDecision.navigate;
  117 + // /// Block the navigation
  118 + // /// return NavigationDecision.prevent;
  119 + // if (url.startsWith("alipay:") || url.startsWith("alipays")) {
  120 + // Log.d("$TAG onNavigationRequest 支付宝 $url");
  121 + // launch(request.url);
  122 + // return NavigationDecision.navigate;
  123 + // }
  124 + // if (url.startsWith('http:') || url.startsWith('https:')) {
  125 + // return NavigationDecision.navigate;
  126 + // } else {
  127 + // try {
  128 + // await launch(url);
  129 + // } catch (e) {
  130 + // print('Could not launch $request.url: $e');
  131 + // }
  132 + // return NavigationDecision.prevent;
  133 + // }
  134 + // },
  135 + // ),
  136 + // )
  137 + // ..loadRequest(Uri.parse(widget.urlStr));
54 138 }
55 139  
56 140 @override
57 141 Widget build(BuildContext context) {
58   - return Scaffold(
59   - backgroundColor: Colors.white,
60   - appBar: WEAppBar(
61   - titleText: widget.webViewTitle,
62   - ),
63   - body: Container(
64   - color: Colors.white,
65   - child: SafeArea(
66   - child: WebViewWidget(controller: _controller,),
67   - )
68   - ),
  142 + return WillPopScope(
  143 + onWillPop: () {
  144 + Future<bool> canGoBack = _inAppWebViewController!.canGoBack();
  145 + return canGoBack.then((isCanGoBack) {
  146 + if (isCanGoBack) {
  147 + _inAppWebViewController!.goBack();
  148 + return false;
  149 + } else {
  150 + return true;
  151 + }
  152 + });
  153 + },
  154 + // return PopScope(
  155 + // canPop: () async {
  156 + // if (_controller != null) {
  157 + // bool canGoBack = await _controller!.canGoBack();
  158 + // return !canGoBack;
  159 + // }
  160 + // return true;
  161 + // },
  162 + // onPopInvoked: (PopDisposition disposition) async {
  163 + // if (_controller != null) {
  164 + // bool canGoBack = await _controller!.canGoBack();
  165 + // if (canGoBack) {
  166 + // _controller!.goBack();
  167 + // return PopDisposition.popCancelled;
  168 + // }
  169 + // }
  170 + // return PopDisposition.pop;
  171 + // },
  172 + child: Scaffold(
  173 + backgroundColor: Colors.red,
  174 + appBar: WEAppBar(
  175 + titleText: widget.webViewTitle,
  176 + leadingWidth: 96,
  177 + leading: Row(
  178 + children: <Widget>[
  179 + IconButton(
  180 + icon: Image.asset(
  181 + 'back_around'.assetPng,
  182 + height: 40.h,
  183 + width: 40.w,
  184 + ),
  185 + onPressed: () {
  186 + if (isCanGoBack) {
  187 + _inAppWebViewController!.goBack();
  188 + } else {
  189 + Navigator.pop(context);
  190 + }
  191 + },
  192 + ),
  193 + Visibility(
  194 + visible: isCanGoBack,
  195 + child: IconButton(
  196 + // icon: Image.asset(
  197 + // 'back_around'.assetPng,
  198 + // height: 40.h,
  199 + // width: 40.w,
  200 + // ),
  201 + icon: Icon(Icons.close),
  202 + onPressed: () {
  203 + Navigator.pop(context);
  204 + },
  205 + ),
  206 + ),
  207 + ],
  208 + ),
  209 + ),
  210 + body: Container(
  211 + color: Colors.white,
  212 + // child: SafeArea(
  213 + child: InAppWebView(
  214 + key: webViewKey,
  215 + initialUrlRequest: URLRequest(
  216 + url: Uri.parse(widget.urlStr),
  217 + ),
  218 + initialUserScripts: UnmodifiableListView<UserScript>([]),
  219 + initialOptions: webViewSettings,
  220 + onWebViewCreated: (controller) {
  221 + _inAppWebViewController = controller;
  222 + },
  223 + onTitleChanged:
  224 + (InAppWebViewController controller, String? title) {
  225 + Log.d("$TAG onTitleChanged title=$title`");
  226 + setState(() {
  227 + if (title?.endsWith(".com") == true) {
  228 + widget.webViewTitle = defaultWebViewTitle;
  229 + } else {
  230 + widget.webViewTitle = title ?? defaultWebViewTitle;
  231 + }
  232 + });
  233 + },
  234 + shouldOverrideUrlLoading: (controller, navigationAction) async {
  235 + var url = navigationAction.request.url;
  236 + Log.d(
  237 + "$TAG shouldOverrideUrlLoading url=$url scheme=${url?.scheme}");
  238 + if (![
  239 + "http",
  240 + "https",
  241 + "file",
  242 + "chrome",
  243 + "data",
  244 + "javascript",
  245 + "about",
  246 + ].contains(url?.scheme ?? "")) {
  247 + Log.d("$TAG canLaunchUrl(url)=${canLaunchUrl(url!)}");
  248 + await canLaunchUrl(url)
  249 + ? await launchUrl(url)
  250 + : Log.e("对不起,打不开链接地址:$url");
  251 + return NavigationActionPolicy.CANCEL;
  252 + }
  253 + return NavigationActionPolicy.ALLOW;
  254 + },
  255 + onLoadStop: (controller, url) async {
  256 + Log.d("$TAG onLoadStop url=$url");
  257 + //页面加载完毕,显示隐藏AppBar的返回键
  258 + _inAppWebViewController!.canGoBack().then((canGoBack) => {
  259 + setState(() {
  260 + isCanGoBack = canGoBack;
  261 + })
  262 + });
  263 + _inAppWebViewController!.canGoForward().then((canForward) => {
  264 + setState(() {
  265 + isCanForward = canForward;
  266 + })
  267 + });
  268 + },
  269 + onLoadError: (controller, request, code, message) {
  270 + Log.d(
  271 + "$TAG onReceivedError request=$request error=$code message=$message");
  272 + },
  273 + onLoadHttpError: (controller, request, errorResponse, message) {
  274 + Log.d(
  275 + "$TAG onReceivedError request=$request errorResponse=$errorResponse message=$message");
  276 + },
  277 + onProgressChanged: (controller, progress) {
  278 + Log.d("$TAG onProgressChanged progress=$progress");
  279 + //进度从0 ~ 100
  280 + setState(() {
  281 + _progress = progress / 100.0;
  282 + });
  283 + },
  284 + onUpdateVisitedHistory: (controller, url, androidIsReload) {
  285 + Log.d("$TAG onUpdateVisitedHistory url=$url");
  286 + },
  287 + // onLoadResourceWithCustomScheme: (controller, request) {
  288 + // Log.d("$TAG onLoadResourceWithCustomScheme request=$request");
  289 + // },
  290 + onConsoleMessage: (controller, consoleMessage) {
  291 + // Log.d("$TAG onConsoleMessage consoleMessage=$consoleMessage");
  292 + },
  293 + ),
  294 + )),
  295 + // ),
69 296 );
70 297 }
71 298 }
... ...
lib/common/widgets/we_app_bar.dart
... ... @@ -9,17 +9,18 @@ class WEAppBar extends StatelessWidget implements PreferredSizeWidget {
9 9 final Color? backgroundColor;
10 10 final PreferredSizeWidget? bottom;
11 11 final Widget? leading;
  12 + final double? leadingWidth;
12 13 final List<Widget>? actions;
13 14  
14   - const WEAppBar(
15   - {this.titleText,
16   - this.centerTitle = true,
17   - this.onBack,
18   - this.backgroundColor,
19   - this.bottom,
20   - this.leading,
21   - this.actions,
22   - super.key});
  15 + const WEAppBar({this.titleText,
  16 + this.centerTitle = true,
  17 + this.onBack,
  18 + this.backgroundColor,
  19 + this.bottom,
  20 + this.leading,
  21 + this.leadingWidth,
  22 + this.actions,
  23 + super.key});
23 24  
24 25 @override
25 26 Widget build(BuildContext context) {
... ... @@ -33,6 +34,7 @@ class WEAppBar extends StatelessWidget implements PreferredSizeWidget {
33 34 fontWeight: FontWeight.w700,
34 35 ),
35 36 ),
  37 + leadingWidth: leadingWidth,
36 38 leading: leading ??
37 39 GestureDetector(
38 40 onTap: () {
... ... @@ -58,5 +60,7 @@ class WEAppBar extends StatelessWidget implements PreferredSizeWidget {
58 60  
59 61 @override
60 62 // TODO: implement preferredSize
61   - Size get preferredSize => Size.fromHeight(kToolbarHeight + (bottom == null ? 0.0 : bottom!.preferredSize.height));
  63 + Size get preferredSize =>
  64 + Size.fromHeight(kToolbarHeight +
  65 + (bottom == null ? 0.0 : bottom!.preferredSize.height));
62 66 }
... ...
lib/common/widgets/webview_dialog.dart
  1 +import 'dart:collection';
1 2 import 'package:flutter/material.dart';
2   -import 'package:flutter_easyloading/flutter_easyloading.dart';
3   -import 'package:webview_flutter/webview_flutter.dart';
  3 +import 'package:flutter_inappwebview/flutter_inappwebview.dart';
4 4  
5 5 class WebviewDialog extends StatelessWidget {
6 6 final String title;
... ... @@ -45,8 +45,8 @@ class WebviewDialog extends StatelessWidget {
45 45 mainAxisAlignment: MainAxisAlignment.spaceBetween,
46 46 children: [
47 47 TextButton(
48   - child:
49   - const Text('同意并继续', style: TextStyle(color: Color(0xFFFBB621))),
  48 + child: const Text('同意并继续',
  49 + style: TextStyle(color: Color(0xFFFBB621))),
50 50 onPressed: () {
51 51 // 处理接受按钮的点击事件
52 52 leftTap(); // 关闭对话框
... ... @@ -68,30 +68,51 @@ class WebviewDialog extends StatelessWidget {
68 68 Future<Widget> buildWebViewController(String url) async {
69 69 Widget res;
70 70 try {
71   - WebViewController controller = WebViewController()
72   - ..setJavaScriptMode(JavaScriptMode.unrestricted)
73   - ..setBackgroundColor(const Color(0x00000000))
74   - ..setNavigationDelegate(
75   - NavigationDelegate(
76   - onProgress: (int progress) {
77   - // Update loading bar.
78   - },
79   - onPageStarted: (String url) {
80   - EasyLoading.show();
81   - },
82   - onPageFinished: (String url) {
83   - EasyLoading.dismiss();
84   - },
85   - onWebResourceError: (WebResourceError error) {
86   - EasyLoading.showError(error.description);
87   - },
88   - onNavigationRequest: (NavigationRequest request) {
89   - return NavigationDecision.navigate;
90   - },
  71 + // WebViewController controller = WebViewController()
  72 + // ..setJavaScriptMode(JavaScriptMode.unrestricted)
  73 + // ..setBackgroundColor(const Color(0x00000000))
  74 + // ..setNavigationDelegate(
  75 + // NavigationDelegate(
  76 + // onProgress: (int progress) {
  77 + // // Update loading bar.
  78 + // },
  79 + // onPageStarted: (String url) {
  80 + // EasyLoading.show();
  81 + // },
  82 + // onPageFinished: (String url) {
  83 + // EasyLoading.dismiss();
  84 + // },
  85 + // onWebResourceError: (WebResourceError error) {
  86 + // EasyLoading.showError(error.description);
  87 + // },
  88 + // onNavigationRequest: (NavigationRequest request) {
  89 + // return NavigationDecision.navigate;
  90 + // },
  91 + // ),
  92 + // );
  93 + // await controller.loadRequest(Uri.parse(url));
  94 + // res = WebViewWidget(controller: controller);
  95 +
  96 + res = InAppWebView(
  97 + initialUrlRequest: URLRequest(url: Uri.parse(url)),
  98 + initialUserScripts: UnmodifiableListView<UserScript>([]),
  99 + initialOptions: InAppWebViewGroupOptions(
  100 + crossPlatform: InAppWebViewOptions(
  101 + useShouldOverrideUrlLoading: true, // 是否需要跳转
  102 + mediaPlaybackRequiresUserGesture: false, // 设置为true,方式H5的音频自动播放
  103 + transparentBackground: true
  104 + ),
  105 + android: AndroidInAppWebViewOptions(
  106 + useHybridComposition: true,
  107 + ),
  108 + ios: IOSInAppWebViewOptions(
  109 + allowsInlineMediaPlayback: true,
  110 + ),
91 111 ),
92   - );
93   - await controller.loadRequest(Uri.parse(url));
94   - res = WebViewWidget(controller: controller);
  112 + onWebViewCreated: (controller) {
  113 + // _inAppWebViewController = controller;
  114 + },
  115 + );
95 116 } catch (error) {
96 117 res = Text("加载失败:${error.toString()}");
97 118 debugPrint("WebViewController加载失败:${error.toString()}");
... ...
lib/pages/home/view.dart
  1 +import 'dart:io';
  2 +
1 3 import 'package:flutter/material.dart';
  4 +import 'package:flutter/services.dart';
2 5 import 'package:flutter_app_update/azhon_app_update.dart';
3 6 import 'package:flutter_app_update/update_model.dart';
4 7 import 'package:flutter_bloc/flutter_bloc.dart';
  8 +import 'package:limiting_direction_csx/limiting_direction_csx.dart';
5 9 import 'package:url_launcher/url_launcher.dart';
6 10 import 'package:wow_english/common/core/app_config_helper.dart';
7 11 import 'package:wow_english/common/core/app_consts.dart';
... ... @@ -112,18 +116,28 @@ class _HomePageView extends StatelessWidget {
112 116 builder: (context, userState) {
113 117 return GestureDetector(
114 118 onTap: () {
115   - _checkPermission(() async {
116   - await AudioPlayerUtil.getInstance().pause();
117   - Navigator.of(context).pushNamed(
118   - AppRouteName.webView,
119   - arguments: {
120   - 'urlStr': AppConsts.xiaoeShopUrl,
121   - 'webViewTitle': 'Wow精选'
122   - }).then((value) async => {
123   - await AudioPlayerUtil.getInstance().playAudio(
124   - AudioPlayerUtilType.touch),
125   - });
126   - }, bloc);
  119 + _checkPermission(() async {
  120 + await AudioPlayerUtil.getInstance().pause();
  121 + if (Platform.isIOS) {
  122 + await LimitingDirectionCsx.setScreenDirection(DeviceDirectionMask.Portrait);
  123 + } else {
  124 + await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp, DeviceOrientation.portraitDown]);
  125 + }
  126 + Navigator.of(context).pushNamed(
  127 + AppRouteName.webView,
  128 + arguments: {
  129 + 'urlStr': AppConsts.xiaoeShopUrl,
  130 + 'webViewTitle': 'Wow精选'
  131 + }).then((value) async => {
  132 + if (Platform.isIOS) {
  133 + await LimitingDirectionCsx.setScreenDirection(DeviceDirectionMask.Landscape),
  134 + } else {
  135 + await SystemChrome.setPreferredOrientations([DeviceOrientation.landscapeLeft, DeviceOrientation.landscapeRight]),
  136 + },
  137 + await AudioPlayerUtil.getInstance().playAudio(
  138 + AudioPlayerUtilType.touch),
  139 + });
  140 + }, bloc);
127 141 },
128 142 child: Offstage(
129 143 offstage: AppConfigHelper.shouldHidePay() ||
... ...
pubspec.yaml
... ... @@ -49,7 +49,9 @@ dependencies:
49 49 #Url跳转 https://pub.dev/packages/url_launcher
50 50 url_launcher: ^6.1.11
51 51 #网页加载 https://pub.dev/packages/webview_flutter
52   - webview_flutter: ^4.2.2
  52 +# webview_flutter: ^4.8.0
  53 + #https://pub.dev/packages/flutter_inappwebview
  54 + flutter_inappwebview: 5.8.0
53 55 #下拉刷新 https://pub.dev/packages/pull_to_refresh
54 56 pull_to_refresh: ^2.0.0
55 57 # 数据持久化 https://pub.dev/packages/shared_preferences
... ...