2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Flutter Web】Javascriptを呼び出す (packages:js)

Posted at

webの開発の為に多種多様なjavascriptのライブラリが存在しますが、flutter web開発時に使いたくてもdart・flutterでそれらを扱うパッケージが存在しないこともあると思います

そんな場合にはdart側からJavascirptの処理を直接呼び出すサポートをしてくれるのがjsパッケージです

その基本的な使い方を見ていきます

前提

今回は「 Flutter webにて外部のjsライブラリもしくは自身で作成したjsの処理を実行する 」事を想定しており、純粋にDartからjs処理を呼び出す事についてはあまり言及しません

概要

全体の流れとしては、以下の通りとなります

  1. webフォルダ配下にjavascriptの処理を定義したファイルを作成
  2. web/index.html内でそのファイルをインポート
  3. Dart(Flutter)側でインポートしたjsファイルの処理を呼び出すメソッドを定義

ファイル構成

.
├── lib
│   ├── my_javascript.dart # このファイルにjs処理を書いていく
│   └── main.dart
│
└── web
    ├── my_javascript.js # このファイルにjs処理を書いていく
    ├── index.html
    └── manifest.json

基本の流れ

  1. jsパッケージをインポートしておく
$ flutter pub add js
  1. jsのfunctionを定義したファイルをwebディレクトリに作成
web/my_javascript.js
function basicFunction() {
    console.log("function executed!")
}
  1. web/index.htmlにて定義したjsファイルをインポート
web/index.html
  <script src="my_javascript.js"></script>
  1. js処理を呼び出すDart側のメソッドを定義
lib/js/my_javascript.dart
@JS()
library my_javascript;

import 'package:js/js.dart';

@JS('basicFunction')
external void basicFunction();
  • 前提としてjsファイルの呼び出し部分の前には@JS()アノテーションを付ける
  • libraryとしてjsの処理を定義したファイルを指定
  • 定義したjsの処理を割り当てるdartメソッドの前に@JS(<割り当てるfunction名>)を付ける
  • dartメソッドの頭にexternal句を付けて引数、返り値をjsの処理と同様にする
  1. Dart側のメソッドを実行する
    あとはDart側で定義したメソッドを実行するだけです
lib/main.dart
onPressed: ()=> basicFunction(),

外部jsライブラリの使い方

上記では自分で定義したjavascriptの処理を呼び出しましたが、今度は公開されている外部のjsライブラリを使ってみましょう

扱い方は2通り

外部ライブラリを使う場合は、「自分で定義したjavascriptの処理で使うやり方」と「dart側で直接外部ライブラリの処理を呼び出すやり方」と2通りが存在します

自分で定義したjsの処理で使う

  1. 外部ライブラリをweb/index.htmlでインポートしておく
web/index.html
  <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
  1. 自分で定義したjavascriptの処理内で呼び出す
    web/index.htmlでインポートしておけば、自身のjsファイル内で改めてimportする事なく外部ライブラリの処理を呼ぶ事ができます
web/my_javascript.js
// function using library
function functionUsingLibrary(list){
    // using max method from lodash
    console.log(`biggest number is ${_.max(list)}`)
}

Dart側から直接呼び出す

  1. 外部ライブラリをweb/index.htmlでインポートしておく
web/index.html
  <script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
  1. Dart側で外部ライブラリの処理を直接呼ぶ
    Dart側で直接呼び出す際はlibraryとしてインポートします
lib/js/lodash.dart
@JS('_')
library lodash;

import 'package:js/js.dart';

@JS('uniq')
external List<int> uniq(List<int> list);

どちらが良いのか?

呼び出したいjsの処理がそのライブラリの処理だけであれば、Dart側から直接呼んでも良いですが、複数あったり、今後増える可能性もあるのであれば 自分で定義したjsファイルを用意し、そこに処理を集約させる 方が個人的には分かりやすく、責務もスッキリすると考えています。あくまでも個人的な意見ですが。

jsの処理に引数を渡す

引数を受け取るjs処理があると思います。Stringboolなどプリミティブな型はそのまま渡せますが、オブジェクトを引数として渡す場合は、そのオブジェクトのクラスをjsパッケージを使って定義する必要があります

lib/js/my_javascript.dart
@JS('functionWithObjectArg')
external void functionWithObjectArg(Person person);

@JS('')
@anonymous
class Person {
  external String get name;
  external int get age;
  external String get message;
  external factory Person({String name, int age, String message});
  • jsに用意されている@anonymousアノテーションを付ける
  • @anonymousアノテーションが付いたクラスではfactoryコンストラクタを定義する必要がある
  • 通常、名前付き引数は使えないので、上記の通りクラスとして定義し、そのコンストラクタを名前付きにする事で実現させる
lib/main.dart
  onPressed: () => functionWithObjectArg(
    Person(
      name: 'John',
      age: 52,
      message: "Hi, I'm John!!",
    ),
  ),

jsの処理に関数を引数として渡す

関数を引数で渡したい場合は、渡す関数をallowInteropメソッドでラップする。onSuccessやonFailureのコールバックメソッドを使う際に有効です。

web/my_javascript.js
// example with passing function
function functionWithFunctionArg(func) {
    const result = func()
    console.log(`function result was ${result}`)
}
lib/js/my_javascript.dart
@JS('functionWithFunctionArg')
external void functionWithFunctionArg(Function func);
lib/main.dart
  onPressed: () {
    functionWithFunctionArg(allowInterop(() {
      return 125 + 25;
    }));
  },

jsのPromiseをdart側で実行する

Promiseのjs処理を呼びたい場合、Dart側でそのままFuture型に変換する事はできません。

以下二つの対応が必要です

  1. js_utilライブラリ(jsパッケージに内包されている)のpromiseToFuture関数でFutureに変換する
  2. promiseToFutureメソッドは引数としてObjectを受け取る為、dart側のインターフェースでは Objectを返すメソッドとして定義する必要がある
web/my_javascript.js
// example with Promise function
async function functionWithPromise() {
    console.log("execution started")
    await new Promise(resolve => {
        setTimeout(resolve, 3000) // wait 3 seconds
      });
      console.log("call waited 3 seconds")
}
lib/js/my_javascript.dart
@JS('functionWithPromise')
external Object functionWithPromise();
lib/main.dart
// 呼び出し
  onPressed: () async {
    await promiseToFuture(functionWithPromise());
    print('execution finished');
  },

おわり

コード:

以上の知識があれば、大体のjsライブラリは活用する事が出来るかと思います。Web開発では豊富なjsライブラリを使いたいシーンも、そのライブラリに対応したFlutterパッケージが存在しない事も多々有ります。packages:jsはそんな制約を一気に取っ払ってくれる強力な助っ人です。

参考

https://flutter-square.com/js-call-dart/ (ja)
https://liewjuntung.medium.com/ use-javascript-in-flutter-web-a6eed3efb9a0 (en)
https://qiita.com/tfandkusu/items/b8b498d60ddff3db4d50 (ja)
https://codeburst.io/ how-to-use-javascript-libraries-in-your-dart-applications-e44668b8595d (en)
https://www.youtube.com/watch?v=zoN1_5tYzOM&t=44s (en)

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?