1
1
package io.goooler.demoapp.detail.ui
2
2
3
+ import androidx.compose.animation.core.Spring
4
+ import androidx.compose.animation.core.animateDpAsState
5
+ import androidx.compose.animation.core.spring
3
6
import androidx.compose.foundation.clickable
4
7
import androidx.compose.foundation.layout.Box
5
8
import androidx.compose.foundation.layout.Column
6
9
import androidx.compose.foundation.layout.Row
7
10
import androidx.compose.foundation.layout.Spacer
8
- import androidx.compose.foundation.layout.fillMaxSize
9
11
import androidx.compose.foundation.layout.height
10
12
import androidx.compose.foundation.layout.padding
11
13
import androidx.compose.foundation.layout.size
12
14
import androidx.compose.foundation.layout.width
13
- import androidx.compose.foundation.rememberScrollState
14
- import androidx.compose.foundation.verticalScroll
15
+ import androidx.compose.foundation.lazy.LazyColumn
16
+ import androidx.compose.foundation.lazy.items
15
17
import androidx.compose.material.icons.Icons
16
18
import androidx.compose.material.icons.filled.Share
17
19
import androidx.compose.material.icons.filled.Star
@@ -20,6 +22,7 @@ import androidx.compose.material3.ButtonDefaults
20
22
import androidx.compose.material3.ExperimentalMaterial3Api
21
23
import androidx.compose.material3.Icon
22
24
import androidx.compose.material3.MaterialTheme
25
+ import androidx.compose.material3.Surface
23
26
import androidx.compose.material3.Text
24
27
import androidx.compose.material3.pulltorefresh.PullToRefreshContainer
25
28
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
@@ -28,12 +31,13 @@ import androidx.compose.runtime.LaunchedEffect
28
31
import androidx.compose.runtime.collectAsState
29
32
import androidx.compose.runtime.getValue
30
33
import androidx.compose.runtime.mutableStateOf
31
- import androidx.compose.runtime.remember
34
+ import androidx.compose.runtime.saveable.rememberSaveable
32
35
import androidx.compose.runtime.setValue
33
36
import androidx.compose.ui.Alignment
34
37
import androidx.compose.ui.Modifier
35
38
import androidx.compose.ui.input.nestedscroll.nestedScroll
36
39
import androidx.compose.ui.tooling.preview.Preview
40
+ import androidx.compose.ui.unit.coerceAtLeast
37
41
import androidx.compose.ui.unit.dp
38
42
import androidx.lifecycle.viewmodel.compose.viewModel
39
43
import io.goooler.demoapp.common.util.getQuantityString
@@ -50,7 +54,6 @@ fun DetailPageWithSwipeRefresh(
50
54
) {
51
55
val model by vm.repoDetailModel.collectAsState()
52
56
val isRefreshing by vm.isRefreshing.collectAsState()
53
-
54
57
val refreshState = rememberPullToRefreshState()
55
58
56
59
if (refreshState.isRefreshing) {
@@ -63,29 +66,45 @@ fun DetailPageWithSwipeRefresh(
63
66
}
64
67
}
65
68
66
- Box (modifier.nestedScroll(refreshState.nestedScrollConnection)) {
67
- DetailPage (model = model, onForkClick = vm::fork)
69
+ Surface (
70
+ color = MaterialTheme .colorScheme.surface,
71
+ modifier = modifier.padding(horizontal = 10 .dp),
72
+ ) {
73
+ Box (Modifier .nestedScroll(refreshState.nestedScrollConnection)) {
74
+ LazyColumn {
75
+ val models = List (10 ) { model }
76
+ items(models) { model ->
77
+ DetailCard (model = model, onForkClick = vm::fork)
78
+ }
79
+ }
68
80
69
- PullToRefreshContainer (
70
- modifier = Modifier .align(Alignment .TopCenter ),
71
- state = refreshState,
72
- )
81
+ PullToRefreshContainer (
82
+ modifier = Modifier .align(Alignment .TopCenter ),
83
+ state = refreshState,
84
+ )
85
+ }
73
86
}
74
87
}
75
88
76
89
@Composable
77
- fun DetailPage (
90
+ fun DetailCard (
78
91
model : RepoDetailModel ,
79
92
modifier : Modifier = Modifier ,
80
93
onForkClick : () -> Unit ,
81
94
) {
82
- var isDescExpanded by remember { mutableStateOf(false ) }
95
+ var isDescExpanded by rememberSaveable { mutableStateOf(false ) }
96
+ val extraPadding by animateDpAsState(
97
+ targetValue = if (isDescExpanded) 20 .dp else 0 .dp,
98
+ label = " extraPadding" ,
99
+ animationSpec = spring(
100
+ dampingRatio = Spring .DampingRatioMediumBouncy ,
101
+ stiffness = Spring .StiffnessLow
102
+ )
103
+ )
83
104
84
105
Column (
85
106
modifier = modifier
86
- .padding(8 .dp)
87
- .fillMaxSize()
88
- .verticalScroll(rememberScrollState()),
107
+ .padding(extraPadding.coerceAtLeast(0 .dp)),
89
108
) {
90
109
Text (
91
110
text = model.fullName,
@@ -95,17 +114,20 @@ fun DetailPage(
95
114
Spacer (modifier = Modifier .height(5 .dp))
96
115
Text (
97
116
text = model.description,
98
- style = MaterialTheme .typography.bodyMedium ,
99
- maxLines = if (isDescExpanded) Int .MAX_VALUE else 2 ,
117
+ style = MaterialTheme .typography.bodyLarge ,
118
+ maxLines = if (isDescExpanded) Int .MAX_VALUE else 1 ,
100
119
modifier = Modifier .clickable {
101
120
isDescExpanded = ! isDescExpanded
102
121
},
103
122
)
104
123
Spacer (modifier = Modifier .height(5 .dp))
105
124
Row {
106
- Button (onClick = {
107
- R .plurals.detail_star_count_tip.getQuantityString(model.starsCount)?.showToast()
108
- }) {
125
+ Button (
126
+ modifier = Modifier .weight(1f ),
127
+ onClick = {
128
+ R .plurals.detail_star_count_tip.getQuantityString(model.starsCount)?.showToast()
129
+ },
130
+ ) {
109
131
Icon (
110
132
Icons .Filled .Star ,
111
133
contentDescription = " Star" ,
@@ -115,7 +137,10 @@ fun DetailPage(
115
137
Text (model.starsCount.toString())
116
138
}
117
139
Spacer (modifier = Modifier .width(20 .dp))
118
- Button (onClick = onForkClick) {
140
+ Button (
141
+ modifier = Modifier .weight(1f ),
142
+ onClick = onForkClick,
143
+ ) {
119
144
Icon (
120
145
Icons .Filled .Share ,
121
146
contentDescription = " Fork" ,
@@ -143,5 +168,5 @@ private fun DetailPagePreview() {
143
168
1 ,
144
169
2 ,
145
170
)
146
- DetailPage (model) {}
171
+ DetailCard (model) {}
147
172
}
0 commit comments