はじめに
Flutter2のリリースについて
2021年3月4日にFlutter2がリリースされました。Flutter2では、Android/iOSだけではなく、Windows/Mac/Linuxなどのデスクトップアプリに加え、Webアプリ版もリリースされました。Flutterに魅力を感じ、さまざまなプラットフォーム用にアプリケーションを作りたいと思う開発者にとっては、まさに今回のリリースはFlutterの今後の世界を広げるリリースの1つとなることでしょう。
一方、AndroidやiOS用のスマホアプリ用と捉えている方にとっては、メジャーアップデートというよりは、マイナーアップデートに近い印象です。また、Web版は開発過程でエミュレータの代わりに使う場合に便利そうな印象を受けました。
そして、今回紹介しているDartも2.12に更新されました。Dartの更新もマイナーアップデートなのですが、実はNull safetyという大きな変更があります。これは、関連するライブラリなども含めて全体的な変更が必要になります。そのため、実際に活用するのはもう少し先になりそうです。
Flutter2のコマンドを使ってプロジェクトを作成してもDart2.12以前のバージョンがデフォルトになっていたので、利用する人は意識的に有効にする機能という位置付けになります。
対象読者
Dart言語について特に知っている必要はありませんが、他の言語の基本を知っている方を対象に説明いたします。特に、JavaScriptやTypeScriptもしくは、Javaなど言語を使ってプログラミングしたことがある方であれば、より理解がしやすくなります。
カスケード表記
同じオブジェクトのメソッドを連続して実行する場合には「..」を使った便利な表記方法があります。「..」演算子を利用することで、メソッドチェーンと同じような記述が可能になります。
// (1)使わない場合 var b1 = Builder(); b1.text = "sample"; b1.opts = Opts(); b1.opts.type = "normal"; b1.create(); // (2)カスケード表記を使った場合 var b2 = Builder() ..text = "sample" ..opts = ( Opts() ..type = "normal" ) ..create();
(1)は、カスケード表記を使わない場合の例です。このコードを、カスケード表記を使って記述すると(2)のようになります。カスケード表記とインデントとを併用するとわかりやすいコードが記述できます。
コレクション作成を拡張する表現
Dartでは、コレクションを定義する際に非常に便利な表現機能があります。
spreads表現
spreads表現とは、コレクションの前に「...」を置くことで中身を展開できるしくみのこと。既存のコレクションデータを利用して、新たにコレクションデータを作る場合に便利な機能です。リスト2は、既存のListデータを利用して新たにListデータを作成する場合の例です。
// (1) 通常のListデータの定義 ListandroidWidgets1 = [ Widget("android1"), Widget("android2"), ]; List children1 = [ Widget("sample1"), ...androidWidgets1, // (2) ...を使った表現 Widget("sample2") ]; // 実行結果 print(children1); // [sample1, android1, android2, sample2]
(1)は挿入するためのListデータです。そして、(2)のように、...を使うと(1)で生成したListデータを展開したものをその場に挿入できます。List以外にもMapなどでも同様の表現ができます。
Collection if表現
さらに、コレクション要素を追加する際に、条件分岐や繰り返しの処理を加えることも可能です。リスト3は、コレクション定義内に条件分岐(collection if)を使う例です。
var isAndroid = false; var isIos = true; var appends = [Widget("append1"),Widget("append2")]; Listchildren = [ Widget("sample1"), // (1) ifを記述 if(isAndroid) AndroidWidget("sample2") // elseでさらに条件を追加 else if(isIos) IOSWidget("ios1") // (2)複数を追加したい場合にはspreads表現を組み合わせる else ...appends, Widget("sample3") ]; print(children); // [sample1, ios1, sample3] // 参考 : isAndroid = true, isIos = false の場合の結果 // [sample1, sample2, sample3] // 参考 : isAndroid = false , isIos= false の場合の結果 // [sample1, append1, append2, sample3]
(1)のように、if...else if...elseがリスト定義の中で利用できます。ただし、通常のif文と異なり、{}の配下に複数のコードを記述することはできません({}を記述した場合には、Map型と見なされます)。複数の子要素を追加したい場合には、先ほど紹介したspreads表現を利用してください。
Collection for表現
同じく、コレクション内で繰り返し処理を加えることもできます。リスト4は、その例です。
var appends = ["append1","append2"]; var children = [ Widget("sample1"), Widget("sample2"), for(var name in appends) Widget(name), // (1) Widget("sample3") ]; print(children); // [sample1, sample2, append1, append2, sample3]
(1)のようにコレクション定義内にforを利用することができます。先ほどのspreads表現、collection ifとcollection forを組み合わせることで、非常に強力な表現ができるようになります。
nullを考慮したコード
Dartは、nullの可能性がある変数を言語レベルで安全に扱うことができます。
特に、バージョン2.12(Flutter2と共にリリース)からはより安全なnullに対策ができるコードが記述できます。この2.12からの変更点は非常に大きいため、現在のコードを記述する上でも知っておいた方がよい内容です。すぐに対応が必要というわけではありませんが、その内容についても簡単に触れたいと思います。
Dart 2.11まで(Flutter1)でのnullを考慮したコード
現行バージョンでは、nullの可能性がある変数にアクセスする際に、「?.」を利用するとnullの場合にはその後の変数にアクセスすることを抑制することができます。そのため、リスト5のようにそれぞれの変数でnullチェックをしなくてもエラーにならないコードを記述できます。
// (省略) main(){ var root = Node.root(); root.appendChild("child1"); // どのインスタンがnullでもエラーにならない記述 var target1 = root?.firstChild?.next?.firstChild; // (1) // (省略) }
(1)のようにインスタンス変数にアクセスする際に「.」の代わりに「?.」を利用します。このサンプルコードでは、nextという変数がnullになるのですが、「?.」が指定されているために、その後のfirstChildは評価されません。またこの式の値はnullになります。
ただし、本来はnullになってはいけいない場所で使用してしまうとエラーが発生しないためにより問題が分かりにくくなるケースもあるのでご注意ください。
Dart 2.12以降(Flutter2)でのnullへの考慮
先ほどの例は、変数の値にnullが入っている場合でも、nullチェックせずに安心して変数を利用するためのコードでした。一方、2.12以降では変数の定義自体にnullを許可、または、許可しないという管理ができるようになります。
ただし、2.12以前で利用してきた表記方法の意味が変わってしまうため、非常に大きな変更です。今すぐに移行が必要になることはありませんが、将来的には影響が出てくることが予想されるので、新規にコードを記述する際には変更を意識して準備しておく必要があるでしょう。
リスト6は、バージョン2.12以降を考慮した場合のコード例です。
int? num = null; // (1) int num2 = 2; // (2) int? randValue(){ // (3) return null; } class Node{ late int size; // (4) Node(){ size = 0; } }
(1)はnullを許容する変数の定義例です。型指定の後に「?」を指定することで、この変数にはnullが設定される可能性があることを示します。現行バージョンでの「?」を指定しない場合と同様です。
(2)はこれまでの記述と同様ですが、こちらはnullの値が設定できない変数の定義方法です。このようにnullを許容する変数は明示する必要が生じ、これまでと意味が異なります。また、(3)のように関数の結果にnullがあり得る場合には、同様に「?」を指定します。「?」の指定は引数などでも同様のルールになります。
インスタンス変数を定義する場合も同様です。しかし、nullを許容しない変数でも定義時には値を指定できず、コンストラクタで指定する場合があります。その場合には、(4)のようにlateキーワードを使ってそのことを明示します。このように大きな違いが生じていることがわかります。ここではすべてを紹介してできませんが、詳しく知りたい方はこちらを参照してください。