목적 : 네이티브와 웹뷰간의 양방향 통신 방법을 알아본다.
네이티브 : 서버와 tcp/ip 소켓접속 후 데이터 전송하여 받아오는 역할을 한다.
웹뷰: input 정보를 네이티브에 전송하고 서버에서 받아온 응답 데이터를 네이티브에서 받아온다.
현재까지는 hexString 으로 input 값으로 전달한후 output 값도 hexString 으로 받아온다.
output
- connect : tcp/ip 소켓 접속연결한다.
- sendTr : 연결된 소켓으로 input hexString 보낸다.
Flutter
프로젝트 구조
main.dart
/*
* https://pub.dev/packages/webview_flutter
* https://kbwplace.tistory.com/176
*
*/
import 'dart:convert';
import 'dart:developer';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';
import 'SocketManager.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
/*
* tcp/ip 소켓 connect / send / close Class
*/
final socketManager = SocketManager();
/*
* 웹뷰 컨트롤러 설정
JavaScriptMode.disabled: WebView에서 JavaScript를 비활성화합니다. 웹 페이지의 JavaScript 코드가 실행되지 않습니다.
JavaScriptMode.unrestricted: WebView에서 JavaScript를 활성화합니다. 웹 페이지의 JavaScript 코드가 실행되며, JavaScript로 인터랙티브한 동작이 가능해집니다.
setBackgroundColor(const Color(0x00000000)): 이 부분은 WebView의 배경 색상을 설정하는 것입니다. 여기서 Color(0x00000000)은 투명한 배경색을 나타냅니다. 즉, WebView가 투명한 배경을 가지게 됩니다.
*/
late final controller = WebViewController()
..setJavaScriptMode(JavaScriptMode.unrestricted)
..setBackgroundColor(const Color(0x00000000))
..setNavigationDelegate(
NavigationDelegate(
onProgress: (int progress) {
// update loading bar.
},
onPageStarted: (String url) {},
onPageFinished: (String url) {},
onWebResourceError: (WebResourceError error) {},
onNavigationRequest: (NavigationRequest request) {
if (request.url.startsWith('https://www.youtube.com/')) {
return NavigationDecision.prevent;
}
return NavigationDecision.navigate;
},
),
)
/*
addJavaScriptChannel('Toaster', onMessageReceived: (JavaScriptMessage message) { ... }): 이 부분은 WebView에 JavaScript 채널을 추가하는 것입니다.
채널은 JavaScript와 Flutter 사이의 통신을 가능하게 해주는 메커니즘입니다.
여기서 'Toaster'는 채널의 이름을 나타냅니다.
이 이름을 기반으로 JavaScript에서 해당 채널을 호출하고, Flutter에서 해당 채널로 메시지를 수신할 수 있습니다.
onMessageReceived: (JavaScriptMessage message) { ... }:
이 부분은 JavaScript에서 Flutter로 메시지를 전달할 때 호출되는 콜백 함수를 정의하는 것입니다.
JavaScriptMessage는 flutter_inappwebview 라이브러리에서 제공되는 클래스로서, JavaScript에서 전달된 메시지를 Flutter에서 받을 때 사용합니다.
따라서 addJavaScriptChannel을 사용하여 'Toaster'라는 이름의 채널을 추가하고, 해당 채널로 메시지가 전달되면 onMessageReceived 콜백 함수가 호출됩니다.
이때, message 매개변수를 통해 JavaScript에서 전달된 메시지를 받을 수 있습니다.
실제 웹페이지의 javascript 확인해보면 Toaster 로 된 메시지가 있습니다.
*/
..addJavaScriptChannel('Toaster', onMessageReceived: (JavaScriptMessage message) {
_web2flutter(message);
})
..loadRequest(
Uri.parse('http://220.72.212./flutter_index2.html')
);
/*
* 사용자 정의 함수
*/
_reload() {
controller.reload();
}
_web2flutter(JavaScriptMessage message) {
/*
* json 형태를 객체로 변경
*/
Map<String, dynamic> parsedJson = jsonDecode(message.message);
var action = parsedJson['action'];
var sendHexString = parsedJson['hexString'];
var receivedHexString = '';
switch(action) {
case 'connect':
socketManager.connectToSocket(); // 소켓 접속한다.
break;
case 'sendTr' :
socketManager.sendTr(sendHexString); // 접속된 소켓에 데이터를 전송한다. input
receivedHexString = socketManager.hexString; // 응답 데이터 가져온다. output
break;
}
print('received222 : $receivedHexString');
/*
응답한 hexString 을 웹뷰의 javascript flutter2web() 함수로 전달한다.
*/
controller.runJavaScript("flutter2web('$receivedHexString')");
}
dd(String str) {
if (kDebugMode) {
print(str);
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Flutter WebView 11'),
),
body: WebViewWidget(
controller: controller,
),
);
}
@override
void reassemble() {
super.reassemble();
_reload();
}
}
SocketManageer.dart
import 'dart:io';
import 'dart:typed_data';
import 'package:hex/hex.dart';
class SocketManager {
late Socket _socket;
bool _connected = false;
late String _hexString = '';
static const String host = "13.125.57."; // mymq
static const int port = 9001;
Future<void> connectToSocket() async {
try {
_socket = await Socket.connect(host, port);
print('Socket connected');
_connected = true;
_listenToSocket();
} catch (e) {
print('Failed to connect to socket: $e');
_connected = false;
}
}
void _listenToSocket() {
_socket.listen(
(List<int> data) {
// 데이터 수신 처리
//String message = String.fromCharCodes(data);
//print('Received: $message');
_hexString = HEX.encode(data);
//print('recived: $_hexString');
},
onError: (e) {
print('Socket error: $e');
_disconnect();
},
onDone: () {
print('Socket disconnected');
_disconnect();
},
);
}
Future<void> sendData(String message) async {
if (!_connected) {
print('Socket is not connected');
return;
}
try {
_socket.write(message);
await _socket.flush();
print('Sent: $message');
} catch (e) {
print('Failed to send data: $e');
_disconnect();
}
}
Future<void> sendTr(String message) async {
if (!_connected) {
print('Socket is not connected');
return;
}
List<int> bytes = HEX.decode(message);
try {
_socket.add(bytes);
await _socket.flush();
print('Sent: $message');
} catch (e) {
print('Failed to send data: $e');
_disconnect();
}
}
void _disconnect() {
_socket?.close();
_connected = false;
}
String get hexString {
return _hexString;
}
}
pubspec.yaml
name: webview_flutter_2308
description: A new Flutter project.
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# In Android, build-name is used as versionName while build-number used as versionCode.
# Read more about Android versioning at https://developer.android.com/studio/publish/versioning
# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.
# Read more about iOS versioning at
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
# In Windows, build-name is used as the major, minor, and patch parts
# of the product and file versions while build-number is used as the build suffix.
version: 1.0.0+1
environment:
sdk: '>=2.19.1 <3.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
dev_dependencies:
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^2.0.0
webview_flutter: ^4.2.2
hex: ^0.2.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
안드로이드 설정
./android/app/build.gradle 파일을 열고 다음과 같이 수정합니다
android {
defaultConfig {
minSdkVersion 19
}
}
Html
Toaster.postMessage 를 이용하여 flutter 로 전송한다.
flutter_index2.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1 id="title">This is web title</h1>
<button type="button" onclick="connect()">connect</button>
<button type="button" onclick="send()">sendTr</button>
<script>
if (isWebView()) {
alert('app');
} else {
alert('web');
}
function isWebView() {
const userAgent = window.navigator.userAgent.toLowerCase();
return userAgent.indexOf('wv') > -1;
}
function send() {
// 자바스크립트에서 Flutter로 데이터를 전송하는 코드
const message = {
key: 'value', // 전송할 데이터
action:'sendTr',
hexString: '0000012931334331202020202020202020202020646f6d202020202057313931304130312020202020202020202020202020202020202020202020202020202020203030302e3030302e3030302e303030203030302e3030302e3030302e3030302030302d30302d30302d30302d30302d3030202020533031383238352020202020202020205748202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020202020207b7b2268223a2262617365222c2263223a225731393130413031222c226d223a22726563697665227d7d2020202020202020202020202020202020202020202020202020202020202020202020202020',
};
Toaster.postMessage(JSON.stringify(message));
}
function connect() {
const message = {
key: 'value',
action: 'connect',
hexString: '',
};
Toaster.postMessage(JSON.stringify(message));
}
function flutter2web(msg) {
alert(msg);
}
</script>
</body>
</html>
'프로그래밍 > flutter' 카테고리의 다른 글
[flutter] 소켓 통신 응답받을때 데이터 짤리는 문제 (0) | 2023.08.08 |
---|---|
[flutter] native(앱) 의 웹뷰(vue3) 양방향 통신 방법 webview_flutter 사용 (0) | 2023.08.08 |
flutter json -> object 로 변경 jsonDecode 사용 (0) | 2023.08.04 |
flutter webview 에서 localhost:8080 에러발생시 net::ERR_CONNECTION_TIMED_OUT (0) | 2023.06.29 |
flutter webview 기본샘플 (inappwebview 사용) (0) | 2023.06.29 |