🐥

【Jetpack Compose】StateFlow でカウンターを作ってみる

2022/06/19に公開

Composable 入れ子の更新具合を確認します。

@HiltViewModel
class SampleViewModel @Inject constructor() : ViewModel() {

  private val _count = MutableStateFlow(0)
  val count = _count.asStateFlow()

  fun inc() {
    _count.update { count -> count + 1 }
  }

}

👉 StateFlow の View への公開

@Composable
fun SampleScreen(
  viewModel: SampleViewModel = hiltViewModel()
) {

  val count: Int by viewModel.count.collectAsState()

  Column(
   Modifier.fillMaxSize(),
   Arrangement.Center,
   Alignment.CenterHorizontally
  ) {
    Text("$count")
    Button(onClick = { viewModel.inc() }) {
      Timber.d("Button composed.")
      Icon(Icons.Filled.Add, null)
    }
  }
  
}

何回クリックしても Button の compose は一回。

Button composed.

Button 内に影響される Text() を追加する。

@Composable
fun SampleScreen(
  viewModel: SampleViewModel = hiltViewModel()
) {

  val count: Int by viewModel.count.collectAsState()

  Column(
    Modifier.fillMaxSize(),
    Arrangement.Center,
    Alignment.CenterHorizontally
  ) {
    Text("$count")
    Button(onClick = { viewModel.inc() }) {
      Timber.d("Button composed.")
      Icon(Icons.Filled.Add, null)
      Text("$count") // *
    }
  }
  
}

クリックするたびに、Button は compose される。

Button composed.
Button composed.
Button composed.
Button composed.
Button composed.
Button composed.
...

ネストした Composable

@Composable
fun SampleScreen(
  viewModel: SampleViewModel = hiltViewModel()
) {

  val count: Int by viewModel.count.collectAsState()

  Column(
    Modifier.fillMaxSize(),
    Arrangement.Center,
    Alignment.CenterHorizontally
  ) {
    Text("$count")
    ChildA(onClick = { viewModel.inc() })
    ChildB(count, onClick = { viewModel.inc() })
  }

}

@Composable
fun ChildA(onClick: () -> Unit) {

  Button(onClick = onClick) {
    Timber.d("ChildA composed.")
    Icon(Icons.Filled.Add, null)
  }

}

@Composable
fun ChildB(count: Int, onClick: () -> Unit) {

  Button(onClick = onClick) {
    Timber.d("ChildB composed.")
    Icon(Icons.Filled.Add, null)
    Text("$count")
  }

}

ChildA は初回のみ compose されます。 ChildA、ChildB どちらのボタンを押しても re-compose されるのは、ChildB のみです。

D: ChildA composed.
D: ChildB composed.
D: ChildB composed.
D: ChildB composed.
D: ChildB composed.
D: ChildB composed.
D: ChildB composed.
D: ChildB composed.
D: ChildB composed.
D: ChildB composed.
D: ChildB composed.
D: ChildB composed.
D: ChildB composed.
D: ChildB composed.
...

無駄な描画をしない!

すごいです、Jetpack Compose!!

ただ、カウンター程度なら Flow を使うまでもないが。

👉 JakeWharton/timber: A logger with a small, extensible API which provides utility on top of Android's normal Log class.
👉 Jetpack Compose State Guideline. This article presents the information… | by takahirom | Medium
👉【Jetpack Compose】Icon() や Image() で ImageVector をより便利に使う

Discussion