diff --git a/app/src/fdroid/kotlin/org/meshtastic/app/map/MapView.kt b/app/src/fdroid/kotlin/org/meshtastic/app/map/MapView.kt
index 54935b422..657f7ab74 100644
--- a/app/src/fdroid/kotlin/org/meshtastic/app/map/MapView.kt
+++ b/app/src/fdroid/kotlin/org/meshtastic/app/map/MapView.kt
@@ -77,8 +77,6 @@ import org.meshtastic.app.map.cluster.RadiusMarkerClusterer
import org.meshtastic.app.map.component.CacheLayout
import org.meshtastic.app.map.component.DownloadButton
import org.meshtastic.app.map.component.EditWaypointDialog
-import org.meshtastic.app.map.component.MapButton
-import org.meshtastic.app.map.component.MapControlsOverlay
import org.meshtastic.app.map.model.CustomTileSource
import org.meshtastic.app.map.model.MarkerWithLabel
import org.meshtastic.core.common.gpsDisabled
@@ -130,6 +128,8 @@ import org.meshtastic.core.ui.util.formatAgo
import org.meshtastic.core.ui.util.showToast
import org.meshtastic.feature.map.BaseMapViewModel.MapFilterState
import org.meshtastic.feature.map.LastHeardFilter
+import org.meshtastic.feature.map.component.MapButton
+import org.meshtastic.feature.map.component.MapControlsOverlay
import org.meshtastic.proto.Config.DisplayConfig.DisplayUnits
import org.meshtastic.proto.Waypoint
import org.osmdroid.bonuspack.utils.BonusPackHelper.getBitmapFromVectorDrawable
diff --git a/app/src/fdroid/kotlin/org/meshtastic/app/map/node/NodeTrackOsmMap.kt b/app/src/fdroid/kotlin/org/meshtastic/app/map/node/NodeTrackOsmMap.kt
index b24e57b63..a6aec4c2d 100644
--- a/app/src/fdroid/kotlin/org/meshtastic/app/map/node/NodeTrackOsmMap.kt
+++ b/app/src/fdroid/kotlin/org/meshtastic/app/map/node/NodeTrackOsmMap.kt
@@ -42,7 +42,6 @@ import org.meshtastic.app.map.addCopyright
import org.meshtastic.app.map.addPolyline
import org.meshtastic.app.map.addPositionMarkers
import org.meshtastic.app.map.addScaleBarOverlay
-import org.meshtastic.app.map.component.MapControlsOverlay
import org.meshtastic.app.map.model.CustomTileSource
import org.meshtastic.app.map.rememberMapViewWithLifecycle
import org.meshtastic.core.common.util.nowSeconds
@@ -50,6 +49,7 @@ import org.meshtastic.core.model.util.GeoConstants.DEG_D
import org.meshtastic.core.resources.Res
import org.meshtastic.core.resources.last_heard_filter_label
import org.meshtastic.feature.map.LastHeardFilter
+import org.meshtastic.feature.map.component.MapControlsOverlay
import org.meshtastic.proto.Position
import org.osmdroid.util.BoundingBox
import org.osmdroid.util.GeoPoint
@@ -61,8 +61,8 @@ import kotlin.math.roundToInt
*
* Applies the [lastHeardTrackFilter][org.meshtastic.feature.map.BaseMapViewModel.MapFilterState.lastHeardTrackFilter]
* from [MapViewModel] to filter positions by time, matching the behavior of the Google Maps implementation. Includes a
- * minimal [MapControlsOverlay][org.meshtastic.app.map.component.MapControlsOverlay] with a track time filter slider so
- * users can adjust the time range directly from the map.
+ * minimal [MapControlsOverlay][org.meshtastic.feature.map.component.MapControlsOverlay] with a track time filter slider
+ * so users can adjust the time range directly from the map.
*
* Supports optional synchronized selection via [selectedPositionTime] and [onPositionSelected].
*
diff --git a/app/src/google/kotlin/org/meshtastic/app/map/MapView.kt b/app/src/google/kotlin/org/meshtastic/app/map/MapView.kt
index 125f861cc..c8f2f3fee 100644
--- a/app/src/google/kotlin/org/meshtastic/app/map/MapView.kt
+++ b/app/src/google/kotlin/org/meshtastic/app/map/MapView.kt
@@ -97,8 +97,6 @@ import org.meshtastic.app.map.component.ClusterItemsListDialog
import org.meshtastic.app.map.component.CustomMapLayersSheet
import org.meshtastic.app.map.component.CustomTileProviderManagerSheet
import org.meshtastic.app.map.component.EditWaypointDialog
-import org.meshtastic.app.map.component.MapButton
-import org.meshtastic.app.map.component.MapControlsOverlay
import org.meshtastic.app.map.component.MapFilterDropdown
import org.meshtastic.app.map.component.MapTypeDropdown
import org.meshtastic.app.map.component.NodeClusterMarkers
@@ -137,6 +135,8 @@ import org.meshtastic.core.ui.util.formatAgo
import org.meshtastic.core.ui.util.formatPositionTime
import org.meshtastic.feature.map.BaseMapViewModel.MapFilterState
import org.meshtastic.feature.map.LastHeardFilter
+import org.meshtastic.feature.map.component.MapButton
+import org.meshtastic.feature.map.component.MapControlsOverlay
import org.meshtastic.feature.map.tracerouteNodeSelection
import org.meshtastic.proto.Config.DisplayConfig.DisplayUnits
import org.meshtastic.proto.Position
diff --git a/app/src/google/kotlin/org/meshtastic/app/map/component/CustomMapLayersSheet.kt b/app/src/google/kotlin/org/meshtastic/app/map/component/CustomMapLayersSheet.kt
index fb5f682ed..fd9272579 100644
--- a/app/src/google/kotlin/org/meshtastic/app/map/component/CustomMapLayersSheet.kt
+++ b/app/src/google/kotlin/org/meshtastic/app/map/component/CustomMapLayersSheet.kt
@@ -31,6 +31,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
+import androidx.compose.material3.IconToggleButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
@@ -125,7 +126,10 @@ fun CustomMapLayersSheet(
}
}
}
- IconButton(onClick = { onToggleVisibility(layer.id) }) {
+ IconToggleButton(
+ checked = layer.isVisible,
+ onCheckedChange = { onToggleVisibility(layer.id) },
+ ) {
Icon(
imageVector =
if (layer.isVisible) {
diff --git a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/EditPasswordPreference.kt b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/EditPasswordPreference.kt
index 681952e61..2dce97aa5 100644
--- a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/EditPasswordPreference.kt
+++ b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/EditPasswordPreference.kt
@@ -19,7 +19,7 @@ package org.meshtastic.core.ui.component
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Icon
-import androidx.compose.material3.IconButton
+import androidx.compose.material3.IconToggleButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -36,6 +36,7 @@ import org.meshtastic.core.resources.Res
import org.meshtastic.core.resources.hide_password
import org.meshtastic.core.resources.show_password
import org.meshtastic.core.ui.icon.MeshtasticIcons
+import org.meshtastic.core.ui.icon.Visibility
import org.meshtastic.core.ui.icon.VisibilityOff
@Composable
@@ -63,10 +64,9 @@ fun EditPasswordPreference(
onFocusChanged = {},
visualTransformation = if (isPasswordVisible) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = {
- IconButton(onClick = { isPasswordVisible = !isPasswordVisible }) {
+ IconToggleButton(checked = isPasswordVisible, onCheckedChange = { isPasswordVisible = it }) {
Icon(
- imageVector =
- if (isPasswordVisible) MeshtasticIcons.VisibilityOff else MeshtasticIcons.VisibilityOff,
+ imageVector = if (isPasswordVisible) MeshtasticIcons.VisibilityOff else MeshtasticIcons.Visibility,
contentDescription =
if (isPasswordVisible) {
stringResource(Res.string.hide_password)
diff --git a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/PreferenceFooter.kt b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/PreferenceFooter.kt
index 37e354d32..6bf0065bf 100644
--- a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/PreferenceFooter.kt
+++ b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/PreferenceFooter.kt
@@ -23,6 +23,7 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ElevatedButton
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -43,22 +44,28 @@ fun PreferenceFooter(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+ val mediumHeight = ButtonDefaults.MediumContainerHeight
if (negativeText != null) {
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
ElevatedButton(
- modifier = Modifier.height(48.dp).weight(1f),
+ shapes = ButtonDefaults.shapesFor(mediumHeight),
+ modifier = Modifier.height(mediumHeight).weight(1f),
colors = ButtonDefaults.filledTonalButtonColors(),
onClick = onNegativeClicked,
) {
- Text(text = negativeText)
+ Text(text = negativeText, style = ButtonDefaults.textStyleFor(mediumHeight))
}
}
if (positiveText != null) {
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
ElevatedButton(
- modifier = Modifier.height(48.dp).weight(1f),
+ shapes = ButtonDefaults.shapesFor(mediumHeight),
+ modifier = Modifier.height(mediumHeight).weight(1f),
colors = ButtonDefaults.buttonColors(),
onClick = { if (enabled) onPositiveClicked() },
) {
- Text(text = positiveText)
+ Text(text = positiveText, style = ButtonDefaults.textStyleFor(mediumHeight))
}
}
}
diff --git a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/qr/ScannedQrCodeDialog.kt b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/qr/ScannedQrCodeDialog.kt
index 632c8abb4..d5f4e31ec 100644
--- a/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/qr/ScannedQrCodeDialog.kt
+++ b/core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/qr/ScannedQrCodeDialog.kt
@@ -29,6 +29,7 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Surface
@@ -240,21 +241,33 @@ fun ScannedQrCodeDialog(
val unselectedColors =
ButtonDefaults.outlinedButtonColors(contentColor = MaterialTheme.colorScheme.onSurface)
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+ val mediumHeight = ButtonDefaults.MediumContainerHeight
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
OutlinedButton(
onClick = { shouldReplace = false },
- modifier = Modifier.height(48.dp).weight(1f),
+ shapes = ButtonDefaults.shapesFor(mediumHeight),
+ modifier = Modifier.height(mediumHeight).weight(1f),
colors = if (!shouldReplace) selectedColors else unselectedColors,
) {
- Text(text = stringResource(Res.string.add))
+ Text(
+ text = stringResource(Res.string.add),
+ style = ButtonDefaults.textStyleFor(mediumHeight),
+ )
}
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
OutlinedButton(
onClick = { shouldReplace = true },
- modifier = Modifier.height(48.dp).weight(1f),
+ shapes = ButtonDefaults.shapesFor(mediumHeight),
+ modifier = Modifier.height(mediumHeight).weight(1f),
enabled = incoming.lora_config != null,
colors = if (shouldReplace) selectedColors else unselectedColors,
) {
- Text(text = stringResource(Res.string.replace))
+ Text(
+ text = stringResource(Res.string.replace),
+ style = ButtonDefaults.textStyleFor(mediumHeight),
+ )
}
}
}
diff --git a/feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/ui/components/ConnectingDeviceInfo.kt b/feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/ui/components/ConnectingDeviceInfo.kt
index 9907e01c0..9c86a17bf 100644
--- a/feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/ui/components/ConnectingDeviceInfo.kt
+++ b/feature/connections/src/commonMain/kotlin/org/meshtastic/feature/connections/ui/components/ConnectingDeviceInfo.kt
@@ -26,6 +26,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -74,17 +75,20 @@ fun ConnectingDeviceInfo(
}
}
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+ val largeHeight = ButtonDefaults.LargeContainerHeight
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
Button(
- modifier = Modifier.fillMaxWidth().height(56.dp),
- shape = MaterialTheme.shapes.medium,
+ onClick = onClickDisconnect,
+ shapes = ButtonDefaults.shapesFor(largeHeight),
+ modifier = Modifier.fillMaxWidth().height(largeHeight),
colors =
ButtonDefaults.buttonColors(
containerColor = MaterialTheme.colorScheme.StatusRed,
contentColor = Color.White,
),
- onClick = onClickDisconnect,
) {
- Text(stringResource(Res.string.disconnect), style = MaterialTheme.typography.titleMedium)
+ Text(stringResource(Res.string.disconnect), style = ButtonDefaults.textStyleFor(largeHeight))
}
}
}
diff --git a/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateScreen.kt b/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateScreen.kt
index 0a051fa9c..eee6637af 100644
--- a/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateScreen.kt
+++ b/feature/firmware/src/commonMain/kotlin/org/meshtastic/feature/firmware/FirmwareUpdateScreen.kt
@@ -35,15 +35,18 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.CircularWavyProgressIndicator
import androidx.compose.material3.ElevatedCard
import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
-import androidx.compose.material3.LinearProgressIndicator
+import androidx.compose.material3.LinearWavyProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
@@ -381,24 +384,35 @@ private fun ReadyState(
Spacer(Modifier.height(16.dp))
if (selectedReleaseType == FirmwareReleaseType.LOCAL) {
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+ val largeHeight = ButtonDefaults.LargeContainerHeight
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
Button(
onClick = {
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
showDisclaimer = true
},
- modifier = Modifier.fillMaxWidth().height(56.dp),
+ shapes = ButtonDefaults.shapesFor(largeHeight),
+ modifier = Modifier.fillMaxWidth().height(largeHeight),
) {
Icon(MeshtasticIcons.Folder, contentDescription = null)
Spacer(Modifier.width(8.dp))
- Text(stringResource(Res.string.firmware_update_select_file))
+ Text(
+ stringResource(Res.string.firmware_update_select_file),
+ style = ButtonDefaults.textStyleFor(largeHeight),
+ )
}
} else if (state.release != null) {
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+ val largeHeight = ButtonDefaults.LargeContainerHeight
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
Button(
onClick = {
haptic.performHapticFeedback(HapticFeedbackType.LongPress)
showDisclaimer = true
},
- modifier = Modifier.fillMaxWidth().height(56.dp),
+ shapes = ButtonDefaults.shapesFor(largeHeight),
+ modifier = Modifier.fillMaxWidth().height(largeHeight),
) {
Icon(
imageVector =
@@ -416,6 +430,7 @@ private fun ReadyState(
resource = Res.string.firmware_update_method_detail,
stringResource(state.updateMethod.description),
),
+ style = ButtonDefaults.textStyleFor(largeHeight),
)
}
Spacer(Modifier.height(24.dp))
@@ -680,7 +695,8 @@ private fun ProgressContent(
tint = MaterialTheme.colorScheme.primary,
)
} else {
- CircularProgressIndicator(
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+ CircularWavyProgressIndicator(
progress = { if (isUpdating) progressState.progress else 1f },
modifier = Modifier.size(64.dp),
)
@@ -708,7 +724,8 @@ private fun ProgressContent(
Spacer(Modifier.height(12.dp))
if (isDownloading || isUpdating) {
- LinearProgressIndicator(
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+ LinearWavyProgressIndicator(
progress = { progressState.progress },
modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp),
)
@@ -850,8 +867,15 @@ private fun SuccessState(onDone: () -> Unit) {
textAlign = TextAlign.Center,
)
Spacer(Modifier.height(32.dp))
- Button(onClick = onDone, modifier = Modifier.fillMaxWidth().height(56.dp)) {
- Text(stringResource(Res.string.firmware_update_done))
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+ val largeHeight = ButtonDefaults.LargeContainerHeight
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+ Button(
+ onClick = onDone,
+ shapes = ButtonDefaults.shapesFor(largeHeight),
+ modifier = Modifier.fillMaxWidth().height(largeHeight),
+ ) {
+ Text(stringResource(Res.string.firmware_update_done), style = ButtonDefaults.textStyleFor(largeHeight))
}
}
}
diff --git a/app/src/main/kotlin/org/meshtastic/app/map/component/MapButton.kt b/feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/component/MapButton.kt
similarity index 97%
rename from app/src/main/kotlin/org/meshtastic/app/map/component/MapButton.kt
rename to feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/component/MapButton.kt
index 997d7d08b..a8bce5529 100644
--- a/app/src/main/kotlin/org/meshtastic/app/map/component/MapButton.kt
+++ b/feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/component/MapButton.kt
@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.meshtastic.app.map.component
+package org.meshtastic.feature.map.component
import androidx.compose.material3.FilledIconButton
import androidx.compose.material3.Icon
diff --git a/app/src/main/kotlin/org/meshtastic/app/map/component/MapControlsOverlay.kt b/feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/component/MapControlsOverlay.kt
similarity index 87%
rename from app/src/main/kotlin/org/meshtastic/app/map/component/MapControlsOverlay.kt
rename to feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/component/MapControlsOverlay.kt
index 74f08e07f..431354e6d 100644
--- a/app/src/main/kotlin/org/meshtastic/app/map/component/MapControlsOverlay.kt
+++ b/feature/map/src/commonMain/kotlin/org/meshtastic/feature/map/component/MapControlsOverlay.kt
@@ -14,13 +14,15 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package org.meshtastic.app.map.component
+package org.meshtastic.feature.map.component
import androidx.compose.foundation.layout.Box
-import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.CircularProgressIndicator
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
+import androidx.compose.material3.FloatingToolbarDefaults
+import androidx.compose.material3.HorizontalFloatingToolbar
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -41,8 +43,9 @@ import org.meshtastic.core.ui.icon.Tune
import org.meshtastic.core.ui.theme.StatusColors.StatusRed
/**
- * Shared map controls overlay used by both Google and F-Droid map views. Provides compass, filter button, location
- * tracking button, and optional slots for flavor-specific content (map type selector, layers, refresh).
+ * Shared map controls overlay using [HorizontalFloatingToolbar] for Material 3 Expressive styling. Provides compass,
+ * filter button, location tracking button, and optional slots for flavor-specific content (map type selector, layers,
+ * refresh).
*
* @param onToggleFilterMenu Callback to open/close the filter dropdown.
* @param filterDropdownContent Composable rendered inside a [Box] alongside the filter button — typically a
@@ -54,6 +57,7 @@ import org.meshtastic.core.ui.theme.StatusColors.StatusRed
* @param isRefreshing Whether a refresh is currently in progress.
* @param onRefresh Callback when the refresh button is clicked.
*/
+@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Suppress("LongParameterList")
@Composable
fun MapControlsOverlay(
@@ -71,7 +75,11 @@ fun MapControlsOverlay(
isRefreshing: Boolean = false,
onRefresh: () -> Unit = {},
) {
- Row(modifier = modifier) {
+ HorizontalFloatingToolbar(
+ expanded = true,
+ modifier = modifier,
+ colors = FloatingToolbarDefaults.standardFloatingToolbarColors(),
+ ) {
// Compass
CompassButton(onClick = onCompassClick, bearing = bearing, isFollowing = followPhoneBearing)
diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/component/NodeItem.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/component/NodeItem.kt
index cbf99e9ca..514be15e7 100644
--- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/component/NodeItem.kt
+++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/component/NodeItem.kt
@@ -31,7 +31,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
-import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -96,7 +95,6 @@ private const val ACTIVE_ALPHA = 0.5f
private const val INACTIVE_ALPHA = 0.2f
private const val GRID_COLUMNS = 3
-@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
@Suppress("LongMethod")
fun NodeItem(
@@ -391,7 +389,6 @@ private fun MetricsGrid(items: List<@Composable () -> Unit>) {
}
}
-@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun NodeItemHeader(
thatNode: Node,
diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/BaseMetricChart.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/BaseMetricChart.kt
index b8e6f0aae..8f65bf6d8 100644
--- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/BaseMetricChart.kt
+++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/BaseMetricChart.kt
@@ -31,6 +31,7 @@ import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
+import androidx.compose.material3.IconToggleButton
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -273,7 +274,7 @@ fun BaseMetricScreen(
)
}
}
- IconButton(onClick = { isChartExpanded = !isChartExpanded }) {
+ IconToggleButton(checked = isChartExpanded, onCheckedChange = { isChartExpanded = it }) {
Icon(
imageVector =
if (isChartExpanded) {
diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/DeviceMetrics.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/DeviceMetrics.kt
index f3e02818d..5725da604 100644
--- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/DeviceMetrics.kt
+++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/DeviceMetrics.kt
@@ -15,7 +15,6 @@
* along with this program. If not, see .
*/
@file:Suppress("MagicNumber")
-@file:OptIn(ExperimentalMaterial3ExpressiveApi::class)
package org.meshtastic.feature.node.metrics
@@ -31,7 +30,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
-import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/EnvironmentMetrics.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/EnvironmentMetrics.kt
index 12c604a46..4f9e88d47 100644
--- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/EnvironmentMetrics.kt
+++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/EnvironmentMetrics.kt
@@ -15,7 +15,6 @@
* along with this program. If not, see .
*/
@file:Suppress("TooManyFunctions")
-@file:OptIn(ExperimentalMaterial3ExpressiveApi::class)
package org.meshtastic.feature.node.metrics
@@ -30,7 +29,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
-import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/HostMetricsLog.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/HostMetricsLog.kt
index f22710ef5..2cbf008e1 100644
--- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/HostMetricsLog.kt
+++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/HostMetricsLog.kt
@@ -36,7 +36,6 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DropdownMenu
-import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ProgressIndicatorDefaults
@@ -155,7 +154,6 @@ private fun HostMetricsCard(telemetry: Telemetry, isSelected: Boolean, onClick:
}
/** Card body showing timestamp, load averages with progress bars, memory, disk, and uptime. */
-@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
private fun HostMetricsCardContent(time: String, hostMetrics: org.meshtastic.proto.HostMetrics?) {
Column(modifier = Modifier.padding(12.dp)) {
diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/MetricLogComponents.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/MetricLogComponents.kt
index 653293835..92e929056 100644
--- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/MetricLogComponents.kt
+++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/MetricLogComponents.kt
@@ -14,8 +14,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-@file:OptIn(ExperimentalMaterial3ExpressiveApi::class)
-
package org.meshtastic.feature.node.metrics
import androidx.compose.foundation.BorderStroke
@@ -35,7 +33,6 @@ import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DropdownMenuItem
-import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PaxMetrics.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PaxMetrics.kt
index cad2b63b1..598cd5ca9 100644
--- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PaxMetrics.kt
+++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PaxMetrics.kt
@@ -14,8 +14,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-@file:OptIn(ExperimentalMaterial3ExpressiveApi::class)
-
package org.meshtastic.feature.node.metrics
import androidx.compose.foundation.layout.Column
@@ -28,7 +26,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
-import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt
index e2064fd5f..c815f6622 100644
--- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt
+++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/PowerMetrics.kt
@@ -15,7 +15,6 @@
* along with this program. If not, see .
*/
@file:Suppress("MagicNumber")
-@file:OptIn(ExperimentalMaterial3ExpressiveApi::class)
package org.meshtastic.feature.node.metrics
@@ -31,7 +30,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.rememberScrollState
-import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilterChip
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/SignalMetrics.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/SignalMetrics.kt
index ca6fd2d61..e8b184427 100644
--- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/SignalMetrics.kt
+++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/SignalMetrics.kt
@@ -14,8 +14,6 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-@file:OptIn(ExperimentalMaterial3ExpressiveApi::class)
-
package org.meshtastic.feature.node.metrics
import androidx.compose.foundation.layout.Arrangement
@@ -31,7 +29,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
-import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
diff --git a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt
index bf5846e9f..caf3e1938 100644
--- a/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt
+++ b/feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/metrics/TracerouteLog.kt
@@ -36,7 +36,6 @@ import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DropdownMenu
-import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
@@ -242,7 +241,6 @@ private fun TracerouteCard(
/** Card body showing timestamp, route summary text/icon, and metric indicators. */
@Composable
-@OptIn(ExperimentalMaterial3ExpressiveApi::class)
private fun TracerouteCardContent(time: String, summaryText: String, icon: ImageVector, point: TraceroutePoint) {
Column(modifier = Modifier.padding(12.dp)) {
Row(
diff --git a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/debugging/Debug.kt b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/debugging/Debug.kt
index 3fab5b624..dba15e1a4 100644
--- a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/debugging/Debug.kt
+++ b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/debugging/Debug.kt
@@ -35,6 +35,7 @@ import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
+import androidx.compose.material3.IconToggleButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
@@ -138,7 +139,7 @@ fun DebugScreen(onNavigateUp: () -> Unit, viewModel: DebugViewModel) {
canNavigateUp = true,
onNavigateUp = onNavigateUp,
actions = {
- IconButton(onClick = { showSettings = !showSettings }) {
+ IconToggleButton(checked = showSettings, onCheckedChange = { showSettings = it }) {
Icon(imageVector = MeshtasticIcons.Settings, contentDescription = null)
}
DebugMenuActions(deleteLogs = { viewModel.requestDeleteAllLogs() })
diff --git a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/NetworkConfigItemList.kt b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/NetworkConfigItemList.kt
index b9796aba5..584f8eedc 100644
--- a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/NetworkConfigItemList.kt
+++ b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/NetworkConfigItemList.kt
@@ -22,6 +22,8 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -220,12 +222,19 @@ fun NetworkConfigScreen(viewModel: RadioConfigViewModel, onBack: () -> Unit, onO
onValueChanged = { formState.value = formState.value.copy(wifi_psk = it) },
)
HorizontalDivider()
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+ val mediumHeight = ButtonDefaults.MediumContainerHeight
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
Button(
onClick = { barcodeScanner.startScan() },
- modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp).height(48.dp),
+ shapes = ButtonDefaults.shapesFor(mediumHeight),
+ modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp).height(mediumHeight),
enabled = state.connected,
) {
- Text(text = stringResource(Res.string.wifi_qr_code_scan))
+ Text(
+ text = stringResource(Res.string.wifi_qr_code_scan),
+ style = ButtonDefaults.textStyleFor(mediumHeight),
+ )
}
}
}
diff --git a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/NodeActionButton.kt b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/NodeActionButton.kt
index fe9675e6d..fa6d9a8fb 100644
--- a/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/NodeActionButton.kt
+++ b/feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/radio/component/NodeActionButton.kt
@@ -24,9 +24,10 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.LocalContentColor
-import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@@ -37,14 +38,22 @@ import androidx.compose.ui.unit.dp
@Composable
fun NodeActionButton(
- modifier: Modifier = Modifier.fillMaxWidth().padding(vertical = 4.dp).height(48.dp),
+ modifier: Modifier = Modifier,
title: String,
enabled: Boolean,
icon: ImageVector? = null,
iconTint: Color? = null,
onClick: () -> Unit,
) {
- Button(onClick = { onClick() }, enabled = enabled, modifier = modifier) {
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+ val mediumHeight = ButtonDefaults.MediumContainerHeight
+ @OptIn(ExperimentalMaterial3ExpressiveApi::class)
+ Button(
+ onClick = { onClick() },
+ shapes = ButtonDefaults.shapesFor(mediumHeight),
+ enabled = enabled,
+ modifier = modifier.then(Modifier.fillMaxWidth().padding(vertical = 4.dp).height(mediumHeight)),
+ ) {
Row(verticalAlignment = Alignment.CenterVertically) {
if (icon != null) {
Icon(
@@ -55,7 +64,7 @@ fun NodeActionButton(
)
Spacer(modifier = Modifier.width(8.dp))
}
- Text(text = title, style = MaterialTheme.typography.bodyLarge, modifier = Modifier.weight(1f))
+ Text(text = title, style = ButtonDefaults.textStyleFor(mediumHeight), modifier = Modifier.weight(1f))
}
}
}
diff --git a/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionScreen.kt b/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionScreen.kt
index 20b54825e..785654c71 100644
--- a/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionScreen.kt
+++ b/feature/wifi-provision/src/commonMain/kotlin/org/meshtastic/feature/wifiprovision/ui/WifiProvisionScreen.kt
@@ -52,6 +52,7 @@ import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
+import androidx.compose.material3.IconToggleButton
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.ListItem
import androidx.compose.material3.ListItemDefaults
@@ -414,7 +415,7 @@ internal fun ConnectedContent(
singleLine = true,
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = {
- IconButton(onClick = { passwordVisible = !passwordVisible }) {
+ IconToggleButton(checked = passwordVisible, onCheckedChange = { passwordVisible = it }) {
Icon(
imageVector =
if (passwordVisible) MeshtasticIcons.VisibilityOff else MeshtasticIcons.Visibility,