一週間で作ったお化け屋敷専用アプリの道のり

Flutterだと思って読みに来た人すみません。Flutter要素3割くらいしかありません。
文化祭の苦労をネタにしただけなので僅かなFlutter要素にとびたかったら[[Flutterとは](###-Flutter-とは)]まで

## 前置き

### 文化祭一ヶ月前....

自分:とりまクラスの文化祭打ち合わせでるかぁ(寝起き & 起こされた)

 .. 打ち合わせ会場..

**企画担当者**: 技術できるの〇〇とMoominkだからデフォルトで班にいれました。〇〇はハード担当で、Moominkにはスマホアプリを作ってほしい。スマホで謎解きをしたいと思ってる。詳細は未定。

<b>????</b>

自分: まあ、卒検発表近いし作業は後ででいいか


---

### 文化祭一週間前....

企画担当者
発表お疲れ様です。**来週は文化祭なので本格的に作業を勧めます**。
スマホの方は、チャットみたいな画面で謎解きをしたいと思ってる。ライト点灯と画面の明るさ管理もできるようにしといて。あと、操作はWEB上でできるようにしてほしい。

自分: 分かりました(しか言えない)

---

### おさらい
* お化け屋敷のスマホアプリを作ることになった
* 要件は、[謎解きのヒントを出すチャット, ライト管理, 明るさコントロール] ができるスマホアプリとそれをコントロールするWEBの作成
* 期限は一週間、作業は１人

### さて..どう作るか...
KotlinやらSwiftみたいなNative言語で書いてたら間に合わなさそう。(というかアプリ用の書き方知らない)
動けばいいなら触ったことあるFlutterで書くかぁ。~~パフォーマンス? 状態管理? 知らない子ですねぇ~~

**前置き終わり**

## 開発RTA

### 一日目

自: とりまFlutterでハード周りの要件満たせるかパッケージ探しまくるかぁ

= = パッケージお調べ & 適当UIで動くかテスト = =

自: よし、明るさ変更できるしハードのライトも制御できるの判明!動くこと判明したしなんとかなりそうって明日報告するか!

---

### 2日目
自: ハード周りはうごいたからなんとかなりそうよ ~ 

企画担当者: チャットの詳細仕様が決まったから送るね。WEB画面からチャットと画像の送信をできるようにしてほしい。最悪、送信じゃなくてテンプレの表示でもいいよ~

自: やるだけやってみるわ〜 (なんとかなると思ってた)

= = 帰寮 = =

自: そもそもWEB触ったことないんだがどうしたらええんや....とりまWEBとアプリの通信手段探すか。
(ﾎﾟﾁ ﾎﾟﾁ)お! SocketIO良さそうやん。Flutterのパッケージもあるしこれで通信系はなんとかなるはず!コード書くかぁ...

= = 午前5時前 = =

自: 我文章送信機構構築完了。我濃度触初、珈琲部品意味不明、我沢山頑張。既睡魔到来。然亜腐李未完成、故我睡眠不可。

= = スマホ側のUIｶｷｶｷ = =
自: 既六時半超過、点呼後一刻。我我慢不可、源文章管理機構宛送信完了後、睡眠開始。

(その後、点呼寝過ごす)

---

### 3日目

自: ブログありがと感謝感謝、node触ったことなかったらまじで助かった...
昨日(寝るまでが一日)は文章送信とWEB(サーバ含む)を完成させたから、今日は画像送信の昨日を追加するぞ~

= = コードｶｷｶｷ = =
自: よし、画像送信の機構追加してチャットぽいUIにしたしもう完璧や！文化祭の日は東京のイベントに行きたいから早めに終わらせてM3行くぞ!

---

### 4日目
自: 今こんな感じでできてます。
(アプリ見せる)

自: (意外と余裕やったな...イベント行けるか?)
企画担当者: いいね！WEBUIの方なんだけど、テンプレ文章を送信するボタンを追加してほしいな！誰でも操作できる感じにしたい。
自: (SocketIO完全理解者()のワイには朝飯前や) ウィッス

暇: このgif入れたらおもろくね？(怖い画像)
自: 採用や！後で送ってクレメンス!

鞄: TwitterのDMからUI抜き取ったけど楽できる?
自: 天才や！ありがたく使わせてもらうで！


= = 帰寮 = =

自: というか、チャットと画像だけじゃ絶対怖くならんやん。音と振動加えた方が絶対怖くなるやん。仕様追加や!

= = コードｶｷｶｷ = =

(午前4時前)
自: 我音声斗画像追加完了。我天才、源文章改良追加完了。追加仕様来訪問題無、完璧。然振動未完成。明日之我託。我寝。

---

### 5日目
自: とりまコード追加するかぁ...
あ、そういえば今日懇談会やん。忙しくてスライド作ってなかったけど作らなアカンわ。コード後回しや!

(爆速スライド作成 & 懇談会)
自: あぶねぇ...スライドギリギリ間に合ったけどなんとかなったわ。よし、コードに戻るか

= = コードｶｷｶｷ = =

(午前6時前)
自:恐怖機構開発完了、操作画面改良追加完了。我、天才。残作業恐怖機構調整斗虫修正。我睡魔限界、寝....

(点呼寝過ごす)


### 6日目 (祗園祭前日)
自: こんな感じになったで、操作方法はこんな感じでおねがいします。
企画担当者: ナイス！後は微調整とバグ修正お願い！

自: (バグは発見次第潰したからもう無いはず)うい

= = 流れの練習 = =

企画担当者: あれ？なんかライトがつかないんだけど？どうなってるの？
自: **バ  グ  で  す(徹夜確定演出)**

自: イベント行かずにコード修正するしかないやん。しかも、この調子だと絶対本番も不具合でるやん...
コード知ってるの自分しかいないからずっといることになりそう...とりまバグ修正するか

= = コードｶｷｶｷ = =

(午前7時半前 祗園祭開始まであと1時間半)
自: 不具合修正完了...文化祭開始後一刻半、寝時間無。朝飯食事後直接企画会場向。


### 7日目 (祗園祭当日)
自: 思ってたよりも驚いてくれるやん...開発者冥利に尽きるわ...

クラスメイト
謎解きで与えたヒントでも難しかったりする場合があるから、テンプレとは別にお助け文字送る必要あるかも

自: 一応、デバッグ用に任意の文字が送信可能だからそこからやってみれば?

クラスメイト
いや、ストーリの人物が喋ってるのか分からないから神の声的な感じで分ける必要あるかも?
..このチャットの部分ってコンポーネントで実装してるでしょ？ (空気が変わる瞬間)

自: そうだけど..どうした？

クラスメイト
<font color="red">お助け文字だけ別で赤文字にする事ってすぐにできたりする？</font>  ← 本番当日の仕様変更
自: (動けばいいと思ってたからそこらへんは適当なんだよな..というか)とりまやってみる

= = 祗園祭1日目終了 = = 
自: (サーバ側のコード適当に書いたせいで)赤文字にできませんでした。明日までにはなんとかします。

= = コードｶｷｶｷ = =
(午前2時)
自: 我過労、我疲労困憊、我絶対明日遊.....


## 8日目 (祗園祭2日目)

自:赤文字完了しました。とりま不具合があっても対処できるようにはしとくんで運営は任せました。(他力本願)
企画担当者: うい！ありがとう〜

== 開発終了 ==



---
---



## ここから技術
### Flutter とは?
https://flutter.dev

組み込みからWEBまであらゆるアプリの開発を可能にするGoogle製のフレームワークです。元々はスマホアプリのみでしたが、ver 2.1 にてWindows、ver 3ではMacOSとLinuxがstableになりました。

言語としては、これまたGoogleが開発しており、~~AltJSとして世に出して撃沈した~~Dart言語を使用して記述します。

---

### お世話になった機能・パッケージ
#### Lock Task mode
https://developer.android.com/work/dpc/dedicated-devices/lock-task-mode

通称、kiosk mode

通常、Androidアプリの挙動としてアプリを全画面で開いたとしても通知画面・ナビゲーションバーが表示されるのですが、他のアプリを開いたりライトの挙動を勝手に調整されると雰囲気が破壊されるため制限する必要がありました。

このモードを使うとなんと、アプリのみが操作可能になってナビゲーションバー等はどんなに頑張っても操作することはできません。(大いなる力ってスゲー)

もちろん、大いなる力には大いなる責任が伴うのでそれ相応の対価が必要です。

**対価 : 初期化したスマホ**

はい、高専生にとっては安かったですね。クラスの誰かが初期化しても構わないスマホを持ってるので借りましょう。


あとは、Flutterに対応したパッケージを適当に探して実行するだけです。
コード見れば分かるのですが、Androidの機能を呼び出してるだけなので時間に余裕があればプラグインなんて使わなくてもいけます。

https://github.com/herryhou/kiosk_plugin

Kiosk mode の注意点として、画面がアプリ固定になるのでトーストや通知みたいなアプリよりも上位層に表示するやつは使えません。使いたかったら一旦解除してから使いましょう。

---

#### Screen Brightness
https://pub.dev/packages/screen_brightness

見れば分かる、画面の明るさ制御パッケージです。

```dart:SetBrightness
Future<void> setBrightness(double brightness) async {
  try {
    await ScreenBrightness().setScreenBrightness(brightness); // brightness : float
  } catch (e) {
    print(e);
    throw 'Failed to set brightness';
  }
}
```
テストの段階で判明したので良かったのですが、どうやら機種によっては0.0 == 1.0になるらしいので気をつけましょう。

---

#### Socket IO
https://socket.io

https://pub.dev/packages/socket_io_client

今回、過労死してくれたSocket io君
WebSocketを通じて双方向通信してくれるライブラリです。

文化祭では、利用者のスマホ画面をWEB側から操作する必要がありました。
しかし、現実問題としてWEBクライアント側からスマホに対して指示を出すのは難しいです。(無知なだけ)
また、スマホは一台だけでなく、予備として追加で1台用意して計二台の体制でしたのでIP指定はめんどくさいです。

なので、SocketIOを使用してスマホ2台とWEBをSocketIOでつないでイベントと送るデータでアプリを操作することにしました。触った事はなかったのでチュートリアルとWEB記事ガン見で書きました。

```mermaid
flowchart LR
app(スマホ)
WEB(管理画面)
server(鯖)

server -- 指示出す --> app
WEB -- 指示出す --> server

```

イベントに応じて分岐しまくってたのでinitStateがどえらいことになってます。
```dart:initState
  @override
  void initState() {
    super.initState();

    socket.onConnect((_) {
      print('connect');
    });
    socket.on(
	"debug_message",
	(data) => Provider.of<MessageData>(context, listen:false)
	    .addWidget("debugMessage",data));
    socket.on(
        "message",
        (data) => Provider.of<MessageData>(context, listen: false)
            .addWidget("message", data));
    socket.on(
        "image",
        (data) => Provider.of<MessageData>(context, listen: false)
            .addWidget("image", data));

// ..........

// some switch case

// ...........


    socket.onDisconnect((_) => print('disconnect'));

    socket.connect();
  }
```

という感じで気合で設定していきました。今思うともう少しきれいなコードにしろって思います。

動かしてわかった点として(自分がドキュメント見てないだけか環境の問題)、SocketIOがサーバ側からemitでイベントを複数端末に送信する際、どうやら同時ではなく1端末に送信が完了してから別端末に送信を開始しているらしくラグがひどかったです。

---

#### Visiblity Widget & Stack Widget
https://api.flutter.dev/flutter/widgets/Stack-class.html

https://api.flutter.dev/flutter/widgets/Visibility/Visibility.html

可視・不可視を変更できるWidget と Widget同士を重ねて表示できるWidget

驚かす画像の可視変更をこのWidgetとSocketIOで制御してました。

チャット画面を上書きするような演出を考えていたのですが、出現画像が一枚しかなかったので予め不可視の状態でスタックさせといて特定のタイミングで表示すればよくね？という結論に至りました。

```dart:
Stack(children: <Widget>[
          Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: <Widget>[
                Flexible(
                    // チャット画面
              ]),
          Center(
              child: Visibility(
                // "action" イベントがSocketIOからきたら _visibleを切り替える
                  visible: _visible,
                  child: FittedBox(
                      fit: BoxFit.fill,
                      child: Image.asset("assets/baby.gif"))))
  ])
```
画像サイズがおもってたよりも大きく、最初は画面からはみ出してましたけどFittedBoxでサイズ調整しときました。



## 完走した感想

技術ができるってバレると良くも悪くも色々と頼られます。
脳ある鷹は爪を隠すとも言うのでそこらへんは気をつけましょう。

とはいいつつも、完成してからはクソ楽しかったです。
やっぱり自分が作ったアプリが実際に使われてるの見ると嬉しいですし、クラス企画の運営に携われた点としても楽しかったです。
もう少し計画的に物事進められてたらもっと楽しかったと思います。

**文化祭でクラス企画に携わる人は自分ができないからといって関わらないのではなく、
自分ができる範囲からでもいいのでみんなで協力して良い企画にしましょう**



## 感謝すべき人
* hmjn:[@hmjn023](https://mobile.twitter.com/hmjn023) 
数少ないFlutterが読めるカスタムROM人、画像頂いたり自分がいない間のトラシュを担当してくれた神

* かばん君
TwitterのDM画面をスクショして流用すれば？とかいう記述コード削減の神提案をしてきた、まじで助かった

* わくと:[@otukaw](https://mobile.twitter.com/otukaw)
初期化しても許されるスマホを貸してくれた低レイヤ人、貸してくれなきゃ企画倒れしてました

* いわんこ:[@Iwancof_ptr](https://twitter.com/Iwancof_ptr)
バグの解決策を一瞬で編み出した天才、神様かあんたは

### Github君
アプリの方

https://github.com/Moomink/gionsai_2022_5j

サーバの方

https://github.com/Moomink/gionsai_5j_server

正直、一週間での開発でしたのでREADME等整備してません。この記事も自分に対する備忘録と戒めを込めた記事なので..

この記事・Githubが誰かの参考になれば幸いです。

　　　　　　　　　　　　　　　　　 　　終
　　　　　　　　　　　　　　　　　 制作・著作
　　　　　　　　　　　　　　　　　 ━━━━━
　　　　　　　　　　　　　　　　　~~ⓃⒽⓀ~~

#制作著作 https://seisakuchosaku.sacnoha.com/