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

div要素の中に、Flutterを埋め込んでみよう

 div要素の中に、Flutterを埋め込んでみよう

FlutterのElement Embeddingを試してみた。
勉強会の資料。

beeeyan

May 28, 2023
Tweet

More Decks by beeeyan

Other Decks in Technology

Transcript

  1. Element Embeddingとは?
 Element Embedding : 要素埋め込み
 
 Flutterの文脈においては Widgetを標準的なウェブの<div>に追加できる。 この方法で統合されると、Flutterは単にウェブコンポーネントに変わり、

    DOMとシームレスに統合される。 ただ、<div>の中に表示されるだけではなく、JavaScriptなどで状態を操作できる。 https://www.youtube.com/watch?v=PAOAjOR6K_Q&t=199s
  2. <script> window.addEventListener('load', function(ev) { ・・・省略・・・ }); </script> ↓  <div id="flutter_target"></div>

    ・・・省略・・・ <script> window.addEventListener("load", function (ev) { // Embed flutter into div#flutter_target let target = document.querySelector("#flutter_target"); _flutter.loader.loadEntrypoint({ onEntrypointLoaded: async function (engineInitializer) { let appRunner = await engineInitializer.initializeEngine({ hostElement: target, }); await appRunner.runApp(); }, }); }); </script> プロジェクト作成後、 web/index.htmlを書き換え flutter_targetのセレクタを追加 デフォルトのloadEntrypointの記述を 変更してhostElementを追加
  3. import 'package:flutter/material.dart'; import 'package:js/js.dart' as js; import 'package:js/js_util.dart' as js_util;

    ~~ @js.JSExport() class _MyAppState extends State<MyApp> { // 調査 final _streamController = StreamController<void>.broadcast(); int _counterScreenCount = 0; @override void initState() { super.initState(); final export = js_util.createDartExport(this); js_util.setProperty(js_util.globalThis, '_appState', export); js_util.callMethod<void>(js_util.globalThis, '_stateSet', []); } ~~ main.dart (1/2) js/js.dart js/js_util.dart をimportする。 ※これらのパッケージが Dart 3でないと動かない。 JavaScriptでも利用したい。Exportしたい 部分に 「@js.JSExport()」を付与 「_MyAppState」を「_appState」という JavaScriptのプロパティとして扱えるように、 「setPropertty」する。
  4. main.dart (2/2) final _streamController = StreamController<void>.broadcast(); @js.JSExport() void increment() {

    setState(() { _counterScreenCount++; _streamController.add(null); }); } @js.JSExport() void addHandler(void Function() handler) { _streamController .stream.listen((event) { handler(); }); } StreamController<T>.broadcast 複数のリスナーが同時にデータを受け取ることができるスト リーム 変更をHTML・JavaScript側に反映させるた め。 ※反映処理のcallbackを受け取る部分は、 main.dartのaddHandlerに処理がある。
  5. web/js/demo-js-interop.js let appState = window._appState; ~~ let valueField = document.querySelector("#value");

    let updateState = function () { valueField.value = appState.count; }; // Register a callback to update the HTML field from Flutter. // Streamの処理を検知しているのはここ appState.addHandler(updateState); let incrementButton = document.querySelector("#increment"); incrementButton .addEventListener ("click", (event) => { appState.increment(); }); ~~ DartのState・処理にアクセスできるよう 「appState」を呼び出す。 ボタンの押下のイベントを検知して 「appState.increment()」でmain.dart のincrementを呼び出す。 main.dartのaddHandlerには updateStateを引数に入れる。
  6. HTML上のボタンを押下 ↓ jsからdartのincrementを呼び出す ↓ dartでincrement後、streamの処理を 実行 ↓ dartのaddHandlerを実行 ↓ addHandlerの引数の「handler」の中に

    jsのupdateStateの処理が入っていて、 HTML上の表示が更新される。 jsからmain.dartのincrementを呼び出す
 ↓
 main.dartで値をincrementした後、streamの処理を実行
 ↓
 main.dartのaddHandlerを実行
 ↓
 addHandlerの引数の「handler」の中にjsのupdateStateの処理が入ってい て、HTML上の表示が更新される。
 
 HTML上のボタンを押下した際、
 何が起きているか?
 
 

  7. web/css/style.css #flutter_target { border: 1px solid #aaa; width: 320px; height:

    480px; border-radius: 0px; transition: all 150ms ease-in; } 忘れないように! CSSでFlutter画面を表示する領域は 確保しておくこと。 ※Flutter部分は自動で領域を持たな い。 (自力で実装しようとすると引っかかるかも)
  8. Element Embeddingの利点は?
 Flutter Webの弱点部分を組み合わせを解消するアプローチ
 • SEO
 • (初期読み込みなど)スピード
 その他
 •

    Flutterで既に作成したWidgetをWebでも利用したい。
 • モバイルアプリの画面をWebで表示してみたい。
 (スクレイピングさせたくない要素をFlutterで表示する)