flutter Webview를 사용하고 있다면,
그리고 결제 모듈을 Webview로 연동하고 있다면 net::ERR_UNKNOWN_URL_SCHEME 라는 에러를 겪고, 고통 받고 있을 수도 있습니다.
이처럼 (퍼온 이미지 입니다)
그렇다면 해당 포스트를 잘 찾아 오셨습니다 👏🏿👏🏿👏🏿👏🏿👏🏿
해당 포스트는 5분안에 해당 문제를 해결 할 수 있게 끔, 예시 코드와 원인을 설명하고 있습니다.
GitHub - kimjuno97/webview_err_unknown_url_scheme_solution: Flutter `net::ERR_UNKNOWN_URL_SCHEME` solution
Flutter `net::ERR_UNKNOWN_URL_SCHEME` solution. Contribute to kimjuno97/webview_err_unknown_url_scheme_solution development by creating an account on GitHub.
github.com
주 원인
- webview_flutter나 flutter_inappwebview는 http/https가 아닌 스킴(intent://, kb-acp:// 등)을 기본적으로 지원하지 않기 때문에, 별도로 네이티브 코드나 MethodChannel, 또는 onNavigationRequest/shouldOverrideUrlLoading에서 직접 처리해줘야 합니다.
- 특히 KB Pay 등 일부 결제는 onNavigationRequest 콜백에도 잡히지 않아 추가적인 처리가 필요할 수 있습니다.
즉, 결제모듈 연동 시 가장 흔한 에러는 net::ERR_UNKNOWN_URL_SCHEME 에러이며, 이는 앱 스킴(intent 등) 미처리로 인해 발생합니다.
(출처 perplexity)
해결 방법
해결 방법으로는 첫번째로,
예시코드 프로젝트 기준 android/app/src/main/kotlin/com/example/MainActivity.kt에 MethodChannel 코드를 추가합나다.
MainActivity.kt에 따로 추가로 구현한 코드가 없다면 아래와 비슷한 형태로 구현을 마무리 할 수 있습니다.
class MainActivity: FlutterActivity(){
private val webviewChannel = "com.flutter.webview"
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
MethodChannel(flutterEngine!!.dartExecutor.binaryMessenger, webviewChannel).setMethodCallHandler {
call, result ->
when {
call.method.equals("getAppUrl") -> {
try {
val url: String = call.argument("url")!!
val intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME)
result.success(intent.dataString)
} catch (e: URISyntaxException) {
result.notImplemented()
} catch (e: ActivityNotFoundException) {
result.notImplemented()
}
}
call.method.equals("getMarketUrl") -> {
try {
val url: String = call.argument("url")!!
val packageName = Intent.parseUri(url, Intent.URI_INTENT_SCHEME).getPackage()
val marketUrl = Intent(
Intent.ACTION_VIEW,
Uri.parse("market://details?id=$packageName")
)
result.success(marketUrl.dataString)
} catch (e: URISyntaxException) {
result.notImplemented()
} catch (e: ActivityNotFoundException) {
result.notImplemented()
}
}
}
}
}
}
코드를 간력하게 설명하자면,
getAppUrl 호출 시
- 목적: intent:// 스킴 URL → 실제 앱 실행 가능한 URI 변환
- 동작 과정:
- Flutter에서 url 파라미터 전달 (예: intent://payment#Intent;package=com.kb.pay;end)
- Intent.parseUri()로 안드로이드 인텐트 객체 생성
- intent.dataString 추출 (예: kb-acp://payment)
- 변환된 URI를 Flutter로 반환
getMarketUrl 호출 시
- 목적: 앱 미설치 시 → Play Store 이동용 URL 생성
- 동작 과정:
- Flutter에서 url 파라미터 전달
- 인텐트에서 패키지명 추출 (예: com.kb.pay)
- market://details?id=com.kb.pay 형태로 URL 생성
- 마켓 URL을 Flutter로 반환
두번째로는, Flutter에서
WebViewController의 setNavigationDelegate에서 android일 경우에만 getAppUrl, getMarketUrl 호출하는 로직을 구현합니다.
late final _controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setNavigationDelegate(
NavigationDelegate(
onNavigationRequest: (NavigationRequest request) async {
final Uri uri = Uri.parse(request.url);
final String finalUrl = request.url;
if (uri.scheme == "http" || uri.scheme == 'https') {
return NavigationDecision.navigate;
}
// Intent URL일 경우, OS별로 구분하여 실행
if (defaultTargetPlatform == TargetPlatform.android) {
/// [MEMO] Android의 경우, Native(kotlin)으로 url을 전달해 INTENT처리 후 리턴받는다.
final appScheme = await _convertIntentToAppUrl(request.url);
try {
if (appScheme != null) {
await launchUrlString(
appScheme,
mode: LaunchMode.externalApplication,
);
}
} catch (e) {
// 앱이 설치되어 있지 않는 경우, playStore로 이동
final appMarketUrl = await _convertIntentToMarketURl(request.url);
if (appMarketUrl != null) {
await launchUrlString(
appMarketUrl,
mode: LaunchMode.externalApplication,
);
}
}
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
launchUrlString(finalUrl);
}
return NavigationDecision.prevent;
},
),
)
..loadRequest(
Uri.parse(webviewUrl),
)
/// javascriptChannel 사용시
..addJavaScriptChannel(
'채널명',
onMessageReceived: (_) {},
);
정리하자면,
WebviewController는 http/https가 아닌 스킴(intent://, kb-acp:// 등)을 라우팅 할 수 없으므로, MethodChannel로 안드로이드 네이티브 코드로 넘긴 이후에, Intent.parseUri() 절차를 가진 후에 다시 받아 온 후에 라우팅을 한다고 생각하면 된다.
뭔차이가 있나 싶을 수도 있을것이다.
현대카드로 결제를 한다고 가정하고 finalUrl과 appSheme을 print해보면 아래처럼 나옵니다.
finalString >> intent:hdcardappcardansimclick://appcard?acctid=*******#Intent;package=com.hyundaicard.appcard;end;
appScheme >> hdcardappcardansimclick://appcard?acctid=*******
더 궁금한게 있다면 댓글로 남겨주시면 답변드리겠습니다.
'flutter' 카테고리의 다른 글
flutter, js_interop 사용 후기 및 언제 사용할까 (0) | 2025.04.30 |
---|---|
infinity_scroll_shell 오픈소스 배포 과정 기록 (6) | 2024.09.18 |
Flutter - toss payment결제 페이지 webView 연동 (0) | 2024.05.09 |
flutter Understanding constraints (0) | 2024.05.08 |
Flutter Android 12이상에서의 defalut 스플래시 화면 처리 솔루션 (2) | 2023.12.05 |
댓글