Dart3はFlutter側に結構大きな変更があって、なかなかメインプロダクトでバージョンを上げられなかったので使うのが遅れてしまいました。
Dart3で追加されたSealedクラスを見てみます。
用途としてはKotlinのSealedと同じです。 今まではDart自体にUnionの機能がなかったので、freezedパッケージ を使って再現するのが一般的だったかと思いますが、ついに言語自体でサポートされました。
sealed classだとtoStringが実装されていないので、そのままfreezedでも良いのですが、build runnerを走らせなくても良いのは使い勝手が良いなという感想です。
使い方
一般的な書き方はこんな感じです。
Kotlinとほぼ同じですね。
sealed class Hoge {
const Hoge();
}
class Foo extends Hoge {
const Foo();
}
class Bar extends Hoge {
const Bar();
}
これだけだとEnumと変わらないので、プロパティを持つこともできます。
それぞれ別のプロパティを持つことも可能です。
普通のクラスになるので、そのままデフォルトのプロパティを持ったりもできます。
sealed class Hoge {
const Hoge();
}
class Foo extends Hoge {
const Foo({this.foo = false});
final bool foo;
}
class Bar extends Hoge {
const Bar();
final bool bar = false;
}
親クラスに共通のプロパティを持つこともできますし、名前付き引数を使わなくても大丈夫です。
sealed class Hoge {
const Hoge(this.hoge);
final bool hoge;
}
class Foo extends Hoge {
const Foo(super.hoge);
}
class Bar extends Hoge {
const Bar(super.hoge);
}
ただこれだとprintしてもInstanceになってしまうのは注意してください。
final foo = Foo(true);
print(foo);
// Instance of 'Foo'
そのため、こんな感じで toString
をそれぞれ実装する必要があります。
sealed class Hoge {
const Hoge();
}
class Foo extends Hoge {
const Foo({this.foo = false});
final bool foo;
@override
String toString() => 'Foo(foo: $foo)';
}
class Bar extends Hoge {
const Bar();
final bool bar = false;
@override
String toString() => 'Bar(Bar: $bar)';
}
final foo = Foo(true);
print(foo);
// Foo(foo: true)
利用側
利用側はswitchで場合分けができます。
コンパイラがsealedクラスを把握しているため、defaultも実装しなくて良いです。
void hoge(Hoge hoge) {
switch (hoge) {
case Foo():
print(hoge.foo);
case Bar():
print(hoge.bar);
}
}
他にもいくつかDart3の機能を使っていますが、全体的に良いアップデートで使いやすくなりましたね!