mirror of
https://github.com/wgtunnel/android.git
synced 2026-07-03 14:07:49 +02:00
refactor: animate bottom nav icons
This commit is contained in:
@@ -69,7 +69,7 @@ android {
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro",
|
||||
)
|
||||
signingConfig = signingConfigs.getByName(Constants.RELEASE)
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
resValue("string", "provider", "\"${Constants.APP_NAME}.provider\"")
|
||||
}
|
||||
|
||||
@@ -161,6 +161,7 @@ dependencies {
|
||||
implementation(libs.material)
|
||||
implementation(libs.androidx.storage)
|
||||
|
||||
implementation("androidx.compose.animation:animation-graphics:1.9.2")
|
||||
implementation("androidx.navigation3:navigation3-runtime:1.0.0-alpha10")
|
||||
implementation("androidx.navigation3:navigation3-ui:1.0.0-alpha10")
|
||||
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.common.animations
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
|
||||
@Composable
|
||||
fun AnimatedFloatIcon(
|
||||
inactiveIcon: ImageVector,
|
||||
activeIcon: ImageVector,
|
||||
isSelected: Boolean,
|
||||
modifier: Modifier,
|
||||
) {
|
||||
|
||||
val transition = updateTransition(isSelected, label = null)
|
||||
val scale by
|
||||
transition.animateFloat(transitionSpec = { tween(250, easing = FastOutSlowInEasing) }) {
|
||||
selected ->
|
||||
if (selected) 1.2f else 1f
|
||||
}
|
||||
|
||||
val rotation by
|
||||
animateFloatAsState(targetValue = 0f, animationSpec = tween(400, easing = LinearEasing))
|
||||
|
||||
Crossfade(targetState = isSelected, animationSpec = tween(250, easing = FastOutSlowInEasing)) {
|
||||
selected ->
|
||||
Icon(
|
||||
imageVector = if (selected) activeIcon else inactiveIcon,
|
||||
contentDescription = null,
|
||||
modifier = modifier.scale(scale).rotate(rotation),
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import androidx.compose.material.icons.filled.QuestionMark
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.outlined.Bolt
|
||||
import androidx.compose.material.icons.outlined.Home
|
||||
import androidx.compose.material.icons.outlined.QuestionMark
|
||||
import androidx.compose.material.icons.outlined.Settings
|
||||
import androidx.compose.material.icons.rounded.Bolt
|
||||
import androidx.compose.material.icons.rounded.Home
|
||||
@@ -80,13 +81,20 @@ sealed class Route : NavKey {
|
||||
enum class Tab(
|
||||
val startRoute: Route,
|
||||
val titleRes: Int,
|
||||
val icon: androidx.compose.ui.graphics.vector.ImageVector,
|
||||
val inactiveIcon: androidx.compose.ui.graphics.vector.ImageVector,
|
||||
val activeIcon: androidx.compose.ui.graphics.vector.ImageVector,
|
||||
val index: Int,
|
||||
) {
|
||||
TUNNELS(Route.Tunnels, R.string.tunnels, Icons.Rounded.Home, 0),
|
||||
AUTOTUNNEL(Route.AutoTunnel, R.string.auto_tunnel, Icons.Rounded.Bolt, 1),
|
||||
SETTINGS(Route.Settings, R.string.settings, Icons.Rounded.Settings, 2),
|
||||
SUPPORT(Route.Support, R.string.support, Icons.Rounded.QuestionMark, 3);
|
||||
TUNNELS(Route.Tunnels, R.string.tunnels, Icons.Outlined.Home, Icons.Filled.Home, 0),
|
||||
AUTOTUNNEL(Route.AutoTunnel, R.string.auto_tunnel, Icons.Outlined.Bolt, Icons.Filled.Bolt, 1),
|
||||
SETTINGS(Route.Settings, R.string.settings, Icons.Outlined.Settings, Icons.Filled.Settings, 2),
|
||||
SUPPORT(
|
||||
Route.Support,
|
||||
R.string.support,
|
||||
Icons.Outlined.QuestionMark,
|
||||
Icons.Filled.QuestionMark,
|
||||
3,
|
||||
);
|
||||
|
||||
companion object {
|
||||
fun fromRoute(route: Route): Tab =
|
||||
|
||||
+14
-7
@@ -1,5 +1,7 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.navigation.components
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -10,8 +12,8 @@ import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.zaneschepke.wireguardautotunnel.ui.common.animations.AnimatedFloatIcon
|
||||
import com.zaneschepke.wireguardautotunnel.ui.navigation.Tab
|
||||
import com.zaneschepke.wireguardautotunnel.ui.theme.SilverTree
|
||||
|
||||
@@ -28,6 +30,7 @@ fun BottomNavbar(isAutoTunnelActive: Boolean, currentTab: Tab, onTabSelected: (T
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
val isSelected = currentTab == tab
|
||||
val hasBadge = tab == Tab.AUTOTUNNEL && isAutoTunnelActive
|
||||
|
||||
IconButton(
|
||||
onClick = { onTabSelected(tab) },
|
||||
colors =
|
||||
@@ -54,15 +57,19 @@ fun BottomNavbar(isAutoTunnelActive: Boolean, currentTab: Tab, onTabSelected: (T
|
||||
)
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
imageVector = tab.icon,
|
||||
contentDescription = stringResource(tab.titleRes),
|
||||
AnimatedFloatIcon(
|
||||
activeIcon = tab.activeIcon,
|
||||
inactiveIcon = tab.inactiveIcon,
|
||||
isSelected = isSelected,
|
||||
modifier = Modifier.size(24.dp),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = tab.icon,
|
||||
contentDescription = stringResource(tab.titleRes),
|
||||
AnimatedFloatIcon(
|
||||
activeIcon = tab.activeIcon,
|
||||
inactiveIcon = tab.inactiveIcon,
|
||||
isSelected = isSelected,
|
||||
modifier = Modifier.size(24.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user