はじめに
アプリでよく見る引っ張って更新する機能ですが、Material3のPullToRefreshBox使って引っ張って更新機能を作ってみたいと思います。
PullToRefreshBoxの詳しい内容はこちらを参照ください。
PullToRefreshBoxを使って引っ張って更新機能を実装
PullToRefreshBoxでUIを作成
以下は単純なリストを表示するComposableです。
リストを下に引っ張ると更新機能が走るように、PullToRefreshBoxを使っています。
class MainActivity : ComponentActivity() {
private val viewModel: ListScreenViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
PullToRefreshBoxSampleTheme {
val uiState by viewModel.uiState.collectAsState()
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
ListScreen(uiState, viewModel::refresh, innerPadding)
}
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ListScreen(
uiState: ListScreenViewModel.UiState,
refreshAction: () -> Unit,
padding: PaddingValues
) {
val list = (uiState as? ListScreenViewModel.UiState.Success)?.data ?: emptyList()
val isRefreshing = (uiState as? ListScreenViewModel.UiState.Success)?.isRefreshing ?: false
PullToRefreshBox(
isRefreshing = isRefreshing,
onRefresh = {
refreshAction()
}
) {
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(padding)
) {
items(list.size) {
Text("Item $it")
}
}
}
}
PullToRefreshBoxの引数で設定している2つについて解説します。
- isRefreshing:リフレッシュが行われているかどうかを設定します。trueが設定されている場合、リストの上にローディングインジケーターが表示されます。
- onRefresh:引っ張るアクションを実行した時に動く処理を設定します。
ViewModelを用意
UiStateを用意し、表示するデータとリフレッシュの実行中かどうかを設定するisRefreshingを持たせています。
class ListScreenViewModel : ViewModel() {
sealed interface UiState {
data class Success(val data: List<String>, val isRefreshing: Boolean) : UiState
}
private val _uiState = MutableStateFlow<UiState>(UiState.Success(listOf("1", "2", "3"), false))
val uiState: StateFlow<UiState> = _uiState.asStateFlow()
// 引っ張って更新時に動かす処理
fun refresh() {
val currentUiState = _uiState.value
if (currentUiState is UiState.Success) {
_uiState.value = currentUiState.copy(isRefreshing = true)
}
viewModelScope.launch {
delay(2000)
val currentData = (currentUiState as UiState.Success).data
val newData = currentData + (currentData.size + 1).toString()
_uiState.value = UiState.Success(newData, false)
}
}
}
refresh関数の中身について解説します。
画面で引っ張って更新が実行されたら、表示データは保持しつつ、isRefreshingのフラグをtrueに設定します。
こうすることで、画面にはローディングインジケーターが表示されるようになります。
isRefreshingをtrueに設定した後、coroutineを立ち上げてリストの更新処理を実行しています。
今回はサンプル処理として、2秒遅延後にリストのデータを一つ増やすような更新処理をしています。
更新処理の最後に、uiStateに更新後のデータとisRefreshingのフラグをtrueに設定してリフレッシュ処理を終了します。
実際に動かしてみた時の動き
前述のコードで実際に動かしてみた時の動きになります。
画面を下に引っ張るとローディングが表示され、リストが更新・ローディング非表示となっています。