Flutter 웹뷰에서 JavaScript 채널 구현하기

문제 상황

사내 어드민 시스템을 Flutter 앱에 웹뷰로 임베딩하는 작업을 진행했다. 웹에서 파일 업로드 시 네이티브 카메라를 호출해야 하는 요구사항이 생겼다. 순수 웹뷰만으로는 불가능해서 JavaScript와 네이티브 간 통신 방법을 찾아봤다.

해결 방법

webview_flutter 패키지(버전 1.0.7)의 JavascriptChannel을 사용했다.

import 'package:webview_flutter/webview_flutter.dart';

class AdminWebView extends StatefulWidget {
  @override
  _AdminWebViewState createState() => _AdminWebViewState();
}

class _AdminWebViewState extends State<AdminWebView> {
  WebViewController _controller;

  @override
  Widget build(BuildContext context) {
    return WebView(
      initialUrl: 'https://admin.example.com',
      javascriptMode: JavascriptMode.unrestricted,
      onWebViewCreated: (controller) {
        _controller = controller;
      },
      javascriptChannels: Set.from([
        JavascriptChannel(
          name: 'NativeChannel',
          onMessageReceived: (JavascriptMessage message) {
            _handleNativeCall(message.message);
          },
        ),
      ]),
    );
  }

  void _handleNativeCall(String action) async {
    if (action == 'openCamera') {
      final image = await ImagePicker().getImage(source: ImageSource.camera);
      if (image != null) {
        _controller.evaluateJavascript(
          "window.onImageSelected('${image.path}')"
        );
      }
    }
  }
}

웹 쪽에서는 다음처럼 호출한다.

function openCamera() {
  if (window.NativeChannel) {
    window.NativeChannel.postMessage('openCamera');
  } else {
    // 웹 브라우저에서 실행 시 폴백
    document.getElementById('fileInput').click();
  }
}

window.onImageSelected = function(path) {
  console.log('Image selected:', path);
  // 이미지 처리 로직
};

주의사항

  • javascriptModeunrestricted로 설정해야 채널이 동작한다.
  • iOS에서는 Info.plist에 카메라 권한 설정이 필요하다.
  • 웹 환경에서도 동작하도록 분기 처리를 해뒀다.

재택 근무 중이라 디바이스 테스트가 번거로웠지만, Flutter Hot Reload 덕분에 빠르게 검증할 수 있었다.