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

FlutterをHTML elementに埋め込む

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.
Avatar for ken ken
July 15, 2023

FlutterをHTML elementに埋め込む

Avatar for ken

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