목적 : 네이티브와 웹뷰간의 양방향 통신 방법을 알아본다.
네이티브 : 서버와 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 |