10
3

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 3 years have passed since last update.

Flutter #3Advent Calendar 2020

Day 15

Flutter for webでFlashプロダクトを置き換えた話

Last updated at Posted at 2020-12-14

はじめに

Flashのサポートが2020年12月31日で終わってしまいます。
私が所属する企業のアプリはマルチデバイス対応がウリで、Web版はFlashで作られていました。
もともとのチームは10名(パートナー含めると倍近く)くらいいましたが、今は私とパートナーさんの2名。
少ないリソースでやりきらないといけない。
というところで、白羽の矢が立ったのがFlutter for Webでした。

どんなプロダクト?

電子書籍ビューアです。
よくある漫画ビューアのような難読化(パズルみたいに画像をバラバラにするもの)ではなく、暗号化と復号処理をするものです。これ以上は細かく言えませんが、セキュリティに少し配慮したものになってます。

移行開始

オリジナルのFlashのコードは存在するものの、当時流行ってたオフショア開発だったのでコメントが壊滅的でした。日本語がよくわからないのはいいとして、英語のコメントもなし。幸い、Javaのサンプルがあったのでそちらを参考にしました。

ハマったポイント

バイナリデータの値

復号処理をビューア側でしないといけないのですが、そもそも転送されてきたデータがなにか違う。
ここでのハマりポイントは、DartのUint8List型とJavaのbyte[]型のintの範囲が異なっていたことでした。
なので、最初は復号処理がおかしいんじゃないかとロジックを見直して無駄に時間を浪費してしまいました。
具体的にはDartは0〜255、Javaは-127〜128となっています。なので、128までは問題ないのですが130となるとJava側では-2となり意味が分かりませんでした。
その他のデータ型に関しては下記URLを参考にして実装しました。

HTTPリクエスト

FlutterでHTTPリクエストを実行するにはdart:ioとdart:htmlがあって、前者はモバイルなどネイティブアプリから利用できます。後者はWebで利用できます。しかしどちらか一方しか使えないという制約があるので、ラッパーライブラリのhttp.dartを利用しました。
WebサーバにはContent-Type: x-www-form-urlencodedでリクエストをPOSTすればいいのであまり気にしていなかったのですが、JSONをPOSTするとエラーになってしまいました。

リクエストヘッダにapplication/jsonを設定したのにおかしいなと思って調べたところ、Web版はContent-Typeを設定することができないそうです。
仕方ないのでサーバ側の受け取りをx-www-form-unlencodedに実装を変更して対応しました。
同様のdio.dartでも上手く動かないのでdart:jsで自力で実装しないといけないのかな?
まだ未検証なのでそのうち試してみます。

下記に同様の処理をされている人がいました。POSTするbodyをMapからエンコードしたJSONに指定するとContent-Typeがaplication/jsonに上書きできるそうです。

動いたコード
import 'dart:convert';
import 'package:http.dart' as http;

Future<void> postData(var body) async {
    var body = {'id': 'a001', 'date': '2020/12/15 00:00:00'};
    var response = await http.post(url,
        headers: {
          HttpHeaders.contentTypeHeader: 'application/json',
          HttpHeaders.acceptHeader: 'application/json',
        },
        body: json.encode(body));
} 

参考
https://ttydev.com/2020/07/13/flutter-content-type-error/

動かなかったコード
import 'package:http.dart' as http;

Future<void> postData(var body) async {
    var body = {'id': 'a001', 'date': '2020/12/15 00:00:00'};
    var headers = {'Content-Type': 'application/json'};
    var response = await http.post('https:/domain/endpoint', headers=headers, body=body);
} 

文字列定数が丸見え

DratからJavaScriptにトランスパイラされる為、文字列定数が丸見えになっていました。
URL等は見えてしまっても問題ないのですが、復号処理に使う値が見えるのは流石にまずいので、難読化することで対応しました。
パフォーマンスのこともあるのでそこまで複雑なことはしてませんが、パッと見ではわからなくなりました。

やってよかったこと

テストファースト

ユニットテストを積み上げて手戻りがないように最初に動けたのがよかったです。
Javaでお世話になったmockitoがDartでもそのまま使えたのが大きいと感じています。
また、ユニットテストはxUnitなのでこちらも違和感なくテストを記述できました。
テストについては下記URLを読んで取り組めばいいと思います。プロジェクトにもよりますが、ユニットテストはやっといて損はないかと思いました。
https://flutter.dev/docs/testing

UIとビジネスロジックの分離

既存のコードはMVCパターンで書かれてましたが、名ばかりMVCでした。
今回はMVCに分離することを頑張ったおかげで責任の範囲が限定されて見通しがよくなり、カオスになることを避けれたように思います。

おわりに

FlutterのAdvent CalendarなのにほとんどDartの話になってしまいました。私の担当範囲が通信とかデータの保持、込み入った処理が中心だったためなので仕方ないなと思う事にします。
Flutterでは描画コストの見直し以外は特に困ったことはありませんでした。それくらいサクサクとUIを構築できました。
そのおかげでFlash版で提供していたプロダクトをひとまず置き換えることに成功したと言えるかなと思います。

会社のことなので書けない事が多くなりましたが、同じように悩んでる人の助けになれば幸いです。
他にも思い出したらまた追記します。

10
3
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
10
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?