Open5
【Flutter Web】jsパッケージを使ったLine LIFF開発
必要(そう)なツール
◆ ngrok
- ローカルPC上で稼働してるネットワークを外部に公開してくれるサービス
- ngrokを通して、localhostで立ち上げてるWebアプリケーションを別端末などからもアクセス可能にしてくれる
- httpsのurlでも公開してくれるので、https接続でしか動かない機能の検証も可能
- ただ公開してくれるだけでセキュアではないので、後述のmkcertを使って証明書を発行する事でセキュアな状態で公開する事が可能?
インストール方法
◆ mkcert
参考
ローカル開発環境の整備
- 事前準備
- テスト用のLINEグループを作る
- そこに登録したLIFFアプリの
LIFF URL
を投稿しておく
- ローカルサーバーの立ち上げ
$ flutter run -d web-server --web-port=8080
- ngrokを使って、ローカルサーバーを外部公開
$ ngrok http 8080
- ngrokが吐き出すhttpsのurlをLIFFアプリのエンドポイントURLに登録
ngrok (Ctrl+C to quit)
Join us in the ngrok community @ https://ngrok.com/slack
Session Status online
Account heyhey1028 (Plan: Free)
Version 3.0.7
Region Japan (jp)
Latency -
Web Interface http://127.0.0.1:4040
# ↓ このURL
Forwarding https://xxxx-xxx-xxx-xxx-xx.jp.ngrok.io -> http://localhost:8080
Connections ttl opn rt1 rt5 p50 p90
0 0 0.00 0.00 0.00 0.00
- LINE内でLIFF URLにアクセスすると現在ローカルで立ち上げたFlutter webが表示される
launch.json
でビルドしたローカルサーバーではdart debug extension
が必要になる
壁①:vscodeの現象:
- launch.jsonで下記の通り定義しデバックを実行
{
"name": "Debug web-server",
"program": "lib/main.dart",
"request": "launch",
"type": "dart",
"args": [
"-d",
"web-server",
"--web-port=8080"
]
},
- localhost: 8080にアクセスするも下記のエラーが表示され、ずっとローディング状態となる
解決策:
- コンソールから
flutter run -d web-server --wep-port=8080
を実行して立ち上げたローカルサーバーでは正常に起動 - launch.jsonで立ち上げたローカルサーバーの場合、chrome側に
dart debug extension
をインストールして実行すると正常に起動する dart debug extension
.env
が読み込まれない
壁②:FlutterWebデプロイ時に- LIFF SDKの使用にLIFF IDが必要となる
- 秘匿したいのでflutter_dotenvを使用したい
- しかしfirebase hostingへのデプロイ時、.envファイルの読み込みが行われない
原因
firebase.json
のignore
項目に"**/.*"
が含まれている為、ドットから始まるファイルが無視されてしまう
解決策
① ファイル名を変更する
- ファイル名を
.env
ではなく、env
として作成 - dotenvの読み込みで
env
ファイルを読み込む
main.dart
Future<void> main() async {
// .envファイルの読み込み
+ await dotenv.load(fileName: "env");
- await dotenv.load(fileName: ".env");
runApp(const MyApp());
}
② ignore項目を変更
-
.env
ファイルだけはignoreされない様、!**/.env
を追記
firebase.json
{
"hosting": {
"public": "build/web",
"ignore": [
"firebase.json",
+ "**/.* !**/.env",
- "**/.*",
"**/node_modules/**"
]
}
}
参考:
LIFFアプリの登録
jsパッケージを使用し、LIFF SDKを実行する
jsパッケージの基本
流れ
- jsのfunctionを定義したファイルをwebディレクトリに作成
web/audio.js
function playAudio(audioPath) {
//htmlからAudioタグを取得
var audioElement = document.getElementById("audio");
//取得したAudioタグに対して、引数にて指定されたパスを設定
audioElement.src = audioPath;
//再生
audioElement.play();
}
-
web/index.html
にて定義したjsファイルをimport
web/index.html
<script src="audio.js"></script>
- 前提としてjsファイルの呼び出し部分の前に
@JS()
アノテーションを付ける - libraryとしてjsの処理を定義したファイルを指定
- jsのfunctionを割り当てるdartのfunctionの前に
@JS(<割り当てるfunction名>)
を付ける - jsのfunctionを割り当てるdartのfunctionの頭に
external
句を付けて引数、返り値をjsのfunctionと同様にする
lib/main.dart
() // 2
library audio; // 3
import 'package:js/js.dart';
//interface function
('playAudio') // 4
external void playAudio(String audioPath); // 5
外部のjsライブラリの使用
自前で用意したjsの関数ではなく、jsライブラリを使いたい場合は以下の流れで使用可能
-
web/index.html
にてライブラリをimport
web/index.html
<script defer src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
ファイル構成
jsの処理にオブジェクト引数を渡す
- 自前で定義したオブジェクトを引数として渡す場合は
package:js
が用意している@anonymous
アノテーションを付ける -
@anonymous
アノテーションが付いたクラスではfactory
コンストラクタを定義する必要がある - 通常、名前付き引数は使えないので、下記の通りクラスとして定義し、そのコンストラクタを名前付きにする事で実現させる
/// A class marked with [anonymous] must have an unnamed factory constructor
/// with no positional arguments, only named arguments. Invoking the constructor
/// desugars to creating a JavaScript object literal with name-value pairs
/// corresponding to the parameter names and values.
dog.dart
()
library dog;
// The above two lines are required
import 'package:js/js.dart';
()
class Dog {
external Dog(String name, int age);
external String get name;
external int get age;
external void bark();
external void jump(Function(int height) func); // 引数として関数を渡す
external void sleep(Options options); // 引数としてオブジェクトを渡す
}
()
class Options {
external bool get bed;
external String get hardness;
external factory Options({bool bed, String hardness});
}
jsの処理に関数を引数として渡す
- 上記の
external void jump(Function(int height) func);
の様に関数を渡したい場合は、渡す関数をallowInterop
メソッドでラップする
main.dart
dog.jump(allowInterop((int height){
print(height);
}));
jsのPromiseをdart側で実行する
- Javascriptの
Promise
を実行する場合は、js_utilライブラリ(jsパッケージに内包されている)のpromiseToFuture
関数でFutureに変換する -
promiseToFuture
メソッドは引数としてObject
を受け取る為、dart側のインターフェースではObjectを返すメソッドとして定義する必要がある
app.js
async function classify(image) {
const result = await iKutModel.classify(image);
return JSON.stringify(result, null, 2);
}
main.dart
('classify')
external Object classify(ImageElement img);
// 呼び出し
String result = await promiseToFuture(classify(imageElement));
参考