Skip to the content.

Flutter & Vue Javascript bridge

Flutter와 Vue를 이용해서 하이브리드 앱을 개발할 때 Javascript bridge를 이용해서 메시지를 주고 받는 과정을 설명합니다.

메시지를 서로 주고 받아야 하기 때문에 App과 Web 모두 메시지가 들어오는 in 모듈과 보내는 out 모듈이 필요합니다. 따라서 Vue와 Flutter 파트의 in과 out에 해당하는 모듈에 대해서 각각 살펴보도록 하겠습니다.

실제 사용에 대한 예제는 Flutter & Vue 데이터 동기화에서 다루도록 하겠습니다.

Vue 파트

App to Web (bridge-in.js)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import router from "@/router";

export default {
    init() {
        window.refresh = this.refresh;
        window.loadUrl = this.loadUrl;
    },

    refresh() {
        router.go();
    },

    loadUrl(url) {
        router.push({ path: window.atob(url) });
    },
}

Web to App (bridge-out.js)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/* eslint-disable */

export default {
    /**
     * Web to App Globals sync
     * @param params 싱크할 데이터
     */
    syncGlobals(params) {
        const msg = {
            code: "syncGlobals",
            params: params
        };
        this.post(msg);
    },

    /**
     * 웹앱 초기화면 로딩이 완료된 것을 네이티브에게 알려준다.
     * 네이티브는 이 신호를 받으면 인트로 화면에서 웹뷰 화면으로 전환한다.
     */
    webViewReady() {
        const msg = {
            code: "webViewReady",
        };
        this.post(msg);
    },

    ...

    post(msg) {
        try {
            App.postMessage(JSON.stringify(msg));
        } catch (e) {
            console.log(msg);
            console.log(e);
        }
    },
};

Flutter 파트

App to Web (bridge.dart)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class Bridge extends Core  {
  /**
   * App to Web Globals sync
   * @param params 싱크할 데이터
   */
  void syncGlobals(String params) {
    _sendMessage("syncGlobals", params);
  }

  /**
   * 페이지 새로고침
   */
  void refresh() {
    _sendMessage("refresh", "");
  }

  /**
   * 해당 주소로 웹뷰 이동
   */
  void loadUrl(String url) {
    _sendMessage("loadUrl", url);
  }

  ...

  void _sendMessage(String functionName, String params) {
    Codec<String, String> stringToBase64 = utf8.fuse(base64);
    String encoded = stringToBase64.encode(params);

    _mainController?.runJavascript("$functionName('$encoded')");
    _subController?.runJavascript("$functionName('$encoded')");
  }
}

Web to App (main.dart)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
...
class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return GetMaterialApp(
        onReady: () {
          _init(context);
        },
        home: Bridge().getMainWebView(Config().HOME_URL));
  }

  void _init(BuildContext context) {
    Config().init();
    Globals().init(context);

    Bridge().subscribeEvent((event) async {
      print("${event.code} - ${jsonEncode(event.params)}");
      switch (event.code) {
        case 'webViewReady': // TODO: 스프래시 화면에서 메인화면 전환 등
        break;
      }
    });
  }
}