Upgrade to Pro — share decks privately, control downloads, hide ads and more …

FlutterをHTML elementに埋め込む

ken
July 15, 2023

FlutterをHTML elementに埋め込む

ken

July 15, 2023
Tweet

More Decks by ken

Other Decks in Programming

Transcript

  1. 自己紹介 • kenma: 益満 健 (Masumitsu Ken) • 株式会社ディー・エヌ・エー ◦

    日比谷音楽祭2023アプリ作成 (Flutter) • Twitter: @kenma • Qiita: @kenma
  2. なぜ埋め込みたい?? • 既存のWebページがあるから ◦ 例えば、WordPressで作られたサイトに、 Flutterを埋め込む • SEOのため ◦ Flutter

    for Webは動的コンテンツなので SEOに不向き ◦ HTML/JSでSEO対策をして、コアの部分は Flutterを埋め込む • 高速化のため ◦ (Flutter WASMで)Webの一部をFlutterに置き換えることにより高速化。(?) • 従来は IFrameを利用することで埋め込みができていた。 ◦ が、IFrameの内外間の相互通信が複雑だった。
  3. プロジェクトセットアップ • flutter 3.10.5 • js 0.67 flutter create sample

    --platforms web cd sample flutter pub add js flutter run -d chrome
  4. <!-- 3. #flutter_targetのスタイルを指定。--> <link rel="stylesheet" type="text/css" href="css/style.css" /> </head> <body>

    <script> window.addEventListener('load', function(ev) { _flutter.loader.loadEntrypoint({ serviceWorker: { serviceWorkerVersion: serviceWorkerVersion, }, onEntrypointLoaded: function(engineInitializer) { engineInitializer.initializeEngine({ // 2. HTMLエレメントを指定 hostElement: document.querySelector("#flutter_target"), }).then(function(appRunner) { appRunner.runApp(); }); } }); }); </script> <section class="contents"> <div>この下にFlutterアプリを表示する。</div> <article> <!-- 1. flutterアプリを表示するHTMLエレメント --> <div id="flutter_target" class="center"></div> </article> </section> </body> </html> index.htmlの修正 1. Flutterアプリを表示するHTMLエレメント (※)を準備 ◦ id = flutter_target 2. initializeEngineの引数 hostElement に HTMLエレメント(※)を指定 3. HTMLエレメント(※)にCSSでスタイルを当 てるためにcssを読み込む
  5. css/style.cssの作成 1. #flutter_targetのwidth, heightを指定 ◦ 忘れると表示されない! /** 重要!! */ #flutter_target

    { border: 1px solid #aaa; width: 320px; height: 480px; border-radius: 0px; transition: all 150ms ease-in; } .center { margin: auto; width: 50%; }
  6. 埋め込むだけなら <iframe> でもできていた WebアプリとFlutter間でインタラクションをしたい! • Webアプリが主で、Flutterアプリ側が従という関係。 • なので、したいことは、 a. Webアプリから、Flutterアプリを操作する。

    → JSからFlutterのメソッドを呼ぶ。 b. Webアプリから、Flutterアプリのイベントを検知する。 → JSからFlutterにcallback関数を渡すメソッドを呼ぶ。 ⇒ どちらもJSから、Flutterのメソッドを呼び出せればいい
  7. main.dart • JS側にExportするクラスに@JSExportアノテー ションをつける。 ◦ クラスの全メソッドがExportされる。 • createDataExport()関数で、オブジェクトをJS からアクセスできる形にラップする •

    allowInteropは使わなくてよくなった • js_utilを使って、JSコード呼び出し ◦ setProperty() ◦ callMethod() import 'package:js/js.dart' as js; import 'package:js/js_util.dart' as js_util; // _MyHomePageState を JS側にExport可能にする。 @js.JSExport() class _MyHomePageState extends State<MyHomePage> { int _counter = 0; @override void initState() { super.initState(); // _MyHomePageStateオブジェクトを、JS側にExportする final export = js_util.createDartExport(this); // JS側の globalThis(ブラウザの場合はwindow)の _appStateプロパ ティとして、 // export (_MyHomePageState) をセットする。 js_util.setProperty(js_util.globalThis, '_appState', export); // JS側の globalThisの_stateSetメソッドを引数無しで呼び出す。 js_util.callMethod<void>(js_util.globalThis, '_stateSet', []); }
  8. index.html (function () { // _stateSet関数作成 window._stateSet = function ()

    { console.log('_stateSet function is called') // _appStateプロパティの値を取得 let appState = window._appState; console.log('_appState property is ', appState); }; }()); <!-- js/js-interop.js の読み込み --> <script src="js/js-interop.js" defer></script> </head> • js/js-interop.js を追加 js/js-interop.js • window._stateSet関数の作成 ◦ js_util.callMethod(, _stateSet,) で、呼ば れる
  9. main.dart • JS側にExportするメソッドに@JSExportアノ テーションをつける。 ◦ 指定したメソッドのみ JS側にExportされる ◦ @JSExport(‘foo’) と名前を指定すると、

    JSからそ の名前でExportする。 //@js.JSExport() //コメントアウト class _MyHomePageState extends State<MyHomePage> { int _counter = 0; @override void initState() { super.initState(); // _MyHomePageStateオブジェクトを、JS側にExportする final export = js_util.createDartExport(this); // JS側の globalThis(ブラウザの場合は、window)に プロパティ (_appState)、値 (export)をセットする。 js_util.setProperty(js_util.globalThis, '_appState', export); // JS側の globalThisの_stateSetメソッドを引数無しで呼び出す。 js_util.callMethod<void>(js_util.globalThis, '_stateSet', []); } @js.JSExport('inc') // inc という名前でExport void _incrementCounter() {
  10. main.dart • JS側にExportするメソッドに @js.JSExportアノテーションをつ ける • callback関数を受け取るメソッドも 特殊なことはしない。 // @js.JSExportしない

    class _MyHomePageState extends State<MyHomePage> { @js.JSExport() void _incrementCounter() { setState(() { _counter++; // 変更を通知 _streamController.add(null); }); } @js.JSExport() int get count => _counter; // callback関数を受け取る関数を Exportする @js.JSExport() void addHandler(void Function() handler) { _streamController.stream.listen((event) { handler(); }); }
  11. index.html • カウンター機能を追加 ◦ カウンター値表示 ◦ インクリメントボタン <section class="contents"> <aside

    id="demo_controls"> <fieldset id="interop"> <legend>JS Interop</legend> <!--カウンター値を表示 --> <label for="value"> Value <input id="value" value="" type="text" readonly /> </label> <!--インクリメントボタン --> <input id="increment" value="Increment" type="button" /> </fieldset> </aside> <article> <!-- 1. flutterアプリを表示するHTMLエレメント --> <div id="flutter_target" class="center"></div> </article> </section>
  12. js/js-interop.js • インクリメントボタンを押したら、 Flutterの_incrementCounter()を 呼び出す。 • Flutterのカウンタが更新したら、 updateStateがコールバックされ る。 (function

    () { window._stateSet = function () { // js_util.setProperty() でセットした、_appStateプロパティの取得 const appState = window._appState; // インクリメントボタンのHTMLエレメント取得 const incrementButton = document.querySelector("#increment"); incrementButton.addEventListener("click", (event) => { // Flutterの_MyHomePageState._incrementCounter()の呼び出し appState._incrementCounter(); }); // カウンター値表示エリアの HTMLエレメント取得 const valueField = document.querySelector("#value"); const updateState = function () { // Flutterの_MyHomePageState.count の呼び出し valueField.value = appState.count; }; updateState(); // Flutterの_MyHomePageState.addHandlerを呼び出す。 // callback関数 updateStateを与える。 appState.addHandler(updateState); }; }());
  13. まとめ • FlutterをHTML elementに埋め込める。 • FlutterとHTML/JS間でインタラクションが簡単に • しかしながら、実プロダクトで利用するには時期尚早 ◦ Reactアプリに埋め込む場合、実際には大変。ドキュメントが必要だが、まだない。

    ▪ 参照 https://github.com/flutter/flutter/issues/123940 ◦ JSからDartのAsync関数を呼べない。 ▪ 参照 https://tinyurl.com/29c7fb8c • おまけ ◦ 今日見せたコード:https://github.com/kenmasumitsu/flutter-web-enbedding