Jetpack ComposeがFlutterよりも圧倒的に優れているひとつのこと
概要
初期のFlutterから使っていた私からするとJetpack ComposeよりもFlutterの良い面がたくさん見えてきますが、それもJetpack Composeのバージョンアップとともに差が縮まってきているように思えます。(人によってはJetpack Composeの方が初めから優れていると思う人もいると思いますので、あくまで主観です。)
ただし、元々Jetpack Composeの方が圧倒的にFlutterよりも優れている面があります。
それはFlutterでいうウィジェットの再ビルド処理がJetpack Composeではほぼ意識しなくてもよいということです。Jetpack Composeの方がFlutterより優れている点は他にもたくさんあると思いますが、これはすごいと思えるのでこの点について、記述したいと思います。
サンプルアプリ
以下のようなアプリを作ります。A, B, Cのコンポーザブル関数があり、Cのボタンを押下するとAのテキストが変わります。その際にどこが再コンポジションするかを調べます。
Flutterの場合
今ではsetState()
を直接触ることはないかもしれませんが、setState()
で記述すると全ての再ビルドが走ります。それを避けるために、これも直接触ることはないですが、InheritedWidget
を使います。ProviderもRiverPodも状態管理をするライブラリのほとんどは、内部でInheritedWidget
を使っているはずです。
以下のリンクは、2019年01月に書いた記事なので利用しているメソッドが若干古いのですが、内容は今のバージョンで書いてもほぼ同じになります。
見ていただけるとわかると思いますが、InheritedWidget
を使うとかなり冗長な表現になります。
サンプルのような単純なアプリを作る際もこんなに書かないといけません。
Jetpack Composeの場合
Jetpack Composeの場合は以下だけです。シンプルでわかりやすく、冗長な表現もありません。
FlutterのInheritedWidget
と比べると圧倒的だと思いませんか?
@Composable
fun CountUpScreen(
modifier: Modifier = Modifier,
) {
var count: Int by remember { mutableStateOf(0) }
SideEffect { println("CountUpScreen") }
Column(
modifier = modifier.fillMaxSize(),
verticalArrangement = Arrangement.SpaceEvenly,
horizontalAlignment = Alignment.CenterHorizontally,
) {
ACompose(
count = count
)
BCompose()
CCompose {
count++
}
}
}
@Composable
private fun ACompose(count: Int) {
SideEffect { println("ACompose") }
Column(modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = "$count"
)
ChildACompose()
}
}
@Composable
private fun ChildACompose() {
SideEffect { println("ChildACompose") }
Text(
text = "ChildACompose"
)
}
@Composable
private fun BCompose() {
SideEffect { println("BCompose") }
Text(
text = "I am composable that will not be recompose"
)
}
@Composable
private fun CCompose(onClick: () -> Unit) {
SideEffect { println("CCompose") }
Button(onClick = {
onClick()
}) {
Icon(Icons.Outlined.Add, contentDescription = "+")
}
}
Jetpack Composeをあまり知らない人のために書きますが、SideEffect
関数は、コンポジション(Flutterでいうbuild)があったときに呼ばれます。このサンプルではログを出しています。
ボタンを押下時のログの結果は以下です。大本のCountUpScreenとAだけが再コンポジションしています。
I/System.out: CountUpScreen
I/System.out: ACompose
優れている点
Flutterに比べてシンプルでわかりやすく冗長な部分もないことがわかります。
が、実はこれだけではありません。よく見るともっとすごいことが起きています。
以下の部分です。
@Composable
private fun ACompose(count: Int) {
SideEffect { println("ACompose") }
Column(modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = "$count"
)
ChildACompose()
}
}
@Composable
private fun ChildACompose() {
SideEffect { println("ChildACompose") }
Text(
text = "ChildACompose"
)
}
ChildACompose()
が、ACompose
の中に含まれています。
ACompose
が再コンポジションされていると書きましたが、その中にあるChildACompose()
は再コンポジションされていません。
もし、Flutterだったら、親のウィジェットがリビルドされていたら、そこに含まれている子ウィジェットもリビルドされてしまいます。
このようにJetpack Composeは必要な箇所だけを再Compositonをするという優れものなのです。
いやぁ、これはすごいことだと思います。
Flutterだと細かなパフォーマンスチューニングを気にしだすと必須の知識が、Jetpack Composeだとよしなにやってくれるのです。
これは素晴らしい!
Flutterにのぼせている(?)方々、一度Jetpack Composeを触ってみるのもいいかもしれませんよw
ソーシャル経済メディア NewsPicks メンバーの発信を集約しています。公式テックブログはこちら→ tech.uzabase.com/archive/category/NewsPicks
Discussion
Compose初心者ですが、なるほどと理解しました。ありがとうございますm(__)m