Mobile Front/Flutter

[Flutter] webview 양방향 통신 (flutter webview two way communication)

koh1018 2023. 3. 19. 15:03
반응형

최근 하이브리드 앱을 많이 만들었다.

 

하이브리드 앱은 네이티브 앱에 비해 스토어(구글 플레이스토어, 애플 앱스토어)에 덜 종속적이고 별도의 심사과정없이 최초 등록만 되면 빠른 업데이트가 가능하다는 차별점이 있다.

 

하지만 하이브리드 앱은 웹뷰를 이용하기에 네이티브 앱에 비해 신경써야할 부분이 조금 더 많다.

이 중 하나가 바로 Flutter와 Webview에 띄워진 웹 간의 통신이다.

 

Flutter Webview Two Way Communication (플러터 웹뷰 양방향 통신)

 

필자는 Flutter의 웹뷰 패키지로 아래 webview_flutter 패키지를 사용하였다. (4.0.0 이상 버전 사용)

 

webview_flutter | Flutter Package

A Flutter plugin that provides a WebView widget on Android and iOS.

pub.dev

 

 

1. Webview → Flutter

먼저 Flutter의 코드이다.

// Flutter

controller = WebViewController()

      // Other controller settings ...

      ..addJavaScriptChannel(
        'Toaster',
        onMessageReceived: (JavaScriptMessage message) {
          var data = jsonDecode(message.message);

          // Do something...
        }
      )
      ..loadRequest(Uri.parse('https://www.somapeople.kr'));

webview의 controller에 위와 같이 addJavaScriptChannel을 설정한다.

'Toaster'는 이 javascript channel의 이름이다.

받는 메시지의 형태를 string 형태로 받을 수도 있지만 json 형태로 받고 싶을 수 있다.

이 경우 웹에서 json을 string 형태로 바꾼 후, 이를 위와 같이 jsonDecode로 디코딩해 json형태로 이용할 수 있다.

 

다음으로 웹에서의 코드이다. (React)

필자는 따로 flutterBridgeFunc이라는 이름의 폴더를 만들고 그 안에서 함수를 정의하여 불러오는 방식으로 간편하게 사용했다.

예시 코드는 아래와 같다.

export function addToaster(id: number, isTake: boolean, message: string) {
  // @ts-ignore
  Toaster.postMessage(JSON.stringify({
    id: id,
    isTake: isTake,
    message: message
  }))
}

Toaster는 window에 정의된 함수가 아니라며 에러가 뜨므로 @ts-ignore로 처리해준다.

이제 이 addToaster라는 함수를 가져다가 사용하면 된다.

 

다음으로 Flutter에서 webview로 통신하는 방법을 알아보겠다.

 

2. Flutter → Webview

이 통신 방식에 대한 webview_flutter 패키지의 레퍼런스가 많이 없어 무척 고생했다.

보통 Flutter 프로젝트 폴더 안의 정적 html 파일과 통신하는 레퍼런스만 있었는데, 이를 응용해 성공시켰다.

 

우선 웹에서의 코드이다.

필자는 Next.js를 사용하고 있었는데 (Next는 React의 프레임워크다 React도 똑같이 하면 된다)

요청이 들어올 때 가장 먼저 실행되는 컴포넌트인 _app.tsx (_app.jsx)에서 통신할 함수를 정의해주는 방식으로 시도해보았다.

// _app.tsx

export default function App({ Component, pageProps }: AppProps) {
  const router = useRouter()

  // Flutter Bridge Function 세팅
  useEffect(() => {
    // @ts-ignore
    window.changePage = (index: number) => {
      switch (index) {
        case 0:
          router.push('/')
          break
        case 1:
          router.push('/board')
          break
        default:
          router.push('/profile')
          break
      }
    }
  }, [])
  
  return <Component {...pageProps} />
}

_app에서 useEffect 훅에서 window 객체에 changePage 라는 함수를 정의해두는 방식이다.

이렇게 하면 서비스가 실행되는 최초 시점에 함수를 정의할 수 있다.

 

이제 Flutter의 코드를 보겠다.

무척 간단하다.

// Flutter

void _onItemTapped(int index) {
    controller.runJavaScript('window.changePage($index)');
}

위와 같이 정의했던 webview controller의 runJavaScript 함수를 실행시킨다.

runJavaScript 함수는 string 타입으로만 받으므로 window 객체에 정의해둔 changePage라는 함수를 실행시키는 코드를 string 타입으로 전달해준다.

 

 

 

반응형