AccompanistのSwipeRefreshからcompose.materialのPullRefreshに置き換える

Accompanist にあった `SwipeRefresh` が compose.material に `PullRefresh` として実装されました。
Accompanist 側にマイグレーションガイドがまだないので、どのように置き換えることになるのか書いていこうと思います。

Accompanist 0.27.0、compose.material 1.3.0 時点の内容になります。

## 基本的な置き換え

以下のような Accompanist の `SwipeRefresh` で実装された処理を置き換えていきます。

```kotlin
val viewModel: MyViewModel = viewModel()
val isRefreshing by viewModel.isRefreshing.collectAsState()

val state = rememberSwipeRefreshState(isRefreshing)

SwipeRefresh(
    state = state,
    onRefresh = { viewModel.refresh() },
) {
    LazyColumn {
        items(30) { index ->
            // list items
        }
    }
}
```

`rememberSwipeRefreshState` を　`rememberPullRefreshState` に置き換えます。
また Pull された時のコールバックが State 側に移動しています。

```kotlin
// Before
// val state = rememberSwipeRefreshState(isRefreshing)

// After
val state = rememberPullRefreshState(
    refreshing = isRefreshing,
    onRefresh = { viewModel.refresh() }
)
```

`SwipeRefresh` を `Box` + `PullRefreshIndicator` に置き換えます。
またリストのスクロールを検知するために `Modifier#pullRefresh` を `Box` に設定する必要があります(`Modifier#pullRefresh` は内部で `nestedScroll` を実装している)。

```kotlin
// Before
// SwipeRefresh(
//     state = state,
//     onRefresh = { viewModel.refresh() },
// ) {
//     // contents
// }

// After
Box(modifier = Modifier.pullRefresh(state)) {
    // contents

    PullRefreshIndicator(
        refreshing = isRefreshing,
        state = state,
        modifier = Modifier.align(Alignment.TopCenter)
    )
}
```

挙動としては↓のようになり、大きな差がなく置き換えることができます。

|Accompanist|compose.material|
|:--:|:--:|
|![Accompanist version](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/8d338118-69fb-caac-6c27-08a763be4e85.gif)|![compose.material version](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/126298/55e8904c-afbd-5f1c-6f40-15b8f025fd25.gif) |



### 全コード

- Before

```kotlin
val viewModel: MyViewModel = viewModel()
val isRefreshing by viewModel.isRefreshing.collectAsState()

val state = rememberSwipeRefreshState(isRefreshing)

SwipeRefresh(
    state = state,
    onRefresh = { viewModel.refresh() },
) {
    LazyColumn {
        items(30) { index ->
            // list items
        }
    }
}
```

- After

```kotlin
@Composable
fun SwipeRefreshSample() {
    val viewModel: MyViewModel = viewModel()
    val isRefreshing by viewModel.isRefreshing.collectAsState()

    val state = rememberPullRefreshState(
        refreshing = isRefreshing,
        onRefresh = { viewModel.refresh() }
    )

    Box(Modifier.pullRefresh(state)) {
        LazyColumn {
            items(30) { index ->
                // list items
            }
        }

        PullRefreshIndicator(
            refreshing = isRefreshing,
            state = state,
            modifier = Modifier.align(Alignment.TopCenter)
        )
    }
}
```


## 既存の API の移行先

|Accompanist|compose.material|
|:--|:--|
| `SwipeRefresh(swipeEnabled)` | `Modifier.pullRefresh(enabled)` |
| `SwipeRefresh(refreshTriggerDistance)` | `rememberPullRefreshState(refreshThreshold)` |
| `SwipeRefresh(indicatorAlignment, indicatorPadding)` | `PullRefreshIndicator` の Composable 関数に対して配置するときに指定する |
| `SwipeRefreshIndicator(backgroundColor, contentColor)` | `PullRefreshIndicator(backgroundColor, contentColor)` |
| `SwipeRefreshIndicator(scale)` | `PullRefreshIndicator(scale)` |
| `SwipeRefreshIndicator(refreshingOffset)` | `rememberPullRefreshState(refreshingOffset)` <br />ただし Indicator のサイズ + Offset で指定する必要がある |
| `SwipeRefreshIndicator(largeIndication)` | - <br />(`Modifier.scale` で調整可能ではある) |
| `SwipeRefreshIndicator(fade)` | - |
| `SwipeRefreshIndicator(arrowEnabled)` | - |
| `SwipeRefreshIndicator(shape)` | - |
| `SwipeRefreshIndicator(elevation)` | - |


Indicator のカスタマイズはまだパラメータが少ない状態なので、compose.material-1.3.0 時点では移行が難しい場合があるかもしれません。

## 参考

- https://google.github.io/accompanist/swiperefresh/
- https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material/material/samples/src/main/java/androidx/compose/material/samples/PullRefreshSamples.kt;l=57?q=PullRefresh&sq=&ss=androidx