私:この100をdynamicに入れといて。後でスライドバーが取りに来るからよろしく。
Flutter:わかりました。
スライドバー:doubleくださいな。
Flutter:ちょっと見てきますね…そんなものはありません。
私:さっき100渡したでしょ。
Flutter:100はintです。
私:いや、勝手にintにしろなんて言ってないよ。
Flutter:ひどい…あなたが何も言わなかったから、私がちゃんと整理して入れておいたのに…
私:それなら、doubleちょうだいって言ったときもintをdoubleに変換してよ。
Flutter:それはできません。
私:はぁ???
Flutter:型安全ですので。
こうやって文字にしてみると
圧倒的に私が悪いですね。反省。
問題のコードはこれ
final dynamic initialValue;
この、initiate valueには、数字とbool型両方を入れることが想定されるので
dynamicにしていました。
そして、
values = {
for (final p in widget.definition.touchParams)
p.key: switch (p.uiType) {
TouchUiType.slider =>
p.initialValue ?? p.min ?? 0,
_ =>
p.initialValue,
},
};
width: values['width']
入れた値を、スライドバーで使いたかったので
widthとして呼び出しています。
すると、こんなエラーが出ました。
type 'int' is not a subtype of type 'double?'
正直、何を言っているのか意味がわからないので
ChatGPTにエラーログをコピペして教えてもらいます。
すると、
表示されたエラーメッセージの
スタックトレースが指した箇所はここです、とのこと。
width: values['width'] ?? 100,
height: values['height'] ?? 80,
なるほど。確かにint入れてるっぽい。こうすればいいのかな。
width: values['width'] ?? 100.0,
height: values['height'] ?? 80.0,
しかし、これでは直りませんでした。
エラーの原因
結局、冒頭に書いたような
行き違いが原因でした。
私としては、dynamicに入れたんだから
intで入れたつもりはない。
しかも、表示はintにしたいと思っていたので
doubleで呼び出したつもりも全くない。
それなのに、
Flutterがdynamicに入れる時点で
勝手にintに変換し、
Sliderがdouble 前提で値を参照しに来たので
エラーになっていた、ということでした。
本当にわけが分かりませんでした。
解決方法はこれ
values = {
for (final p in widget.definition.touchParams)
p.key: switch (p.uiType) {
TouchUiType.slider =>
((p.initialValue ?? p.min ?? 0) as num).toDouble(),
_ =>
p.initialValue,
},
};
as numを挟んで
doubleで渡す。
さらにややこしい話
以前、ちょっとだけC++をかじったことがあるのですが
こういうのってエラーにならなかったんですよね。
int x = 100;
double y = x;
つまり、doubleってintの上位互換だと思っていたんです。
そして、実はFlutterでも
このコードはエラーになりません
Dartは int → double を許しているんです
じゃあ何がいけなかったのか。
今回のエラーの本質はこれでした。
dynamic x = 100;
double y = x;
コンパイル時に int だと分かっていると
doubleに変換してくれてエラーにならないけど
dynamicで入れていると
実行時にわざわざ中身を確認しに行くからエラーになる
なんだそれ…難しすぎるだろ…
まとめ
Dartは int → double を許しているが
dynamic や Map を経由すると型チェックのタイミングが変わり
実行時エラーになることがある
ということで
調べてみたら思ったよりややこしい話でした。
そもそも人間にとっては
100=100.0なわけで
この話が腑に落ちるまで、かなり時間がかかってしまいました。
それにしても、こんな真っ赤な画面を出して
かなりご立腹の様子。
そんなに怒らんでも…