入社してまもなく
先輩「じゃあflutter手伝ってもらおか」
ワイ「うす! (flutterってなんだ...?)」
さて、私は入社して一週間目でflutter開発に携わることになりました。
私は職業訓練校でJSやPHPなどを習ってきましたが、flutterとは何なのか一切知りません。
けどまあ、「プログラミング言語なんて方言みたいなもの」だし。そんな変わらないでしょ?
なんとかなるやろ!
そんな感じで始まった私の初仕事でした。
flutterのfの字も知らないようなど素人から、そこそこflutterが書けるようになるまで行き詰まったことなどをここに書いていこうと思います。
ど素人にとってflutterの何が難しいか
※あくまでプログラミング経験の浅い私からの目線です
Dartが静的型付け言語である
flutterはDartという言語のフレームワークです。そしてDartは静的型付け言語です。(動的型付けも使えますが)
PHPなどの動的型付け言語に慣れ親しんだ人間からすると、静的型付けを触るというのは最初辛いものがあります。
intやStringなどの型くらいならすぐに理解できますが、配列や連想配列となると難しくなってきます。
後述しますが、これに非同期処理が絡んでくる場合もあります。
そうなってくると型のミスマッチ(要するにType Error)を起こしやすくなり、最初はここで躓きました。
非同期処理(Future/Stream)
Dartという言語は、代替Javascript(AltJS)を目的として作られました。
AltJSですからもちろん非同期処理が用意されているのですが、その時に登場するのがFuture型です。
私が遭遇したエラーを紹介します。(簡略化しています)
Future<int?> getSteps() async {
int steps = await Authentication.myProfile!.steps; // Firebaseからデータ取得
return steps;
}
Future<double?> getWeight() async {
double weight = await Authentication.myProfile!.weight;
return weight;
}
double calcCalorie() {
int steps = getSteps();
double weight = getWeight();
double calorie = // カロリーの計算
return calorie;
}
future.dartが先輩が書いたコードで、calorie_page.dartが私が書いたコードです。
しかしこれでは、
A value of type 'Future<int?>' can't be assigned to a variable of type 'int'.
Future<int?>の値をint型には代入できないよ!と怒られます。
Future型は非同期処理の返り値として使われるものです。
以下のコードに修正すると解決します。
double calcCalorie() async {
int? steps = await getSteps();
double? weight = await getWeight();
double calorie = // カロリーの計算
return calorie;
}
asyncとawaitを追記しました。
正確な解釈ではないと思いますが、Future型というのは時間が掛かるから値が確定していないという意味だと私は解釈しています。
上のfuture.dartではFirebaseからデータを取ってきていますが、これには時間がかかります。もしかするとFirebase側のエラーでデータが返ってこないかもしれません。
でもcalorie.dartが欲しいのはそんな不確実な値ではありません。処理が終わるのを待ってでも確実な値が欲しいです。
そのために処理を待ちます。await します。
await は async関数の中でしか記述できませんので、async を書きます。(asyncはその関数が非同期関数であることを示します)
ちなみにStreamというものも存在していて、こちらも非同期処理で登場するのですが。私はわかりません(TT)
日本語情報の乏しさ
Dartはまだ発展途上の言語です。AltJSではTypescriptがかなり普及していますので、Dartはその影に隠れる形となっています。
故に日本語情報が結構少ないです。
これは日本人のプログラマーにとっては手痛いです。英語そこまで読めねえし...泣
特に外部ライブラリの情報となるともっと日本語情報が少なくなります。
幸いなことに公式ドキュメントは非常にまとまっていてわかりやすいので、英語が読めなくても大まかには理解できます。
classのメンバやメソッドが簡潔にまとまっているので超見やすいです。
公式ドキュメントなので難しいですが、日本語ドキュメントも用意されています。
外部ライブラリの情報はこちらから検索できます。
苦しいときは公式ドキュメントを読むに限ります。
Widgetがはみ出る!
flutterではWidgetというものを組み合わせてUIを構築していきます。詳説はここでは省きます。調べたらでてきますので。
今回私はflutterでモバイルアプリ作成に携わっていたのですが、スマホの画面サイズは本当にバラバラです。
ですからPCのエミュレーターでは表示に問題がなかったけど、実機だったらはみ出してしまった!ということがよくありました。
はみ出てしまうと、
「bottom overflowed by 67 pixels」みたいな警告が出ます(TT)
しかし困ったことにflutterでは%のように相対値でWidgetのサイズを指定できないみたいです。(調べましたがなさそうでした)
対応策として以下のようにしました。
final double _baseWidth = MediaQuery.of(context).size.width * 0.01;
final height _baseHeight = MediaQuery.of(context).size.height * 0.01;
@override
Widget build(BuildContext context) {
return Scaffold(
body: SizedBox(
width : _baseWidth * 10,
height: _baseHeight * 5,
// 以下略
)
)
}
こうすることで端末の画面幅に応じてWidgetのサイズを指定できます。
しかしわざわざ_baseWidthやら_baseHeightを毎回取得しなければならないのは面倒です。
相対値でWidgetサイズを指定できるようになって欲しいものです。
これ使いやすいな!
今まで難しいところばかり挙げてきましたが、便利な部分もあります。
Null Safety
dartをやっていると「?」とか「!」とか「??」とか「??=」とか、見慣れない疑問符と感嘆符が登場します。
以下のコードでの「?」はnull許容を意味します。
String? name = "hogehoge";
name = null; // エラーにならない
しかし以下のような場合は「!」をつけます。「!」はnullで無いことを確定させます。
class Member {
String? name;
}
@override
Widget build(BuildContext context) {
return Scaffold(
body : Text(Member.name!.toSrting())
)
}
地味に便利なのが「??」と「??=」です。
// 三項演算子で書く
String newData = Data == null ? '' : Data;
// ??を使う
String newData ?? '';
// ??=を使う
String newData ??= '';
「??」の挙動は、newDataがnullだったら空文字を返します。newDataがnullじゃなかったらnewDataを返します。
「??=」の挙動は、newDataがnullだったら、空文字を代入します。
三項演算子がより短い記述ですむので便利ですね。意味が理解できればかなり読みやすいですし。
まとめ
他にもいいところはありますが記事が長くなるので割愛です。
静的型付けって慣れれば便利だなとか、やっぱりプロトタイプベースじゃなくてクラスベースだよな!とかとか。
Dart/flutterはまだ若いですが、これからに期待ですね(^^)
私はJavascriptがあんまり好きでないので、できればJavascriptの代わりになってくれないかなとかボンヤリ思ったりしていますw
ではまた!
追記
2022/11/10 非同期処理に関して間違いがあったので修正しました。int steps = getSteps(); から int? steps = getSteps(); に修正しました。weightも同様です。