<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
已經有很多關於 Flutter WebView 的文章了,為什麼還要寫一篇。兩個原因:
WebView 的文章分兩篇
本篇講 js 互動。首先了解下 4.0 有哪些重大變化。
第 2 條的變化讓我們不需要再寫判斷 android 的程式碼了。
還有 api 的變化。總的來說,讓我們的編碼更加容易了。
寫本文的時候,Flutter WebView 的版本是 4.0.2
雖然檔案上寫的是支援 addroid SDK 19+ or 20+, 但我們最好寫 21 或更高,不是說會影響 Flutter WebView 的使用,而是太低了會影響其它外掛的使用。如果能寫 23 就更好了,這樣可以用 Texture Layer Hybrid Compositiond 了。
android { defaultConfig { minSdkVersion 21 } }
iOS 支援 9.0 以上,新版本的 flutter 預設設定是 ios 11.0 ,所以我們按 Flutter 預設的設定就好。
安裝 webview_flutter
flutter pub add webview_flutter
一般舉例都是先發一個 hello world,咱們也發一個最簡單的,先跑起來。
完整程式碼,貼到 main.dart 就能執行
import 'package:flutter/material.dart'; import 'package:webview_flutter/webview_flutter.dart'; const htmlString = ''' <!DOCTYPE html> <head> <title>webview demo | IAM17</title> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no,viewport-fit=cover" /> <style> *{ margin:0; padding:0; } body{ background:#BBDFFC; display:flex; justify-content:center; align-items:center; height:100px; color:#C45F84; font-size:20px; } </style> </head> <html> <body> <div >大家好,我是 17</div> </body> </html> '''; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return const MaterialApp( home: Scaffold( body: SafeArea(child: MyWebView()), )); } } class MyWebView extends StatefulWidget { const MyWebView({super.key}); @override State<MyWebView> createState() => _MyWebViewState(); } class _MyWebViewState extends State<MyWebView> { late final WebViewController controller; double height = 0; @override void initState() { controller = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) ..loadHtmlString(htmlString); super.initState(); } @override Widget build(BuildContext context) { return Column( children: [Expanded(child: WebViewWidget(controller: controller))], ); } }
執行程式碼,你將看到如下內容
WebView 內容的可以通過網址獲取,但這樣不方便演示各種效果,所以直接用 htmlString 替代了,效果是一樣的。
預設情況下 javascript 是被禁用的。必須手動開啟 setJavaScriptMode(JavaScriptMode.unrestricted)
,否則對於絕大多數的網頁都沒法用了。
WebViewWidget 會嘗試讓自己獲得最大高度和最大寬度,所以 WebView 必須放在有限寬度和有限高度的 Widget 中。一般會用 SizedBox 這樣的容器把 WebView 包起來。但是 WebView 內容的高度是未知的,要如何設定 SizedBox 的 height 呢?
一種方案是 height 採用固定高度,如果 WebView 內容過多,可以用上下滑動的方式來檢視所有內容。如果 WebView 的內容高度是變化的,用固定高度可能會產生大塊空白,這個時候應該把 height 設定成 WebView 內容的高度。
那麼問題來了,如何獲得 WebView 內容的高度?最理想的情況是網頁是自己能控制的,讓網頁自己報告高度。
在 htmlString 中 增加 js
<body> <div class="content">大家好,我是 17</div> <script> const resizeObserver = new ResizeObserver(entries => Report.postMessage(document.scrollingElement.scrollHeight)) resizeObserver.observe(document.body) </script> </body>
如果WebView 不支援 ResizeObserver 可以直接在合適的時機呼叫 Report.postMessage(document.scrollingElement.scrollHeight))
dart 程式碼中
class _MyWebViewState extends State<MyWebView> { late final WebViewController controller; double height = 0; @override void initState() { controller = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) ..addJavaScriptChannel('Report', onMessageReceived: (message) { setState(() { height = double.parse(message.message); }); }) ..loadHtmlString(htmlString); super.initState(); } @override Widget build(BuildContext context) { return Column( children: [ SizedBox(height: height, child: WebViewWidget(controller: controller)), ], ); } }
修改 html 程式碼中的 body 的樣式 height:100px 為 height:200px;
,重新執行程式碼(restart,hot reload 不生效 ),發現 SizedBox 也變為 200px 高了。
如果頁面我們無權修改也沒有辦法協調修改,那就只能通過注入 js 方式獲取了。
如果頁面的高度只由靜態 css 決定,可以簡單的加一個小延時,直接獲取高度即可。
controller.setNavigationDelegate(NavigationDelegate( onPageFinished: (url) async { await Future.delayed(Duration(milliseconds: 50)); var message = await controller.runJavaScriptReturningResult( 'document.scrollingElement.scrollHeight'); setState(() { height =double.parse(message.toString()); }); }, ));
如果頁面載入完成後 js 又對頁面進行了修改,這個時間就很難預估了。js 可以隨時修改頁面,導致高度改變,所以要想時時跟蹤頁面高度,只能靠監聽。如果 webview 不支援 ResizeObserver,還可以用 setInterval。
void initState() { controller = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) ..addJavaScriptChannel('Report', onMessageReceived: (message) { var msgHeight = double.parse(message.message); setState(() { height = msgHeight; }); }) ..setNavigationDelegate(NavigationDelegate( onPageFinished: (url) async { // 注入 js controller.runJavaScript( '''const resizeObserver = new ResizeObserver(entries => Report.postMessage(document.scrollingElement.scrollHeight)) resizeObserver.observe(document.body)'''); }, )) ..loadHtmlString(htmlString); super.initState(); }
必須等到頁面載入完成後再注入 js,否則頁面檔案還不存在,往哪裡注入啊。
因為程式碼都在 dart 這邊,免去了和頁面開發溝通的成本。既使 WebView 載入的頁面中可能還有連結,跳到另一個地址,js 注入的程式碼依然有效!
頁面的高度可能會在很短時間內連續變化,我們可以只對最後一次的高度變化做更新,用 Timer 可以做到。頁面高度要限制一個最大值,否則超出最大允許的高度就報錯了。
可能你會覺得既然注入的方式這麼多優點,不需要頁面報告那種方式了,都用這種注入的方式就可以了。實際上每種方式都有它的利弊,不然我就不會介紹了。頁面報告的方式在於靈活,想什麼時候報告就什麼時候報告,頁面高度變化了,也可以不報告。在頁面沒有內容的時候可以先報告一個預估的高度,會讓頁面避免從 0 開始突然變高。儘量把主動權交給頁面,因為頁面是可以隨時修改的,app 不能!
url 以 /android 結尾時,跳到對應的原生頁面。否則繼續原來的請求。
onNavigationRequest: (request) { if (request.url.endsWith('/android')) { // 跳到原生頁面 return NavigationDecision.prevent; } else { // 繼續原來的請求 return NavigationDecision.navigate; } },
觸發方式有兩種
<a href='/ios'>跳到 Flutter 頁面</a>
window.location.href='完整頁面地址'
用 js 跳轉的地址一定是完整的頁面地址。比如這樣寫都是可以的
https://juejin.cn
aa:/bb
schema 可以自定義,但不能沒有。這樣寫是無效的 /android
先定義跳轉的通道物件為 Jump
void initState() { controller = WebViewController() ..setJavaScriptMode(JavaScriptMode.unrestricted) ..addJavaScriptChannel('Jump', onMessageReceived: (message) { //根據 message 資訊跳轉 }) ..loadHtmlString(htmlString); super.initState(); }
在頁面中執行 Jump.postMessage('video');
實際上,flutter 拿到頁面傳過來的資訊後,除了可以跳轉到 flutter 頁面,還可以執行其它功能,比如調取相機。
通過兩個範例演示了頁面與 flutter 通訊的 3 種方式
向頁面注入 js 需要等頁面載入完成後再注入。注入 js 的能力非常強大的。幾乎可以對頁面做任意修改。比如
刪除頁面中不想要的部分,這是有實際意義的。頁面都會有頁頭,這可能和 app 的頭部衝突。有了注入 js 這個利器,可以在不修改頁面的情況下,直接在 app 中不顯示頁頭。
修改頁面樣式,這個你懂的,既然能注入 js ,也就是能注入 css 了。相比於直接用 js 修改頁面樣式,注入 css 的方式更加容易維護。
當然了,凡事有利有弊,不要濫用這個功能。在 app 單方面修改頁面,將來頁面修改的時候可能會翻車,即使做好溝通,也會給頁面開發造成限制或麻煩,所以如何做一定要權衡各方面的得失。
app 不像頁面那樣可以隨時修改,所以要優先考慮讓頁面實現功能,儘量把控制權交給頁面(說兩遍了,因為很重要)。js 注入這種操作不是萬不得已不要做,把它做為最後的選項。
最後說一點,範例中為了方便演示用 loadHtmlString,實際應用中一般是用 loadRequest 載入網址。
loadHtmlString(htmlString) loadRequest(Uri.parse('https://juejin.cn'))
以上就是js 互動在Flutter 中使用 webview_flutter的詳細內容,更多關於js 互動webview_flutter的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45