Skip to content

Commit e9584b7

Browse files
authored
example: Use CompositionLocal for PagerState and page change handling (#98)
1 parent 7ca2800 commit e9584b7

File tree

2 files changed

+58
-84
lines changed

2 files changed

+58
-84
lines changed

example/src/commonMain/kotlin/UITest.kt

Lines changed: 57 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ import androidx.compose.foundation.pager.HorizontalPager
3434
import androidx.compose.foundation.pager.PagerState
3535
import androidx.compose.foundation.pager.rememberPagerState
3636
import androidx.compose.runtime.Composable
37-
import androidx.compose.runtime.LaunchedEffect
37+
import androidx.compose.runtime.CompositionLocalProvider
3838
import androidx.compose.runtime.MutableState
39+
import androidx.compose.runtime.compositionLocalOf
3940
import androidx.compose.runtime.getValue
40-
import androidx.compose.runtime.mutableIntStateOf
4141
import androidx.compose.runtime.mutableStateOf
4242
import androidx.compose.runtime.remember
4343
import androidx.compose.runtime.rememberCoroutineScope
@@ -136,19 +136,17 @@ data class UIState(
136136
val isWideScreen: Boolean = false,
137137
)
138138

139+
val LocalPagerState = compositionLocalOf<PagerState> { error("No pager state") }
140+
val LocalHandlePageChange = compositionLocalOf<(Int) -> Unit> { error("No handle page change") }
141+
139142
@Composable
140143
fun UITest(
141144
colorMode: MutableState<Int>,
142145
) {
143146
val topAppBarScrollBehaviorList = List(UIConstants.PAGE_COUNT) { MiuixScrollBehavior() }
144147
val pagerState = rememberPagerState(pageCount = { UIConstants.PAGE_COUNT })
145148
val coroutineScope = rememberCoroutineScope()
146-
var selectedPage by remember { mutableIntStateOf(pagerState.currentPage) }
147-
val currentScrollBehavior = topAppBarScrollBehaviorList[selectedPage]
148-
149-
LaunchedEffect(pagerState.settledPage) {
150-
if (selectedPage != pagerState.settledPage) selectedPage = pagerState.settledPage
151-
}
149+
val currentScrollBehavior = topAppBarScrollBehaviorList[pagerState.currentPage]
152150

153151
val navigationItems = remember {
154152
listOf(
@@ -162,46 +160,44 @@ fun UITest(
162160
val showTopPopup = remember { mutableStateOf(false) }
163161
val windowSize by rememberUpdatedState(getWindowSize())
164162

165-
val onPageSelected: (Int) -> Unit = remember {
163+
val handlePageChange: (Int) -> Unit = remember(pagerState, coroutineScope) {
166164
{ page ->
167-
selectedPage = page
168165
coroutineScope.launch { pagerState.animateScrollToPage(page) }
169166
}
170167
}
171168

172-
BoxWithConstraints(
173-
modifier = Modifier.fillMaxSize()
169+
CompositionLocalProvider(
170+
LocalPagerState provides pagerState,
171+
LocalHandlePageChange provides handlePageChange
174172
) {
175-
val isWideScreen = maxWidth > UIConstants.WIDE_SCREEN_THRESHOLD
176-
uiState = uiState.copy(isWideScreen = isWideScreen)
173+
BoxWithConstraints(
174+
modifier = Modifier.fillMaxSize()
175+
) {
176+
val isWideScreen = maxWidth > UIConstants.WIDE_SCREEN_THRESHOLD
177+
uiState = uiState.copy(isWideScreen = isWideScreen)
177178

178-
if (isWideScreen) {
179-
WideScreenLayout(
180-
selectedPage = selectedPage,
181-
navigationItems = navigationItems,
182-
uiState = uiState,
183-
onUiStateChange = { uiState = it },
184-
onPageSelected = onPageSelected,
185-
showTopPopup = showTopPopup,
186-
pagerState = pagerState,
187-
topAppBarScrollBehaviorList = topAppBarScrollBehaviorList,
188-
currentScrollBehavior = currentScrollBehavior,
189-
windowSize = windowSize,
190-
colorMode = colorMode
191-
)
192-
} else {
193-
CompactScreenLayout(
194-
selectedPage = selectedPage,
195-
navigationItems = navigationItems,
196-
uiState = uiState,
197-
onUiStateChange = { uiState = it },
198-
onPageSelected = onPageSelected,
199-
showTopPopup = showTopPopup,
200-
pagerState = pagerState,
201-
topAppBarScrollBehaviorList = topAppBarScrollBehaviorList,
202-
currentScrollBehavior = currentScrollBehavior,
203-
colorMode = colorMode
204-
)
179+
if (isWideScreen) {
180+
WideScreenLayout(
181+
navigationItems = navigationItems,
182+
uiState = uiState,
183+
onUiStateChange = { uiState = it },
184+
showTopPopup = showTopPopup,
185+
topAppBarScrollBehaviorList = topAppBarScrollBehaviorList,
186+
currentScrollBehavior = currentScrollBehavior,
187+
windowSize = windowSize,
188+
colorMode = colorMode
189+
)
190+
} else {
191+
CompactScreenLayout(
192+
navigationItems = navigationItems,
193+
uiState = uiState,
194+
onUiStateChange = { uiState = it },
195+
showTopPopup = showTopPopup,
196+
topAppBarScrollBehaviorList = topAppBarScrollBehaviorList,
197+
currentScrollBehavior = currentScrollBehavior,
198+
colorMode = colorMode
199+
)
200+
}
205201
}
206202
}
207203

@@ -221,13 +217,10 @@ fun UITest(
221217

222218
@Composable
223219
private fun WideScreenLayout(
224-
selectedPage: Int,
225220
navigationItems: List<NavigationItem>,
226221
uiState: UIState,
227222
onUiStateChange: (UIState) -> Unit,
228-
onPageSelected: (Int) -> Unit,
229223
showTopPopup: MutableState<Boolean>,
230-
pagerState: PagerState,
231224
topAppBarScrollBehaviorList: List<ScrollBehavior>,
232225
currentScrollBehavior: ScrollBehavior,
233226
windowSize: WindowSize,
@@ -240,8 +233,6 @@ private fun WideScreenLayout(
240233
Row {
241234
Box(modifier = Modifier.weight(0.4f)) {
242235
WideScreenPanel(
243-
selectedPage = selectedPage,
244-
onPageSelected = onPageSelected,
245236
barScrollBehavior = barScrollBehavior,
246237
uiState = uiState,
247238
windowSize = windowSize,
@@ -253,12 +244,9 @@ private fun WideScreenLayout(
253244
)
254245
Box(modifier = Modifier.weight(0.6f)) {
255246
WideScreenContent(
256-
selectedPage = selectedPage,
257247
navigationItems = navigationItems,
258248
uiState = uiState,
259-
onPageSelected = onPageSelected,
260249
showTopPopup = showTopPopup,
261-
pagerState = pagerState,
262250
topAppBarScrollBehaviorList = topAppBarScrollBehaviorList,
263251
currentScrollBehavior = currentScrollBehavior,
264252
onUiStateChange = onUiStateChange,
@@ -271,13 +259,13 @@ private fun WideScreenLayout(
271259

272260
@Composable
273261
private fun WideScreenPanel(
274-
selectedPage: Int,
275-
onPageSelected: (Int) -> Unit,
276262
barScrollBehavior: ScrollBehavior,
277263
uiState: UIState,
278264
windowSize: WindowSize,
279265
layoutDirection: LayoutDirection
280266
) {
267+
val currentPage = LocalPagerState.current.currentPage
268+
val handlePageChange = LocalHandlePageChange.current
281269
Scaffold(
282270
modifier = Modifier
283271
.padding(start = 18.dp, end = 12.dp)
@@ -312,8 +300,8 @@ private fun WideScreenPanel(
312300
UIConstants.PAGE_TITLES.forEachIndexed { index, title ->
313301
BasicComponent(
314302
title = title,
315-
onClick = { onPageSelected(index) },
316-
holdDownState = selectedPage == index,
303+
onClick = { handlePageChange(index) },
304+
holdDownState = currentPage == index,
317305
)
318306
}
319307
}
@@ -324,12 +312,9 @@ private fun WideScreenPanel(
324312

325313
@Composable
326314
private fun WideScreenContent(
327-
selectedPage: Int,
328315
navigationItems: List<NavigationItem>,
329316
uiState: UIState,
330-
onPageSelected: (Int) -> Unit,
331317
showTopPopup: MutableState<Boolean>,
332-
pagerState: PagerState,
333318
topAppBarScrollBehaviorList: List<ScrollBehavior>,
334319
currentScrollBehavior: ScrollBehavior,
335320
onUiStateChange: (UIState) -> Unit,
@@ -346,14 +331,12 @@ private fun WideScreenContent(
346331
exit = fadeOut() + shrinkVertically()
347332
) {
348333
SmallTopAppBar(
349-
title = UIConstants.PAGE_TITLES[selectedPage],
334+
title = UIConstants.PAGE_TITLES[LocalPagerState.current.currentPage],
350335
scrollBehavior = currentScrollBehavior,
351336
actions = {
352337
TopAppBarActions(
353-
selectedPage = selectedPage,
354338
items = navigationItems,
355-
showTopPopup = showTopPopup,
356-
onPageSelected = onPageSelected
339+
showTopPopup = showTopPopup
357340
)
358341
},
359342
defaultWindowInsetsPadding = false,
@@ -381,7 +364,6 @@ private fun WideScreenContent(
381364
.imePadding()
382365
.windowInsetsPadding(WindowInsets.displayCutout.only(WindowInsetsSides.End))
383366
.windowInsetsPadding(WindowInsets.navigationBars.only(WindowInsetsSides.End)),
384-
pagerState = pagerState,
385367
topAppBarScrollBehaviorList = topAppBarScrollBehaviorList,
386368
padding = PaddingValues(
387369
end = padding.calculateEndPadding(LayoutDirection.Ltr),
@@ -397,13 +379,10 @@ private fun WideScreenContent(
397379

398380
@Composable
399381
private fun CompactScreenLayout(
400-
selectedPage: Int,
401382
navigationItems: List<NavigationItem>,
402383
uiState: UIState,
403384
onUiStateChange: (UIState) -> Unit,
404-
onPageSelected: (Int) -> Unit,
405385
showTopPopup: MutableState<Boolean>,
406-
pagerState: PagerState,
407386
topAppBarScrollBehaviorList: List<ScrollBehavior>,
408387
currentScrollBehavior: ScrollBehavior,
409388
colorMode: MutableState<Int>
@@ -421,10 +400,8 @@ private fun CompactScreenLayout(
421400
scrollBehavior = currentScrollBehavior,
422401
actions = {
423402
TopAppBarActions(
424-
selectedPage = selectedPage,
425403
items = navigationItems,
426-
showTopPopup = showTopPopup,
427-
onPageSelected = onPageSelected
404+
showTopPopup = showTopPopup
428405
)
429406
}
430407
)
@@ -434,8 +411,6 @@ private fun CompactScreenLayout(
434411
NavigationBar(
435412
uiState = uiState,
436413
navigationItems = navigationItems,
437-
selectedPage = selectedPage,
438-
onPageSelected = onPageSelected
439414
)
440415
},
441416
floatingActionButton = {
@@ -455,7 +430,6 @@ private fun CompactScreenLayout(
455430
.imePadding()
456431
.windowInsetsPadding(WindowInsets.displayCutout.only(WindowInsetsSides.Horizontal))
457432
.windowInsetsPadding(WindowInsets.navigationBars.only(WindowInsetsSides.Horizontal)),
458-
pagerState = pagerState,
459433
topAppBarScrollBehaviorList = topAppBarScrollBehaviorList,
460434
padding = padding,
461435
uiState = uiState,
@@ -469,9 +443,9 @@ private fun CompactScreenLayout(
469443
private fun NavigationBar(
470444
uiState: UIState,
471445
navigationItems: List<NavigationItem>,
472-
selectedPage: Int,
473-
onPageSelected: (Int) -> Unit
474446
) {
447+
val currentPage = LocalPagerState.current.currentPage
448+
val handlePageChange = LocalHandlePageChange.current
475449
AnimatedVisibility(
476450
visible = uiState.showNavigationBar,
477451
enter = fadeIn() + expandVertically(),
@@ -484,8 +458,8 @@ private fun NavigationBar(
484458
) {
485459
NavigationBar(
486460
items = navigationItems,
487-
selected = selectedPage,
488-
onClick = onPageSelected
461+
selected = currentPage,
462+
onClick = handlePageChange
489463
)
490464
}
491465
AnimatedVisibility(
@@ -495,10 +469,11 @@ private fun NavigationBar(
495469
) {
496470
FloatingNavigationBar(
497471
items = navigationItems,
498-
selected = selectedPage,
472+
selected = currentPage,
499473
mode = FloatingNavigationBarDisplayMode.fromInt(uiState.floatingNavigationBarMode).toMode(),
500-
horizontalAlignment = FloatingNavigationBarAlignment.fromInt(uiState.floatingNavigationBarPosition).toAlignment(),
501-
onClick = onPageSelected
474+
horizontalAlignment = FloatingNavigationBarAlignment.fromInt(uiState.floatingNavigationBarPosition)
475+
.toAlignment(),
476+
onClick = handlePageChange
502477
)
503478
}
504479
}
@@ -617,12 +592,12 @@ private fun FloatingNavigationBarAlignment.toAlignment(): Alignment.Horizontal =
617592

618593
@Composable
619594
private fun TopAppBarActions(
620-
selectedPage: Int,
621595
items: List<NavigationItem>,
622596
showTopPopup: MutableState<Boolean>,
623-
onPageSelected: (Int) -> Unit
624597
) {
625598
val hapticFeedback = LocalHapticFeedback.current
599+
val currentPage = LocalPagerState.current.currentPage
600+
val handlePageChange = LocalHandlePageChange.current
626601

627602
ListPopup(
628603
show = showTopPopup,
@@ -638,9 +613,9 @@ private fun TopAppBarActions(
638613
DropdownImpl(
639614
text = navigationItem.label,
640615
optionSize = items.size,
641-
isSelected = index == selectedPage,
616+
isSelected = index == currentPage,
642617
onSelectedIndexChange = {
643-
onPageSelected(index)
618+
handlePageChange(index)
644619
hapticFeedback.performHapticFeedback(HapticFeedbackType.Confirm)
645620
showTopPopup.value = false
646621
},
@@ -668,15 +643,14 @@ private fun TopAppBarActions(
668643
@Composable
669644
fun AppPager(
670645
modifier: Modifier = Modifier,
671-
pagerState: PagerState,
672646
topAppBarScrollBehaviorList: List<ScrollBehavior>,
673647
padding: PaddingValues,
674648
uiState: UIState,
675649
onUiStateChange: (UIState) -> Unit,
676650
colorMode: MutableState<Int>
677651
) {
678652
HorizontalPager(
679-
state = pagerState,
653+
state = LocalPagerState.current,
680654
modifier = modifier,
681655
userScrollEnabled = uiState.enablePageUserScroll,
682656
beyondViewportPageCount = 1,

iosApp/iosApp/Info.plist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
<key>CFBundleShortVersionString</key>
1818
<string>1.0.4</string>
1919
<key>CFBundleVersion</key>
20-
<string>503</string>
20+
<string>504</string>
2121
<key>LSRequiresIPhoneOS</key>
2222
<true/>
2323
<key>CADisableMinimumFrameDurationOnPhone</key>

0 commit comments

Comments
 (0)