一、在原生安卓中的使用
在原生安卓中一般採用的是@JavascriptInterface註解和WebView的addJavascriptInterface方法實現橋接
- 實現一個@JavascriptInterface的註解。
class JavaScriptCall() {
@JavascriptInterface
fun CallApp(string: String) {
//{"action":"geolocationGet","jsCallBackId":"func1703575966310","params":{"coordinate":1,"withReGeocode":false}}
LogUtils.e("CallApp" + string)
}
}
- 繼承WebView使用addJavascriptInterface添加橋接方法。
class BridgeWebView : WebView{
var bridgeJs = JavaScriptCall(this)
constructor(context: Context) : super(context) {
init()
}
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
init()
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init()
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) {
init()
}
private fun init() {
addJavascriptInterface(bridgeJs, "ISANDNative")
}
}
- js端調用橋接方法。
// JavaScript代碼調用Android原生
ISANDNative.CallApp('{"action":"closeWebView","jsCallBackId":"...","params":{...}}');
二、在flutter端的使用
- 在pubspec.yaml中引用webview的包。
webview_flutter: ^4.0.7
- 創建一個widget實現WebViewController和WebViewWidget(老版的WebView已不建議使用)。
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:webview_flutter/webview_flutter.dart';
class UserAgreementWebPage extends StatefulWidget {
final String url;
final String title;
const UserAgreementWebPage({Key? key, required this.url, this.title = ''}) : super(key: key);
@override
State<UserAgreementWebPage> createState() => _UserAgreementWebPageState();
}
class _UserAgreementWebPageState extends State<UserAgreementWebPage> {
late final WebViewController _controller;
bool _isLoading = true;
@override
void initState() {
super.initState();
// 初始化WebViewController
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel(
'ISANDNative',
onMessageReceived: (JavaScriptMessage message) {
print('接收到JS消息: ${message.message}');
Get.back();
},
)
// 添加額外的錯誤處理通道,便於調試
..addJavaScriptChannel(
'debugChannel',
onMessageReceived: (JavaScriptMessage message) {
print('調試信息: ${message.message}');
},
)
..setNavigationDelegate(
NavigationDelegate(
onPageStarted: (String url) {
setState(() {
_isLoading = true;
});
},
onPageFinished: (String url) {
setState(() {
_isLoading = false;
});
},
onWebResourceError: (WebResourceError error) {
setState(() {
_isLoading = false;
});
// 處理錯誤
print('Web錯誤: ${error.description}');
},
onNavigationRequest: (NavigationRequest request) async {
print("onNavigationRequest:${request.url}");
return NavigationDecision.navigate;
},
),
)
..loadRequest(Uri.parse(widget.url));
}
@override
Widget build(BuildContext context) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarColor: Colors.white, // 設置狀態欄背景色
),
child: SafeArea(
child: Stack(
children: [
WebViewWidget(controller: _controller),
if (_isLoading)
const Center(
child: CircularProgressIndicator(),
),
],
),
),
);
}
@override
void dispose() {
// 恢復狀態欄的默認樣式
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent, // 根據您應用的默認樣式設置
statusBarIconBrightness: Brightness.dark, // 根據您應用的默認樣式設置
));
super.dispose();
}
}
其中的addJavaScriptChannel即是添加橋接的方法,ISANDNative為通道名稱,onMessageReceived中為收到的消息。
- 通過上述配置後會發現實際還是收不到通道的消息,這是因為原生安卓和flutter調用的差異導致的。
Android原生調用方式:
// JavaScript代碼調用Android原生
ISANDNative.CallApp('{"action":"closeWebView","jsCallBackId":"...","params":{...}}');
Flutter調用方式:
// JavaScript代碼調用Flutter
ISANDNative.postMessage('{"action":"closeWebView","jsCallBackId":"...","params":{...}}');
如果Web端採用了flutter的調用方式,我們就可以正常收到通道的消息了。
- 如果您無法修改Web頁面的JavaScript代碼,可以在Flutter中注入一個適配腳本,將CallApp調用轉換為postMessage。完整代碼如下:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:get/get.dart';
import 'package:webview_flutter/webview_flutter.dart';
class UserAgreementWebPage extends StatefulWidget {
final String url;
final String title;
const UserAgreementWebPage({Key? key, required this.url, this.title = ''}) : super(key: key);
@override
State<UserAgreementWebPage> createState() => _UserAgreementWebPageState();
}
class _UserAgreementWebPageState extends State<UserAgreementWebPage> {
late final WebViewController _controller;
bool _isLoading = true;
@override
void initState() {
super.initState();
// 初始化WebViewController
_controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..addJavaScriptChannel(
'ISANDNative',
onMessageReceived: (JavaScriptMessage message) {
print('接收到JS消息: ${message.message}');
Get.back();
},
)
// 添加額外的錯誤處理通道,便於調試
..addJavaScriptChannel(
'debugChannel',
onMessageReceived: (JavaScriptMessage message) {
print('調試信息: ${message.message}');
},
)
..setNavigationDelegate(
NavigationDelegate(
onPageStarted: (String url) {
setState(() {
_isLoading = true;
});
},
onPageFinished: (String url) {
// 注入適配腳本,將CallApp調用轉換為postMessage
_controller.runJavaScript('''
// 創建一個包裝器來捕獲CallApp調用
window.ISANDNative_Original = window.ISANDNative;
window.ISANDNative = {
CallApp: function(jsonString) {
// 轉換為Flutter的調用方式
window.ISANDNative_Original.postMessage(jsonString);
}
};
''');
setState(() {
_isLoading = false;
});
},
onWebResourceError: (WebResourceError error) {
setState(() {
_isLoading = false;
});
// 處理錯誤
print('Web錯誤: ${error.description}');
},
onNavigationRequest: (NavigationRequest request) async {
print("onNavigationRequest:${request.url}");
return NavigationDecision.navigate;
},
),
)
..loadRequest(Uri.parse(widget.url));
}
@override
Widget build(BuildContext context) {
return AnnotatedRegion<SystemUiOverlayStyle>(
value: SystemUiOverlayStyle(
statusBarColor: Colors.white, // 設置狀態欄背景色
),
child: SafeArea(
child: Stack(
children: [
WebViewWidget(controller: _controller),
if (_isLoading)
const Center(
child: CircularProgressIndicator(),
),
],
),
),
);
}
@override
void dispose() {
// 恢復狀態欄的默認樣式
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent, // 根據您應用的默認樣式設置
statusBarIconBrightness: Brightness.dark, // 根據您應用的默認樣式設置
));
super.dispose();
}
}
- 經過上述操作後,就可以在日誌打印中看到我們收到的消息了。
本文章為轉載內容,我們尊重原作者對文章享有的著作權。如有內容錯誤或侵權問題,歡迎原作者聯繫我們進行內容更正或刪除文章。