Skip to content

Commit 7e6e631

Browse files
committed
library: Optimize some recomposition issues
1 parent 0df892c commit 7e6e631

File tree

8 files changed

+280
-269
lines changed

8 files changed

+280
-269
lines changed

example/build.gradle.kts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,6 @@ android {
150150
(this as BaseVariantOutputImpl).outputFileName = "$appName-v$versionName($versionCode)-$name.apk"
151151
}
152152
}
153-
resources.excludes += "**"
154153
}
155154
buildTypes {
156155
release {
@@ -166,6 +165,12 @@ android {
166165
}
167166
}
168167

168+
androidComponents {
169+
onVariants(selector().withBuildType("release")) {
170+
it.packaging.resources.excludes.add("**")
171+
}
172+
}
173+
169174
compose.desktop {
170175
application {
171176
mainClass = "Main_desktopKt"

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>484</string>
20+
<string>485</string>
2121
<key>LSRequiresIPhoneOS</key>
2222
<true/>
2323
<key>CADisableMinimumFrameDurationOnPhone</key>

miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/ListPopup.kt

Lines changed: 107 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -75,53 +75,66 @@ fun ListPopup(
7575
) {
7676
if (!show.value) return
7777

78-
val currentOnDismissRequest by rememberUpdatedState(onDismissRequest)
79-
val currentContent by rememberUpdatedState(content)
80-
81-
var offset by remember { mutableStateOf(IntOffset.Zero) }
8278
val density = LocalDensity.current
8379
val layoutDirection = LocalLayoutDirection.current
8480

8581
val getWindowSizeState = rememberUpdatedState(getWindowSize())
8682
var windowSize by remember { mutableStateOf(IntSize(getWindowSizeState.value.width, getWindowSizeState.value.height)) }
8783
var parentBounds by remember { mutableStateOf(IntRect.Zero) }
8884

89-
val windowBounds = with(density) {
90-
IntRect(
91-
left = WindowInsets.displayCutout.asPaddingValues(density).calculateLeftPadding(layoutDirection).roundToPx(),
92-
top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding().roundToPx(),
93-
right = windowSize.width -
94-
WindowInsets.displayCutout.asPaddingValues(density).calculateRightPadding(layoutDirection).roundToPx(),
95-
bottom = windowSize.height -
96-
WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding().roundToPx() -
97-
WindowInsets.captionBar.asPaddingValues().calculateBottomPadding().roundToPx()
98-
)
99-
}
100-
101-
var popupContentSize by remember { mutableStateOf(IntSize.Zero) }
85+
Layout(
86+
modifier = Modifier.onGloballyPositioned { childCoordinates ->
87+
childCoordinates.parentLayoutCoordinates?.let { parentLayoutCoordinates ->
88+
val positionInWindow = parentLayoutCoordinates.positionInWindow()
89+
val newParentBounds = IntRect(
90+
left = positionInWindow.x.toInt(),
91+
top = positionInWindow.y.toInt(),
92+
right = positionInWindow.x.toInt() + parentLayoutCoordinates.size.width,
93+
bottom = positionInWindow.y.toInt() + parentLayoutCoordinates.size.height
94+
)
95+
if (parentBounds != newParentBounds) parentBounds = newParentBounds
96+
}
97+
val newUtilWindowSize = getWindowSizeState.value
98+
val newIntSize = IntSize(newUtilWindowSize.width, newUtilWindowSize.height)
99+
if (windowSize != newIntSize) windowSize = newIntSize
100+
}
101+
) { _, _ -> layout(0, 0) {} }
102102

103-
val popupMargin = remember(popupPositionProvider, density, layoutDirection) {
104-
val popupMarginsPd = popupPositionProvider.getMargins()
103+
val popupMargin = remember(popupPositionProvider, layoutDirection, density) {
104+
val pd = popupPositionProvider.getMargins()
105105
with(density) {
106106
IntRect(
107-
left = popupMarginsPd.calculateLeftPadding(layoutDirection).roundToPx(),
108-
top = popupMarginsPd.calculateTopPadding().roundToPx(),
109-
right = popupMarginsPd.calculateRightPadding(layoutDirection).roundToPx(),
110-
bottom = popupMarginsPd.calculateBottomPadding().roundToPx()
107+
left = pd.calculateLeftPadding(layoutDirection).roundToPx(),
108+
top = pd.calculateTopPadding().roundToPx(),
109+
right = pd.calculateRightPadding(layoutDirection).roundToPx(),
110+
bottom = pd.calculateBottomPadding().roundToPx()
111111
)
112112
}
113113
}
114114

115-
val transformOrigin = remember(parentBounds, popupMargin, windowSize, density, alignment) {
116-
val xInWindow = if (alignment in listOf(
117-
PopupPositionProvider.Align.Right,
118-
PopupPositionProvider.Align.TopRight,
119-
PopupPositionProvider.Align.BottomRight,
120-
)
121-
) {
122-
parentBounds.right - popupMargin.right - with(density) { 64.dp.roundToPx() }
123-
} else {
124-
parentBounds.left + popupMargin.left + with(density) { 64.dp.roundToPx() }
115+
val displayCutoutPadding = WindowInsets.displayCutout.asPaddingValues(density)
116+
val statusBarsPadding = WindowInsets.statusBars.asPaddingValues()
117+
val navigationBarsPadding = WindowInsets.navigationBars.asPaddingValues()
118+
val captionBarPadding = WindowInsets.captionBar.asPaddingValues()
119+
120+
val windowBounds = with(density) {
121+
IntRect(
122+
left = displayCutoutPadding.calculateLeftPadding(layoutDirection).roundToPx(),
123+
top = statusBarsPadding.calculateTopPadding().roundToPx(),
124+
right = windowSize.width - displayCutoutPadding.calculateRightPadding(layoutDirection).roundToPx(),
125+
bottom = windowSize.height -
126+
navigationBarsPadding.calculateBottomPadding().roundToPx() -
127+
captionBarPadding.calculateBottomPadding().roundToPx()
128+
)
129+
}
130+
131+
val transformOrigin = remember(parentBounds, popupMargin, windowSize, alignment, density) {
132+
val xInWindow = when (alignment) {
133+
PopupPositionProvider.Align.Right,
134+
PopupPositionProvider.Align.TopRight,
135+
PopupPositionProvider.Align.BottomRight -> parentBounds.right - popupMargin.right - with(density) { 64.dp.roundToPx() }
136+
137+
else -> parentBounds.left + popupMargin.left + with(density) { 64.dp.roundToPx() }
125138
}
126139
val yInWindow = parentBounds.top + parentBounds.height / 2 - with(density) { 56.dp.roundToPx() }
127140
safeTransformOrigin(
@@ -130,96 +143,78 @@ fun ListPopup(
130143
)
131144
}
132145

133-
// Anchor point for the popup
134-
Layout(
135-
modifier = Modifier.onGloballyPositioned { childCoordinates ->
136-
val parentLayoutCoordinates = childCoordinates.parentLayoutCoordinates
137-
if (parentLayoutCoordinates != null) {
138-
val positionInWindow = parentLayoutCoordinates.positionInWindow()
139-
parentBounds = IntRect(
140-
left = positionInWindow.x.toInt(),
141-
top = positionInWindow.y.toInt(),
142-
right = positionInWindow.x.toInt() + parentLayoutCoordinates.size.width,
143-
bottom = positionInWindow.y.toInt() + parentLayoutCoordinates.size.height
144-
)
145-
}
146-
val newUtilWindowSize = getWindowSizeState.value
147-
val newIntSize = IntSize(newUtilWindowSize.width, newUtilWindowSize.height)
148-
if (windowSize != newIntSize) {
149-
windowSize = newIntSize
150-
}
151-
}
152-
) { _, _ ->
153-
layout(0, 0) {}
154-
}
146+
var offset by remember { mutableStateOf(IntOffset.Zero) }
147+
var popupContentSize by remember { mutableStateOf(IntSize.Zero) }
155148

156-
PopupLayout(
157-
visible = show,
158-
enableWindowDim = enableWindowDim,
159-
transformOrigin = { transformOrigin },
160-
) {
161-
val shape = remember { SmoothRoundedCornerShape(16.dp) }
162-
val elevationPx = with(density) { shadowElevation.toPx() }
163-
164-
Box(
165-
modifier = popupModifier
166-
.pointerInput(currentOnDismissRequest) {
167-
detectTapGestures {
168-
currentOnDismissRequest?.invoke()
169-
}
170-
}
171-
.layout { measurable, constraints ->
172-
val placeable = measurable.measure(
173-
constraints.copy(
174-
minWidth = if (minWidth.roundToPx() <= windowSize.width) minWidth.roundToPx() else windowSize.width,
175-
minHeight = if (50.dp.roundToPx() <= windowSize.height) 50.dp.roundToPx() else windowSize.height,
176-
maxHeight = maxHeight?.roundToPx()?.coerceAtLeast(50.dp.roundToPx())
177-
?: (windowBounds.height - popupMargin.top - popupMargin.bottom).coerceAtLeast(
178-
50.dp.roundToPx()
179-
),
180-
maxWidth = if (minWidth.roundToPx() <= windowSize.width) windowSize.width else minWidth.roundToPx()
181-
)
182-
)
183-
val measuredSize = IntSize(placeable.width, placeable.height)
184-
if (popupContentSize != measuredSize) {
185-
popupContentSize = measuredSize
186-
}
149+
if (parentBounds != IntRect.Zero && windowSize != IntSize.Zero) {
150+
PopupLayout(
151+
visible = show,
152+
enableWindowDim = enableWindowDim,
153+
transformOrigin = { transformOrigin },
154+
) {
155+
val shape = remember { SmoothRoundedCornerShape(16.dp) }
156+
val elevationPx = with(density) { shadowElevation.toPx() }
187157

188-
val calculatedOffset = popupPositionProvider.calculatePosition(
189-
parentBounds,
190-
windowBounds,
191-
layoutDirection,
192-
measuredSize,
193-
popupMargin,
194-
alignment
195-
)
196-
if (offset != calculatedOffset) {
197-
offset = calculatedOffset
158+
Box(
159+
modifier = popupModifier
160+
.pointerInput(onDismissRequest) {
161+
detectTapGestures(
162+
onTap = { onDismissRequest?.invoke() }
163+
)
198164
}
165+
.layout { measurable, constraints ->
166+
val placeable = measurable.measure(
167+
constraints.copy(
168+
minWidth = if (minWidth.roundToPx() <= windowSize.width) minWidth.roundToPx() else windowSize.width,
169+
minHeight = if (50.dp.roundToPx() <= windowSize.height) 50.dp.roundToPx() else windowSize.height,
170+
maxHeight = maxHeight?.roundToPx()?.coerceAtLeast(50.dp.roundToPx())
171+
?: (windowBounds.height - popupMargin.top - popupMargin.bottom).coerceAtLeast(
172+
50.dp.roundToPx()
173+
),
174+
maxWidth = if (minWidth.roundToPx() <= windowSize.width) windowSize.width else minWidth.roundToPx()
175+
)
176+
)
177+
val measuredSize = IntSize(placeable.width, placeable.height)
178+
if (popupContentSize != measuredSize) {
179+
popupContentSize = measuredSize
180+
}
181+
182+
val calculatedOffset = popupPositionProvider.calculatePosition(
183+
parentBounds,
184+
windowBounds,
185+
layoutDirection,
186+
measuredSize,
187+
popupMargin,
188+
alignment
189+
)
190+
if (offset != calculatedOffset) {
191+
offset = calculatedOffset
192+
}
199193

200-
layout(constraints.maxWidth, constraints.maxHeight) {
201-
placeable.place(offset)
194+
layout(constraints.maxWidth, constraints.maxHeight) {
195+
placeable.place(offset)
196+
}
202197
}
203-
}
204-
) {
205-
Box(
206-
modifier = Modifier
207-
.graphicsLayer(
208-
clip = true,
209-
shape = shape,
210-
shadowElevation = elevationPx,
211-
ambientShadowColor = MiuixTheme.colorScheme.windowDimming,
212-
spotShadowColor = MiuixTheme.colorScheme.windowDimming
213-
)
214-
.background(MiuixTheme.colorScheme.surface)
215198
) {
216-
currentContent()
199+
Box(
200+
modifier = Modifier
201+
.graphicsLayer(
202+
clip = true,
203+
shape = shape,
204+
shadowElevation = elevationPx,
205+
ambientShadowColor = MiuixTheme.colorScheme.windowDimming,
206+
spotShadowColor = MiuixTheme.colorScheme.windowDimming
207+
)
208+
.background(MiuixTheme.colorScheme.surface)
209+
) {
210+
content()
211+
}
217212
}
218213
}
219214
}
220215

221216
BackHandler(enabled = show.value) {
222-
currentOnDismissRequest?.invoke()
217+
onDismissRequest?.invoke()
223218
}
224219
}
225220

miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/NavigationBar.kt

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import androidx.compose.foundation.layout.size
2727
import androidx.compose.foundation.layout.statusBars
2828
import androidx.compose.foundation.layout.windowInsetsPadding
2929
import androidx.compose.runtime.Composable
30+
import androidx.compose.runtime.derivedStateOf
3031
import androidx.compose.runtime.getValue
3132
import androidx.compose.runtime.mutableStateOf
3233
import androidx.compose.runtime.remember
@@ -104,25 +105,26 @@ fun NavigationBar(
104105
val itemWeight = 1f / items.size
105106

106107
items.forEachIndexed { index, item ->
107-
val isSelected = selected == index
108+
val isSelected = remember(selected) { selected == index }
108109
var isPressed by remember { mutableStateOf(false) }
109110

110111
val onSurfaceContainerColor = MiuixTheme.colorScheme.onSurfaceContainer
111112
val onSurfaceContainerVariantColor = MiuixTheme.colorScheme.onSurfaceContainerVariant
112113

113-
val tint by animateColorAsState(
114-
targetValue = when {
115-
isPressed -> if (isSelected) {
116-
onSurfaceContainerColor.copy(alpha = 0.6f)
117-
} else {
118-
onSurfaceContainerVariantColor.copy(alpha = 0.6f)
119-
}
114+
val tint by remember(isSelected, isPressed) {
115+
derivedStateOf {
116+
when {
117+
isPressed -> if (isSelected) {
118+
onSurfaceContainerColor.copy(alpha = 0.6f)
119+
} else {
120+
onSurfaceContainerVariantColor.copy(alpha = 0.6f)
121+
}
120122

121-
isSelected -> onSurfaceContainerColor
122-
else -> onSurfaceContainerVariantColor
123-
},
124-
label = "tintAnimation"
125-
)
123+
isSelected -> onSurfaceContainerColor
124+
else -> onSurfaceContainerVariantColor
125+
}
126+
}
127+
}
126128
val fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Normal
127129

128130
Column(
@@ -132,9 +134,7 @@ fun NavigationBar(
132134
.pointerInput(currentOnClick, index) {
133135
detectTapGestures(
134136
onPress = {
135-
isPressed = true
136137
tryAwaitRelease()
137-
isPressed = false
138138
},
139139
onTap = { currentOnClick(index) }
140140
)
@@ -269,7 +269,7 @@ fun FloatingNavigationBar(
269269
verticalAlignment = Alignment.CenterVertically
270270
) {
271271
items.forEachIndexed { index, item ->
272-
val isSelected = selected == index
272+
val isSelected = remember(selected) { selected == index }
273273
var isPressed by remember { mutableStateOf(false) }
274274

275275
val onSurfaceContainerColor = MiuixTheme.colorScheme.onSurfaceContainer

0 commit comments

Comments
 (0)