Skip to content

Commit ece8878

Browse files
committed
Improve settings screen with modern UI design and animations
1 parent d253a08 commit ece8878

1 file changed

Lines changed: 245 additions & 46 deletions

File tree

  • app/src/main/java/dev/abd3lraouf/learn/decompose/features/settings/presentation

app/src/main/java/dev/abd3lraouf/learn/decompose/features/settings/presentation/SettingsScreen.kt

Lines changed: 245 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,30 @@
11
package dev.abd3lraouf.learn.decompose.features.settings.presentation
22

3+
import androidx.compose.animation.AnimatedVisibility
4+
import androidx.compose.animation.core.animateFloatAsState
5+
import androidx.compose.foundation.background
36
import androidx.compose.foundation.clickable
47
import androidx.compose.foundation.layout.Arrangement
58
import androidx.compose.foundation.layout.Box
69
import androidx.compose.foundation.layout.Column
710
import androidx.compose.foundation.layout.Row
11+
import androidx.compose.foundation.layout.Spacer
812
import androidx.compose.foundation.layout.fillMaxSize
913
import androidx.compose.foundation.layout.fillMaxWidth
14+
import androidx.compose.foundation.layout.height
1015
import androidx.compose.foundation.layout.padding
16+
import androidx.compose.foundation.layout.size
17+
import androidx.compose.foundation.layout.width
18+
import androidx.compose.foundation.rememberScrollState
19+
import androidx.compose.foundation.shape.CircleShape
20+
import androidx.compose.foundation.verticalScroll
1121
import androidx.compose.material3.Card
12-
import androidx.compose.material3.DropdownMenu
13-
import androidx.compose.material3.DropdownMenuItem
22+
import androidx.compose.material3.CardDefaults
23+
import androidx.compose.material3.Divider
1424
import androidx.compose.material3.Icon
1525
import androidx.compose.material3.MaterialTheme
1626
import androidx.compose.material3.Switch
27+
import androidx.compose.material3.SwitchDefaults
1728
import androidx.compose.material3.Text
1829
import androidx.compose.runtime.Composable
1930
import androidx.compose.runtime.getValue
@@ -22,14 +33,24 @@ import androidx.compose.runtime.remember
2233
import androidx.compose.runtime.setValue
2334
import androidx.compose.ui.Alignment
2435
import androidx.compose.ui.Modifier
36+
import androidx.compose.ui.draw.clip
2537
import androidx.compose.ui.draw.rotate
38+
import androidx.compose.ui.graphics.Color
2639
import androidx.compose.ui.text.font.FontWeight
2740
import androidx.compose.ui.tooling.preview.Preview
2841
import androidx.compose.ui.unit.dp
2942
import com.arkivanov.decompose.extensions.compose.subscribeAsState
3043
import com.woowla.compose.icon.collections.heroicons.Heroicons
44+
import com.woowla.compose.icon.collections.heroicons.heroicons.Outline
3145
import com.woowla.compose.icon.collections.heroicons.heroicons.Solid
32-
import com.woowla.compose.icon.collections.heroicons.heroicons.solid.ArrowLeft
46+
import com.woowla.compose.icon.collections.heroicons.heroicons.outline.ChevronDown
47+
import com.woowla.compose.icon.collections.heroicons.heroicons.outline.InformationCircle
48+
import com.woowla.compose.icon.collections.heroicons.heroicons.outline.MoonIcon
49+
import com.woowla.compose.icon.collections.heroicons.heroicons.outline.SunIcon
50+
import com.woowla.compose.icon.collections.heroicons.heroicons.solid.Cog
51+
import com.woowla.compose.icon.collections.heroicons.heroicons.solid.Document
52+
import com.woowla.compose.icon.collections.heroicons.heroicons.solid.Heart
53+
import com.woowla.compose.icon.collections.heroicons.heroicons.solid.ShieldCheck
3354
import dev.abd3lraouf.learn.decompose.ui.preview.PreviewSettingsComponent
3455
import dev.abd3lraouf.learn.decompose.ui.theme.DecomposeTutorialTheme
3556

@@ -39,72 +60,250 @@ fun SettingsContent(
3960
modifier: Modifier = Modifier
4061
) {
4162
val settings by component.model.subscribeAsState()
42-
var expanded by remember { mutableStateOf(false) }
63+
val scrollState = rememberScrollState()
4364

4465
Column(
4566
modifier = modifier
4667
.fillMaxSize()
47-
.padding(16.dp),
68+
.padding(16.dp)
69+
.verticalScroll(scrollState),
4870
verticalArrangement = Arrangement.spacedBy(16.dp)
4971
) {
50-
Text(
51-
text = "Settings",
52-
style = MaterialTheme.typography.headlineMedium,
53-
fontWeight = FontWeight.Bold
54-
)
72+
// Header
73+
Row(
74+
modifier = Modifier.fillMaxWidth(),
75+
verticalAlignment = Alignment.CenterVertically
76+
) {
77+
Box(
78+
modifier = Modifier
79+
.size(48.dp)
80+
.clip(CircleShape)
81+
.background(MaterialTheme.colorScheme.primaryContainer),
82+
contentAlignment = Alignment.Center
83+
) {
84+
Icon(
85+
imageVector = Heroicons.Solid.Cog,
86+
contentDescription = "Settings",
87+
tint = MaterialTheme.colorScheme.onPrimaryContainer,
88+
modifier = Modifier.size(24.dp)
89+
)
90+
}
91+
Spacer(modifier = Modifier.width(16.dp))
92+
Text(
93+
text = "Settings",
94+
style = MaterialTheme.typography.headlineMedium,
95+
fontWeight = FontWeight.Bold
96+
)
97+
}
5598

56-
Card(
57-
modifier = Modifier.fillMaxWidth()
99+
Spacer(modifier = Modifier.height(8.dp))
100+
101+
// Appearance Card
102+
SettingsCard(
103+
title = "Appearance",
104+
icon = Heroicons.Outline.SunIcon,
105+
iconTint = Color(0xFFFFA000) // Amber
58106
) {
59-
Column(
107+
Row(
60108
modifier = Modifier
61109
.fillMaxWidth()
62-
.padding(16.dp),
63-
verticalArrangement = Arrangement.spacedBy(16.dp)
110+
.padding(vertical = 8.dp),
111+
horizontalArrangement = Arrangement.SpaceBetween,
112+
verticalAlignment = Alignment.CenterVertically
64113
) {
65-
Text(
66-
text = "Appearance",
67-
style = MaterialTheme.typography.titleMedium,
68-
fontWeight = FontWeight.Bold
69-
)
70-
71114
Row(
72-
modifier = Modifier.fillMaxWidth(),
73-
horizontalArrangement = Arrangement.SpaceBetween,
74115
verticalAlignment = Alignment.CenterVertically
75116
) {
76-
Text("Dark Mode")
77-
Switch(
78-
checked = settings.isDarkMode,
79-
onCheckedChange = { component.onThemeChanged(it) }
117+
Icon(
118+
imageVector = if (settings.isDarkMode) Heroicons.Outline.MoonIcon else Heroicons.Outline.SunIcon,
119+
contentDescription = "Theme",
120+
tint = MaterialTheme.colorScheme.onSurfaceVariant,
121+
modifier = Modifier.size(20.dp)
122+
)
123+
Spacer(modifier = Modifier.width(12.dp))
124+
Text(
125+
text = if (settings.isDarkMode) "Dark Mode" else "Light Mode",
126+
style = MaterialTheme.typography.bodyLarge
80127
)
81128
}
129+
130+
Switch(
131+
checked = settings.isDarkMode,
132+
onCheckedChange = { component.onThemeChanged(it) },
133+
thumbContent = if (settings.isDarkMode) {
134+
{
135+
Icon(
136+
imageVector = Heroicons.Outline.MoonIcon,
137+
contentDescription = null,
138+
modifier = Modifier.size(SwitchDefaults.IconSize)
139+
)
140+
}
141+
} else {
142+
{
143+
Icon(
144+
imageVector = Heroicons.Outline.SunIcon,
145+
contentDescription = null,
146+
modifier = Modifier.size(SwitchDefaults.IconSize)
147+
)
148+
}
149+
}
150+
)
82151
}
83152
}
84153

85-
Card(
86-
modifier = Modifier.fillMaxWidth()
154+
// Help & Support
155+
SettingsCard(
156+
title = "Help & Support",
157+
icon = Heroicons.Outline.InformationCircle,
158+
iconTint = MaterialTheme.colorScheme.primary
87159
) {
88-
Column(
89-
modifier = Modifier
90-
.fillMaxWidth()
91-
.padding(16.dp),
92-
verticalArrangement = Arrangement.spacedBy(8.dp)
93-
) {
94-
Text(
95-
text = "About",
96-
style = MaterialTheme.typography.titleMedium,
97-
fontWeight = FontWeight.Bold
98-
)
99-
100-
Text(
101-
text = "Decompose Tutorial App v1.0",
102-
style = MaterialTheme.typography.bodyMedium
160+
SettingsItem(
161+
icon = Heroicons.Solid.Document,
162+
title = "Documentation",
163+
subtitle = "Learn how to use this app",
164+
onClick = { /* Open documentation */ }
165+
)
166+
167+
Divider(modifier = Modifier.padding(vertical = 8.dp))
168+
169+
SettingsItem(
170+
icon = Heroicons.Solid.Heart,
171+
title = "Rate the App",
172+
subtitle = "Let us know what you think",
173+
onClick = { /* Open rating */ }
174+
)
175+
}
176+
177+
// About Card
178+
var showAboutDetails by remember { mutableStateOf(false) }
179+
val rotationState by animateFloatAsState(
180+
targetValue = if (showAboutDetails) 180f else 0f,
181+
label = "Rotation Animation"
182+
)
183+
184+
SettingsCard(
185+
title = "About",
186+
icon = Heroicons.Solid.ShieldCheck,
187+
iconTint = Color(0xFF4CAF50), // Green
188+
titleTrailingContent = {
189+
Icon(
190+
imageVector = Heroicons.Outline.ChevronDown,
191+
contentDescription = "Expand",
192+
modifier = Modifier
193+
.rotate(rotationState)
194+
.clickable { showAboutDetails = !showAboutDetails }
103195
)
196+
}
197+
) {
198+
Text(
199+
text = "Decompose Tutorial App v1.0",
200+
style = MaterialTheme.typography.bodyMedium,
201+
fontWeight = FontWeight.Medium
202+
)
203+
204+
AnimatedVisibility(visible = showAboutDetails) {
205+
Column(modifier = Modifier.padding(top = 8.dp)) {
206+
Text(
207+
text = "This app demonstrates the capabilities of the Decompose library for navigation and state management in Jetpack Compose applications.",
208+
style = MaterialTheme.typography.bodyMedium
209+
)
210+
211+
Spacer(modifier = Modifier.height(8.dp))
212+
213+
Text(
214+
text = "Developed by Abdelraouf Sabri",
215+
style = MaterialTheme.typography.bodySmall
216+
)
217+
218+
Text(
219+
text = "© 2023 abd3lraouf.dev",
220+
style = MaterialTheme.typography.bodySmall
221+
)
222+
}
223+
}
224+
}
225+
}
226+
}
227+
228+
@Composable
229+
fun SettingsCard(
230+
title: String,
231+
icon: Any,
232+
iconTint: Color = MaterialTheme.colorScheme.primary,
233+
titleTrailingContent: @Composable (() -> Unit)? = null,
234+
content: @Composable () -> Unit
235+
) {
236+
Card(
237+
modifier = Modifier.fillMaxWidth(),
238+
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
239+
) {
240+
Column(
241+
modifier = Modifier
242+
.fillMaxWidth()
243+
.padding(16.dp),
244+
verticalArrangement = Arrangement.spacedBy(12.dp)
245+
) {
246+
Row(
247+
modifier = Modifier.fillMaxWidth(),
248+
verticalAlignment = Alignment.CenterVertically,
249+
horizontalArrangement = Arrangement.SpaceBetween
250+
) {
251+
Row(verticalAlignment = Alignment.CenterVertically) {
252+
Icon(
253+
imageVector = icon as androidx.compose.ui.graphics.vector.ImageVector,
254+
contentDescription = null,
255+
tint = iconTint,
256+
modifier = Modifier.size(24.dp)
257+
)
258+
Spacer(modifier = Modifier.width(12.dp))
259+
Text(
260+
text = title,
261+
style = MaterialTheme.typography.titleMedium,
262+
fontWeight = FontWeight.Bold
263+
)
264+
}
104265

266+
if (titleTrailingContent != null) {
267+
titleTrailingContent()
268+
}
269+
}
270+
271+
content()
272+
}
273+
}
274+
}
275+
276+
@Composable
277+
fun SettingsItem(
278+
icon: Any,
279+
title: String,
280+
subtitle: String? = null,
281+
onClick: () -> Unit
282+
) {
283+
Row(
284+
modifier = Modifier
285+
.fillMaxWidth()
286+
.clickable(onClick = onClick)
287+
.padding(vertical = 8.dp),
288+
verticalAlignment = Alignment.CenterVertically
289+
) {
290+
Icon(
291+
imageVector = icon as androidx.compose.ui.graphics.vector.ImageVector,
292+
contentDescription = null,
293+
tint = MaterialTheme.colorScheme.onSurfaceVariant,
294+
modifier = Modifier.size(20.dp)
295+
)
296+
Spacer(modifier = Modifier.width(12.dp))
297+
Column {
298+
Text(
299+
text = title,
300+
style = MaterialTheme.typography.bodyLarge
301+
)
302+
if (subtitle != null) {
105303
Text(
106-
text = "This app demonstrates the capabilities of the Decompose library for navigation and state management in Jetpack Compose applications.",
107-
style = MaterialTheme.typography.bodySmall
304+
text = subtitle,
305+
style = MaterialTheme.typography.bodySmall,
306+
color = MaterialTheme.colorScheme.onSurfaceVariant
108307
)
109308
}
110309
}

0 commit comments

Comments
 (0)