왜 웹뷰에서 버튼을 눌렀을 때
흰색 화면만 보일까?
안녕하세요. 앱개밸자지식나눔입니다.
오늘은 플러터 패키지인 InAppWebView에서 새로운 창(탭)을 여는 법에 대해서 알아보겠습니다. 그전에 저는 사람들에게 간단한 화면 보기는 url_launcher 혹은 InAppWebView 패키지의 ChromeSafariBrowser를 사용하는 것을 추천하고 있습니다. 자세한 것은 아래 포스트를 확인해주세요.
[Flutter] - [플러터] 대표 웹뷰 패키지 소개 및 장단점
하지만 회사에 맞게 아니면 서비에 맞는 기능을 충족하려면 InAppWebView 클래스를 사용해야 하는 경우가 생깁니다.
그럼 한 번 알아가볼까요?
InAppWebView 패키지: https://inappwebview.dev/docs/in-app-webview/basic-usage/
사전에 알면 좋은 것
1. InAppWebView 패키지와 달리 WebView 패키지는 새창 열기가 불가능하다.
2. 새창 열기는 html 언어에서 window.open() 자바스크립트로 실행된다.
3. InAppWebView 패키지에서 window.open()을 캐치할 수 있는 부분은 onCreateWindow 메서드이다.
새창 열기 소스코드
유저가 버튼을 클릭하면 html에서 onclick이 설정된 부분이 실행된다. onclick에 해당되는 부분에 window.open(html에서 새로운 창을 여는 함수)가 있으면 이것이 실행되며 새로운 탭이 열린다. 하지만 플러터 InAppWebView에서는 일일이 다 설정해야 한다. (이것이 가장 큰 단점이다...)
HTML 코드 중 Window.open 부분
<p class="font-size-small text-center text-1">Window Open Button</p>
<button type="button" onclick="openThisWindow(this.name, 'b', 'g', 'common', '', 'testcode11', 'app')" name="adult" class="button-blue-big color-white font-weight-bold font-size-normal-more mx-auto button-0">본인인증</button>
function openDRMOKWindow(type, coworker, service, a_type, kitid, client_key, machine)
{
window.name = "parentPage";
DRMOK_window = window.open('', 'DRMOKWindow', 'width=425, height=550, resizable=0, scrollbars=no, status=0, titlebar=0, toolbar=0, left=435, top=250' );
document.form1.action = 'https://www.duam.net'
}
그래서 InAppWebView 클래스의 onCreateWindow 메서드에 일단 새창을 플러터 코드로 일단 만들어야 한다. 그중에서 가장 많이 쓰이는 것이 AlertDialog의 parent 위젯인 showDialog이다. 나는 팝업 형식이 아닌 아예 다른 화면을 띄워야 해서(PASS 화면) alertDialog를 제외했다.
그전에 ShowDialog와 AlertDialog의 관계에 대해서 간단히 알아보고 가자.
우리는 경고창, 팝업창이라 불리는 AlertDialog를 자주 쓴다. 이것은 기존 화면 위에 띄우는 UI이다. 그러면 어떻게 이것을 기존 창 위에 띄울 수 있을까? 바로 showdialog의 역할이 있기 때문이다. 정리하면 ShowDialog는 기존 화면 위에 무엇을 띄울 때 쓰이고, 그 위에 경고 창인 alertdialog를 띄우는 것이다. 즉 showdialog가 alertdialog보다 상위 위젯이다.
위 사진에서 보면 나는 html에서 window.open가 실행될 때 onCreateWindow를 실행하게 구성하였다. 그다음 showdialog를 실행시켜 지금 웹 화면 위에 무언가를 띄우라고 지시했고, 새롭게 만들어진 창에 새로운 InAppWebView를 실행하도록 구성했다. 새로운 InAppWebView에서 다음 홈페이지가 실행될 것이다.
전체 소스코드
import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:html/dom.dart' as Document;
import 'dart:io';
import 'package:html/parser.dart' as parser;
import 'package:cp949/cp949.dart' as cp949;
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'package:url_launcher/url_launcher.dart';
Future main() async {
WidgetsFlutterBinding.ensureInitialized();
if (Platform.isAndroid) {
await AndroidInAppWebViewController.setWebContentsDebuggingEnabled(true);
}
runApp(MaterialApp(
home: MyApp(),
));
}
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
final GlobalKey webViewKey = GlobalKey();
InAppWebViewController webViewController;
PullToRefreshController pullToRefreshController;
String url = "";
double progress = 0;
final urlController = TextEditingController();
String res;
InAppWebViewGroupOptions options;
String id;
String naver;
String daum;
@override
void initState() {
super.initState();
id = 'testcode11';
naver = 'https://www.naver.com;
Daum = ‘https://www.daum.net;
options = InAppWebViewGroupOptions(
crossPlatform: InAppWebViewOptions(
javaScriptEnabled: true,
javaScriptCanOpenWindowsAutomatically: true,
useShouldOverrideUrlLoading: true,
mediaPlaybackRequiresUserGesture: false,
),
android: AndroidInAppWebViewOptions(
useHybridComposition: true,
),
ios: IOSInAppWebViewOptions(
allowsInlineMediaPlayback: true,
));
pullToRefreshController = PullToRefreshController(
options: PullToRefreshOptions(
color: Colors.blue,
),
onRefresh: () async {
if (Platform.isAndroid) {
webViewController?.reload();
} else if (Platform.isIOS) {
webViewController?.loadUrl(
urlRequest: URLRequest(url: await webViewController?.getUrl()));
}
},
);
}
@override
void dispose() {
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("일반 InAppWebView 테스트 코드")),
body: SafeArea(
child: Column(children: <Widget>[
Expanded(
child: Stack(
children: [
InAppWebView(
key: webViewKey,
initialUrlRequest: URLRequest(
url: Uri.parse(
naver)),
initialOptions: options,
pullToRefreshController: pullToRefreshController,
onWebViewCreated: (controller) async {
},
onLoadStart: (controller, url) async {
setState(() {
this.url = url.toString();
urlController.text = this.url;
});
},
onCreateWindow: (controller, action) {
return showDialog(
barrierDismissible: true,
context: context,
builder: (context) {
return InAppWebView(
initialOptions: options,
pullToRefreshController: pullToRefreshController,
onWebViewCreated: (controller) async {
print(await controller.getProgress());
},
onConsoleMessage: (controller, message) {
print("message is:" + message.message);
},
}
},
initialUrlRequest: URLRequest(
url: Uri.parse(
daum),
),
);
});
},
androidOnPermissionRequest:
(controller, origin, resources) async {
return PermissionRequestResponse(
resources: resources,
action: PermissionRequestResponseAction.GRANT);
},
shouldOverrideUrlLoading:
(controller, navigationAction) async {
var uri = navigationAction.request.url;
if (![
"http",
"https",
"file",
"chrome",
"data",
"javascript",
"about"
].contains(uri.scheme)) {
if (await canLaunch(url)) {
// Launch the App
await launch(
url,
);
// and cancel the request
return NavigationActionPolicy.CANCEL;
}
}
return NavigationActionPolicy.ALLOW;
},
onLoadStop: (controller, url) async {
}
pullToRefreshController.endRefreshing();
setState(() {
this.url = url.toString();
urlController.text = this.url;
});
},
onLoadError: (controller, url, code, message) {
pullToRefreshController.endRefreshing();
},
),
],
),
),
]))),
);
}
}
헷갈리는 점
분명 코드를 읽어본 사람들은 이런 생각을 할 것이다. '왜 html 코드에도 다음 주소로 이동하라는 기능이 있고 플러터 코드에도 다음 주소로 이동하라는 기능이 있지?'라는 고민을 할 수 있다. 다시 말하지만 InAppWebView는 일일이 설정을 해줘야 한다. onCreateWindow가 실행되면 플러터 쪽에서도 설정해야 정상적으로 열린다. (나는 이 사실을 몰라 2주 넘게 삽질했다..)
결론
새창 열기가 실행되는 과정을 정리하면 해당 사이트(여기서는 네이버)에서 사용자가 버튼 클릭 => onclick 실행 => onclick에 해당하는 자바스크립트 함수 실행 => 이 함수에 속한 window.open 실행 => 플러터에서 window.open을 캐치하고 onCreateWindow실행 => onCreateWindow에 속한 ShowDialog 실행으로 기존 화면 위에 새 화면 띄우기 => 새로운 InAppWebView 실행 => 해당 URL Request(여기서 Daum 페이지)
이상 앱개발지식나눔이였습니다.
'Flutter' 카테고리의 다른 글
플러터 빌드 에러 시 해결법 (0) | 2022.12.17 |
---|---|
[플러터] PASS 인증 InAppWebView로 실행하기 (2) | 2022.03.06 |
[플러터] 대표 웹뷰 패키지 소개 및 장단점 (2) | 2022.03.03 |
[플러터] 부트페이 패키지 연동시 주의사항 (0) | 2022.03.03 |