[068] 플러터 (Flutter) 배우기 - WebView(웹뷰) JavaScript 통신(데이터 주고받기)

2023. 7. 15. 14:58모바일어플개발/Flutter

반응형

안녕하세요~ totally 개발자입니다.

 

WebView JavaScript Communication

 

Flutter로 개발을 하다보면 웹뷰를 통해서 구현하는 경우가 필요합니다. 웹뷰를 통해서 구현할 때 웹 쪽과 플러터 앱 사이에 통신이 필요한 경우가 있습니다. 통신한다는 이야기는 데이터를 주고 받는 것을 말하며 주로 url 뒤에 데이터를 붙여 parameter 값을 넘기거나 웹 쪽의 자바스크립트 함수를 호출하는 방법이 있습니다. 이 포스팅에서는 자바스크립트 함수를 호출하는 방법을 통해 통신해보도록 하겠습니다.

사용한 패키지는 webview_flutter이며 최신 버전을 사용하면 아래 방법으로 구현이 안 되기 때문에 조금 이전 버전을 사용해야 하며 저는 3.0.4 버전을 사용하였습니다. 참고로 웹뷰로 사용할 웹사이트는 호스팅이 되어 있는 상태여야 가능하므로 준비해주셔야 합니다. 저는 000webhost를 사용하였습니다.

 

Step 1: pubspec.yaml에 webview_flutter 패키지 3.0.4 버전을 추가하고 Command(Ctrl) + S 또는 터미널에 flutter pub get 해줍니다.

 

 

 

Step 2: 웹뷰로 사용할 파일 web_view_test.html을 생성하시고 다음처럼 자바스크립트 함수를 작성하여 줍니다. 71-73번째 라인은 앱에서 웹으로 데이터를 전달할 함수이며 75-77번째 라인은 웹에서 앱으로 데이터를 전달할 함수입니다. 아래에서 보시겠지만, 웹에서 앱으로 데이터를 전달할 때에는 플러터의 javascriptChannels를 통해 데이터를 전달할 수 있습니다. 그 때 필요한 name을 지정한 것이 toApp이며 (이름 변경 가능) postMessage를 통해 데이터를 전달할 수 있습니다.

 

그리고 아래에서 메시지를 출력해줄 div 태그와 버튼을 만들어 onclick 속성을 통해 함수를 호출할 수 있도록 해줍니다.

 

Step 3: webViewPage.dart 파일을 만드신 후에 2번째 라인에 webview_flutter를 임포트해주시고 12번째 라인처럼 WebViewController 변수를 선언해줍니다.

 

 

 

Step 4: WebView 위젯을 만들어주시고 아래처럼 구성해주시면 됩니다. 설명을 스크린샷에 달아놓았습니다. 웹뷰에서 버튼을 눌러 함수를 호출하고 난 다음 메인 페이지로 돌아올 수 있도록 구성합니다.

 

 

 

Step 5: 웹뷰를 호출할 수 있는 메인 페이지를 만들어줍니다. 

 

Step 6: 실행하여 테스트해본 모습입니다.

 

 

위처럼 console에도 메시지가 잘 나오면 성공입니다.

 

[전체 소스 코드]

 

[web_view_test.html]

 

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>My Website</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
background-color: #f2f2f2;
color: #333;
margin: 0;
padding: 0;
}
</style>
<script>
function fromAppToWeb(msg) {
document.querySelector("#flutterMessageTitle").innerText = msg;
}
function fromWebToApp(msg) {
toApp.postMessage(msg);
}
</script>
</head>
<body>
<!-- Navigation bar -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<a class="navbar-brand" href="index.html">My Website</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item active">
<a class="nav-link" href="index.html">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="about.html">About</a>
</li>
<li class="nav-item">
<a class="nav-link" href="contact.html">Contact</a>
</li>
</ul>
</div>
</nav>
<br />
<div id="flutterMessageTitle" style="text-align:center;">
</div>
<br />
<div style="text-align:center;">
<button class="btn btn-primary" onclick="fromWebToApp('JAVASCRIPT WEB MESSAGE')">돌아가기</button>
</div>
</body>
</html>

 

[webviewPage.dart]

 

import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
class WebViewPage extends StatefulWidget {
const WebViewPage({super.key});
@override
State<WebViewPage> createState() => _WebViewPageState();
}
class _WebViewPageState extends State<WebViewPage> {
late final WebViewController _controller;
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: WebView(
initialUrl:
"https://testprojectmanager.000webhostapp.com/web_view_test.html",
onWebViewCreated: (controller) {
_controller = controller;
},
onPageFinished: (url) {
String message = "FLUTTER MESSAGE";
_controller.runJavascript('fromAppToWeb("$message")');
print("플러터앱에서 웹으로 메시지 전송: $message");
},
javascriptMode: JavascriptMode.unrestricted,
javascriptChannels: <JavascriptChannel>{
JavascriptChannel(
name: 'toApp',
onMessageReceived: (JavascriptMessage message) {
print("웹에서 플러터앱으로 메시지 전송: ${message.message}");
Navigator.of(context).pop();
}),
},
),
),
);
}
}

 

[main.dart]

 

import 'package:flutter/material.dart';
import 'package:project/webviewPage.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const MaterialApp(
title: 'Flutter App',
home: MainPage(),
);
}
}
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);
@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Center(
child: ElevatedButton(
onPressed: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => const WebViewPage()));
},
child: const Text("웹뷰 호출"),
),
),
),
);
}
}
view raw 068_main.dart hosted with ❤ by GitHub

 

 

반응형