Skip to content

Commit d48e273

Browse files
Merge pull request #95 from CompassMB/features
feat: More aesthetically pleasing setting item impl
2 parents 4543fb4 + dccc78e commit d48e273

3 files changed

Lines changed: 108 additions & 89 deletions

File tree

app/src/main/java/com/mubarak/mbcompass/ui/settings/SettingsScreen.kt

Lines changed: 72 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,16 @@ import androidx.compose.foundation.layout.requiredSize
2424
import androidx.compose.foundation.lazy.LazyColumn
2525
import androidx.compose.foundation.lazy.rememberLazyListState
2626
import androidx.compose.foundation.rememberScrollState
27+
import androidx.compose.foundation.shape.RoundedCornerShape
2728
import androidx.compose.foundation.verticalScroll
2829
import androidx.compose.material.icons.Icons
2930
import androidx.compose.material.icons.automirrored.filled.ArrowBack
3031
import androidx.compose.material3.AlertDialog
31-
import androidx.compose.material3.ElevatedCard
3232
import androidx.compose.material3.ExperimentalMaterial3Api
3333
import androidx.compose.material3.Icon
3434
import androidx.compose.material3.IconButton
35+
import androidx.compose.material3.ListItem
36+
import androidx.compose.material3.ListItemDefaults
3537
import androidx.compose.material3.MaterialTheme
3638
import androidx.compose.material3.RadioButton
3739
import androidx.compose.material3.RadioButtonDefaults
@@ -41,6 +43,7 @@ import androidx.compose.material3.SnackbarHostState
4143
import androidx.compose.material3.Text
4244
import androidx.compose.material3.TextButton
4345
import androidx.compose.material3.TopAppBar
46+
import androidx.compose.material3.surfaceColorAtElevation
4447
import androidx.compose.runtime.Composable
4548
import androidx.compose.runtime.getValue
4649
import androidx.compose.runtime.mutableStateOf
@@ -60,8 +63,11 @@ import androidx.hilt.navigation.compose.hiltViewModel
6063
import androidx.lifecycle.compose.collectAsStateWithLifecycle
6164
import com.mubarak.mbcompass.R
6265
import com.mubarak.mbcompass.ui.theme.MBCompassTheme
66+
import com.mubarak.mbcompass.ui.theme.MBShapeDefaults.bottomListItemShape
67+
import com.mubarak.mbcompass.ui.theme.MBShapeDefaults.middleListItemShape
68+
import com.mubarak.mbcompass.ui.theme.MBShapeDefaults.singleListItemShape
69+
import com.mubarak.mbcompass.ui.theme.MBShapeDefaults.topListItemShape
6370
import com.mubarak.mbcompass.ui.theme.iconDefaultSize
64-
import com.mubarak.mbcompass.ui.theme.spacingLarge
6571
import com.mubarak.mbcompass.ui.theme.spacingMedium
6672
import com.mubarak.mbcompass.ui.theme.spacingSmall
6773
import com.mubarak.mbcompass.utils.Const.APP_PAGE
@@ -80,9 +86,8 @@ fun SettingsScreen(
8086
val context = LocalContext.current
8187
val uriHandler = LocalUriHandler.current
8288
val launcher = rememberLauncherForActivityResult(
83-
contract = ActivityResultContracts.StartActivityForResult(),
84-
onResult = {}
85-
)
89+
contract = ActivityResultContracts.StartActivityForResult(), onResult = {})
90+
8691
SettingsScreen(
8792
uiState = uiState,
8893
onBackClicked = onBack,
@@ -98,8 +103,7 @@ fun SettingsScreen(
98103
},
99104
onSourceClicked = {
100105
uriHandler.openUri(APP_PAGE)
101-
}
102-
)
106+
})
103107
}
104108

105109
@OptIn(ExperimentalMaterial3Api::class)
@@ -116,17 +120,14 @@ fun SettingsScreen(
116120
val snackbarHostState = remember { SnackbarHostState() }
117121
Scaffold(
118122
topBar = {
119-
TopAppBar(
120-
title = { Text(stringResource(R.string.settings)) },
121-
navigationIcon = {
122-
IconButton(onClick = onBackClicked) {
123-
Icon(
124-
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
125-
contentDescription = "Localized description"
126-
)
127-
}
123+
TopAppBar(title = { Text(stringResource(R.string.settings)) }, navigationIcon = {
124+
IconButton(onClick = onBackClicked) {
125+
Icon(
126+
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
127+
contentDescription = "Localized description"
128+
)
128129
}
129-
)
130+
})
130131
},
131132
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
132133
) { paddingValues ->
@@ -155,17 +156,18 @@ private fun SettingsList(
155156
modifier: Modifier = Modifier,
156157
uiState: SettingsViewModel.SettingsUiState,
157158
onThemeItemClicked: () -> Unit,
158-
onAuthorPageClicked: () -> Unit = {},
159-
onLicensesClicked: () -> Unit = {},
160-
onSupportClicked: () -> Unit = {},
161-
onSourceClicked: () -> Unit = {},
159+
onAuthorPageClicked: () -> Unit,
160+
onLicensesClicked: () -> Unit,
161+
onSupportClicked: () -> Unit,
162+
onSourceClicked: () -> Unit,
162163
) {
163164
Box(
164165
modifier = modifier.fillMaxSize(),
165166
) {
166167
val listState = rememberLazyListState()
167168
LazyColumn(
168169
contentPadding = PaddingValues(16.dp),
170+
verticalArrangement = Arrangement.spacedBy(2.dp),
169171
state = listState,
170172
) {
171173
item(key = "__displayHeader") {
@@ -178,10 +180,11 @@ private fun SettingsList(
178180
}
179181
item(key = "__themeItem") {
180182
SettingsItem(
181-
title = stringResource(R.string.theme),
182183
icon = R.drawable.theme_icon24px,
183-
subtitle = getThemeName(option = uiState.theme),
184-
onClick = onThemeItemClicked,
184+
shape = singleListItemShape,
185+
headlineText = stringResource(R.string.theme),
186+
supportingText = getThemeName(option = uiState.theme),
187+
onItemClicked = onThemeItemClicked
185188
)
186189
}
187190
item(key = "__aboutHeader") {
@@ -195,90 +198,75 @@ private fun SettingsList(
195198
}
196199
item(key = "__aboutItem") {
197200
SettingsItem(
198-
title = stringResource(R.string.author),
199201
icon = R.drawable.person_icon24px,
200-
subtitle = stringResource(R.string.developer),
201-
onClick = onAuthorPageClicked,
202+
shape = topListItemShape,
203+
headlineText = stringResource(R.string.author),
204+
supportingText = stringResource(R.string.developer),
205+
onItemClicked = onAuthorPageClicked
202206
)
203207
}
204208
item(key = "__licenseItem") {
205209
SettingsItem(
206-
title = stringResource(R.string.licenses),
207210
icon = R.drawable.license_icon24px,
208-
subtitle = stringResource(R.string.app_license),
209-
onClick = onLicensesClicked,
211+
shape = middleListItemShape,
212+
headlineText = stringResource(R.string.licenses),
213+
supportingText = stringResource(R.string.app_license),
214+
onItemClicked = onLicensesClicked
210215
)
211216
}
212217
item(key = "__supportItem") {
213218
SettingsItem(
214-
title = stringResource(R.string.support),
215219
icon = R.drawable.icon_support_24,
216-
subtitle = stringResource(R.string.donate),
217-
onClick = onSupportClicked,
220+
shape = middleListItemShape,
221+
headlineText = stringResource(R.string.support),
222+
supportingText = stringResource(R.string.donate),
223+
onItemClicked = onSupportClicked
218224
)
219225
}
220226
item(key = "__sourcecodeItem") {
221227
SettingsItem(
222-
title = stringResource(R.string.source_code),
223228
icon = R.drawable.code_icon24px,
224-
subtitle = stringResource(R.string.github),
225-
onClick = onSourceClicked,
229+
shape = bottomListItemShape,
230+
headlineText = stringResource(R.string.source_code),
231+
supportingText = stringResource(R.string.github),
232+
onItemClicked = onSourceClicked
226233
)
227234
}
228235
}
229236
}
230237
}
231238

232239
@Composable
233-
private fun SettingsItem(
234-
title: String,
235-
onClick: () -> Unit,
236-
subtitle: String? = null,
237-
@DrawableRes icon: Int? = null,
240+
fun SettingsItem(
241+
modifier: Modifier = Modifier,
242+
@DrawableRes icon: Int,
243+
shape: RoundedCornerShape,
244+
headlineText: String,
245+
supportingText: String,
246+
onItemClicked: () -> Unit
238247
) {
239-
240-
ElevatedCard(
241-
modifier = Modifier
242-
.fillMaxWidth()
243-
.padding(vertical = 6.dp)
244-
) {
245-
Row(
246-
horizontalArrangement = Arrangement.spacedBy(spacingSmall),
247-
verticalAlignment = Alignment.CenterVertically,
248-
modifier = Modifier
249-
.fillMaxWidth()
250-
.clip(MaterialTheme.shapes.large)
251-
.clickable(onClick = onClick)
252-
.padding(vertical = spacingMedium)
253-
.padding(start = spacingLarge),
254-
) {
255-
256-
if (icon != null) {
257-
Icon(
258-
painter = painterResource(icon),
259-
contentDescription = null,
260-
tint = MaterialTheme.colorScheme.onBackground,
261-
modifier = Modifier.requiredSize(iconDefaultSize),
262-
)
263-
}
264-
Column {
265-
Text(
266-
text = title,
267-
style = MaterialTheme.typography.titleMedium,
268-
color = MaterialTheme.colorScheme.onBackground,
269-
)
270-
subtitle?.let {
271-
Text(
272-
text = it,
273-
style = MaterialTheme.typography.bodyMedium,
274-
color = MaterialTheme.colorScheme.onSurfaceVariant,
275-
)
276-
}
277-
}
278-
}
279-
}
248+
ListItem(
249+
leadingContent = {
250+
Icon(
251+
painter = painterResource(icon),
252+
contentDescription = headlineText,
253+
modifier = Modifier.requiredSize(iconDefaultSize)
254+
)
255+
}, colors = ListItemDefaults.colors(
256+
containerColor = MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp),
257+
headlineColor = MaterialTheme.colorScheme.onSurface,
258+
supportingColor = MaterialTheme.colorScheme.onSurfaceVariant
259+
), headlineContent = {
260+
Text(headlineText)
261+
}, supportingContent = {
262+
Text(supportingText)
263+
}, modifier = modifier
264+
.clip(shape)
265+
.clickable(onClick = onItemClicked)
266+
)
280267
}
281268

269+
282270
@Composable
283271
private fun ThemeDialog(
284272
isDialogVisible: Boolean,
@@ -331,7 +319,7 @@ private fun ThemeDialog(
331319
TextButton(
332320
onClick = onDismissRequest,
333321
) {
334-
Text(text = stringResource(R.string.cancel))
322+
Text(text = stringResource(R.string.dismiss))
335323
}
336324
},
337325
)
@@ -370,7 +358,6 @@ fun SettingsScreenPreview() {
370358
onLicensesClicked = {},
371359
onSupportClicked = {},
372360
onAuthorPageClicked = {},
373-
onSourceClicked = {}
374-
)
361+
onSourceClicked = {})
375362
}
376363
}

app/src/main/java/com/mubarak/mbcompass/ui/theme/Shape.kt

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,44 @@
22

33
package com.mubarak.mbcompass.ui.theme
44

5-
5+
import androidx.compose.foundation.shape.RoundedCornerShape
6+
import androidx.compose.material3.MaterialTheme.shapes
7+
import androidx.compose.runtime.Composable
68
import androidx.compose.ui.unit.dp
79

810
val spacingSmall = 8.dp
911
val spacingMedium = 16.dp
10-
val spacingLarge = 24.dp
1112

1213

13-
val iconDefaultSize = 24.dp
14+
val iconDefaultSize = 24.dp
15+
16+
object MBShapeDefaults {
17+
18+
val singleListItemShape = RoundedCornerShape(12.dp)
19+
20+
val topListItemShape: RoundedCornerShape
21+
@Composable get() =
22+
RoundedCornerShape(
23+
topStart = shapes.large.topStart,
24+
topEnd = shapes.large.topEnd,
25+
bottomStart = shapes.extraSmall.bottomStart,
26+
bottomEnd = shapes.extraSmall.bottomStart
27+
)
28+
29+
val middleListItemShape: RoundedCornerShape
30+
@Composable get() = RoundedCornerShape(
31+
topStart = shapes.extraSmall.topStart,
32+
topEnd = shapes.extraSmall.topEnd,
33+
bottomStart = shapes.extraSmall.bottomStart,
34+
bottomEnd = shapes.extraSmall.bottomEnd
35+
)
36+
37+
val bottomListItemShape: RoundedCornerShape
38+
@Composable get() =
39+
RoundedCornerShape(
40+
topStart = shapes.extraSmall.topStart,
41+
topEnd = shapes.extraSmall.topEnd,
42+
bottomStart = shapes.large.bottomStart,
43+
bottomEnd = shapes.large.bottomEnd
44+
)
45+
}

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
<string name="light_theme">Light</string>
3737
<string name="dark_theme">Dark</string>
3838
<string name="choose_theme">Choose theme</string>
39-
<string name="cancel">Cancel</string>
39+
<string name="dismiss">Dismiss</string>
4040
<string name="app_license">GNU GPLv3</string>
4141
<string name="settings_about">About</string>
4242
<string name="display">Display</string>

0 commit comments

Comments
 (0)