Web을 기반으로 하는 App을 만들다 보니 intent://, snssdk1180:// 등과 같이 웹프로토콜이 아닌 App Scheme로 링크를 요청하는 경우에는 WebView 자체에서 처리를 하지 못합니다.
이런 경우에 처리하는 방법입니다.
우선 기본 옵션을 변경해야합니다.
initialOptions: InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
useShouldOverrideUrlLoading: true,
...
),
...
위와 같이 옵션을 변경하시면 InAppWebView에서 제공하는 EventHandler 중
shouldOverrideUrlLoading 함수를 이용 가능합니다.
이제 아래에서, Android에 method를 invoke 해서 intent를 처리하는 로직을
작성해보도록 하겠습니다.
우선 Kotlin 으로 해서 Method Channel 을 구성해야합니다.
package com.example.inappwebviewintent
import android.content.ActivityNotFoundException
import android.content.Intent
import android.util.Log
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
import java.net.URISyntaxException
class MainActivity: FlutterActivity() {
private val CHANNEL = "Channel Name"
//MethodChannel 구현
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
if(call.method == "getAppUrl") { // Intent:// 스키마를 통한 URL파싱
try {
val url: String? = call.argument("url")
if(url == null) {
result.error("9999", "URL PARAMETER IS NULL", null)
} else {
Log.i("[getAppUrl] url", url)
val intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME)
result.success(intent.dataString)
}
} catch (e: URISyntaxException) {
result.notImplemented()
} catch (e: ActivityNotFoundException) {
result.notImplemented()
}
} else if(call.method == "getMarketUrl") { // 들어온 URL을 통해 package 명 및 market 다운로드 주소 반환
try {
val url: String? = call.argument("url")
if(url == null) {
result.error("9999", "URL PARAMETER IS NULL", null)
} else {
Log.i("[getMarketUrl] url", url)
val intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME)
val scheme = intent.scheme
val packageName = intent.getPackage()
if (packageName != null) {
result.success("market://details?id=$packageName")
}
result.notImplemented()
}
} catch (e: URISyntaxException) {
result.notImplemented()
} catch (e: ActivityNotFoundException) {
result.notImplemented()
}
} else {
result.notImplemented()
}
}
}
}
이후 Flutter에서 해당 Method Channel을 이용하면 됩니다.
우리는 url_launcher 를 사용했습니다.
https://pub.dev/packages/url_launcher
아래와 같이 채널명을 선언을 합니다.
Kotlin에서 선언한 것과 동일하게 선언해야합니다.
static const methodChannel = MethodChannel('Channel Name');
아래 함수는 Url에서 Scheme가 어떤 것인지를 확인하는 함수입니다.
bool isAppLink(Uri url) {
final appScheme = url.scheme;
return appScheme != 'http' &&
appScheme != 'https' &&
appScheme != 'about:blank' &&
appScheme != 'data';
}
아래는 InAppWebView Method 내에서 사용하는 방법입니다.
shouldOverrideUrlLoading: (controller, navigationAction) async {
print('====================shouldOverrideUrlLoading====================');
var curUrl = navigationAction.request.url;
if (curUrl == null) return NavigationActionPolicy.CANCEL;
// URL String의 Shceme가 http, https 가 아닌 지 검거
if (isAppLink(curUrl)) {
await controller.stopLoading();
var scheme = curUrl.scheme;
if (scheme == "intent") { // 일반적인 Intent의 경우
if (Platform.isAndroid) {
try {
final parsedIntent = await methodChannel.invokeMethod('getAppUrl', {'url': curUrl.toString()});
print(parsedIntent);
if (await canLaunchUrl(Uri.parse(parsedIntent))) {
launchUrl(parsedIntent);
} else {
final marketUrl = await methodChannel.invokeMethod('getMarketUrl', {'url': curUrl.toString()});
launchUrl(marketUrl);
}
} on PlatformException catch (e) {
// 오류 처리
print('${e.message}');
}
}
} else if (scheme.contains('snssdk')) { // TikTok 인경우 이미 파싱이 되어진 상태로 넘어온다. 따라서, 실행가능여부 검사 후 실행 및 패키지 다운로드로 진행
if (Platform.isAndroid) {
try {
if (await canLaunchUrl(curUrl)) {
launchUrl(curUrl.toString());
} else {
launchUrl('market://details?id=com.ss.android.ugc.trill');
}
} on PlatformException catch (e) {
// 오류 처리
print('${e.message}');
}
}
}
return NavigationActionPolicy.CANCEL;
} else {
return NavigationActionPolicy.ALLOW;
}
print(
'====================shouldOverrideUrlLoading====================');
}
InAppWebView를 사용해서 타사의 웹을 띄우는것이 쉬울줄 알았지만 이런 부분도 해결해야하는지는 이번 기회에 알게되었답니다.
즐거운 Fluttering 되세요.
글 내용이 마음에 드시면 좋아요! 구독 부탁드립니다.
[Flutter]App Bundle command (0) | 2023.06.02 |
---|---|
[햄버거 메뉴]Flutter Drawer 제어방법 (0) | 2023.05.24 |
[MANAGE_EXTERNAL_STORAGE] App Reject 보완하기 (0) | 2023.05.23 |
[Flutter] GetX로 페이지 이동시 오류 아닌 오류 (0) | 2023.05.19 |
[Flutter] Safari Debug 하는 방법 (0) | 2023.05.03 |