1
1
package com.sujith.kotlin.koinnew
2
2
3
+ import android.app.Application
4
+ import android.content.Context
3
5
import android.os.Bundle
6
+ import android.widget.Toast
4
7
import androidx.activity.ComponentActivity
5
8
import androidx.activity.compose.setContent
6
9
import androidx.activity.enableEdgeToEdge
10
+ import androidx.compose.animation.AnimatedVisibility
11
+ import androidx.compose.animation.core.Spring
12
+ import androidx.compose.animation.core.spring
13
+ import androidx.compose.animation.fadeIn
14
+ import androidx.compose.animation.slideInVertically
15
+ import androidx.compose.animation.slideOutVertically
7
16
import androidx.compose.foundation.background
8
17
import androidx.compose.foundation.border
9
18
import androidx.compose.foundation.layout.Arrangement
19
+ import androidx.compose.foundation.layout.Box
10
20
import androidx.compose.foundation.layout.Column
11
21
import androidx.compose.foundation.layout.PaddingValues
22
+ import androidx.compose.foundation.layout.Row
12
23
import androidx.compose.foundation.layout.Spacer
13
24
import androidx.compose.foundation.layout.fillMaxSize
14
25
import androidx.compose.foundation.layout.fillMaxWidth
26
+ import androidx.compose.foundation.layout.height
15
27
import androidx.compose.foundation.layout.padding
16
28
import androidx.compose.foundation.lazy.LazyColumn
17
29
import androidx.compose.foundation.lazy.items
30
+ import androidx.compose.foundation.shape.RoundedCornerShape
18
31
import androidx.compose.material3.Button
32
+ import androidx.compose.material3.ButtonDefaults
19
33
import androidx.compose.material3.Card
20
34
import androidx.compose.material3.CardDefaults
35
+ import androidx.compose.material3.MaterialTheme
21
36
import androidx.compose.material3.OutlinedTextField
22
37
import androidx.compose.material3.Scaffold
38
+ import androidx.compose.material3.Snackbar
39
+ import androidx.compose.material3.SnackbarHost
40
+ import androidx.compose.material3.SnackbarHostState
41
+ import androidx.compose.material3.SnackbarResult
23
42
import androidx.compose.material3.Text
43
+ import androidx.compose.material3.TextButton
24
44
import androidx.compose.runtime.Composable
25
- import androidx.compose.runtime.collectAsState
45
+ import androidx.compose.runtime.LaunchedEffect
26
46
import androidx.compose.runtime.getValue
47
+ import androidx.compose.runtime.mutableStateOf
27
48
import androidx.compose.runtime.remember
49
+ import androidx.compose.runtime.rememberCoroutineScope
28
50
import androidx.compose.ui.Alignment
29
51
import androidx.compose.ui.Modifier
30
52
import androidx.compose.ui.graphics.Color
31
53
import androidx.compose.ui.graphics.RectangleShape
54
+ import androidx.compose.ui.text.font.FontWeight
32
55
import androidx.compose.ui.tooling.preview.Preview
33
56
import androidx.compose.ui.unit.dp
34
57
import androidx.lifecycle.compose.collectAsStateWithLifecycle
58
+ import androidx.navigation.NavHostController
59
+ import androidx.navigation.compose.NavHost
60
+ import androidx.navigation.compose.composable
61
+ import androidx.navigation.compose.rememberNavController
35
62
import com.sujith.kotlin.koinnew.model.Tweet
36
63
import com.sujith.kotlin.koinnew.ui.theme.KoinNewTheme
37
64
import com.sujith.kotlin.koinnew.viewmodel.MainViewModel
65
+ import com.sujith.kotlin.koinnew.viewmodel.ObserveAsEvents
66
+ import com.sujith.kotlin.koinnew.viewmodel.SnackBarAction
67
+ import com.sujith.kotlin.koinnew.viewmodel.SnackBarController
68
+ import com.sujith.kotlin.koinnew.viewmodel.SnackBarEvent
69
+ import kotlinx.coroutines.CoroutineScope
38
70
import kotlinx.coroutines.flow.MutableStateFlow
39
71
import kotlinx.coroutines.flow.StateFlow
72
+ import kotlinx.coroutines.flow.firstOrNull
73
+ import kotlinx.coroutines.launch
74
+ import kotlinx.serialization.Serializable
40
75
import org.koin.androidx.viewmodel.ext.android.viewModel
41
76
42
77
class MainActivity : ComponentActivity () {
@@ -57,36 +92,159 @@ class MainActivity : ComponentActivity() {
57
92
}*/
58
93
setContent {
59
94
KoinNewTheme {
60
- Scaffold (modifier = Modifier .fillMaxSize()) { innerPadding ->
61
- val tweets by viewModel.tweetsFlow.collectAsState()/* collectAsStateWithLifecycle()*/
62
- AddTweet (
63
- innerPadding,
64
- viewModel::addTweet,
65
- tweets,
66
- viewModel.tweetText,
67
- viewModel.category,
68
- viewModel
69
- )
95
+ val scope = rememberCoroutineScope()
96
+ val snackbarHostState = remember { SnackbarHostState () }
97
+ val currentEvent = remember { mutableStateOf<SnackBarEvent ?>(null ) }
98
+
99
+ ObserveAsEvents (flow = SnackBarController .events) { events ->
100
+ currentEvent.value = events // Store the latest event
101
+
102
+ scope.launch {
103
+ snackbarHostState.currentSnackbarData?.dismiss()
104
+ val result = snackbarHostState.showSnackbar(
105
+ message = events.message,
106
+ actionLabel = events.action?.label,
107
+ duration = events.duration,
108
+ withDismissAction = events.withDismissAction
109
+ )
110
+ when (result) {
111
+ SnackbarResult .ActionPerformed -> events.action?.action?.invoke()
112
+ SnackbarResult .Dismissed -> events.action?.dismissAction?.invoke()
113
+ }
114
+ }
115
+ }
116
+ Scaffold (
117
+ modifier = Modifier .fillMaxSize(),
118
+ snackbarHost = {
119
+ SnackbarHost (hostState = snackbarHostState) { data ->
120
+ /* AnimatedVisibility( // Animation for Snackbar entry
121
+ visible = snackbarHostState.currentSnackbarData != null,
122
+ enter = fadeIn(animationSpec = spring(stiffness = Spring.DampingRatioHighBouncy)), // Slide up animation
123
+ exit = slideOutVertically(targetOffsetY = { it }) // Slide down when dismissed
124
+ ) {
125
+
126
+ }*/
127
+ Snackbar (
128
+ modifier = Modifier
129
+ .padding(16 .dp)
130
+ .background(
131
+ currentEvent.value?.backgroundColor ? : MaterialTheme .colorScheme.primary,
132
+ shape = RoundedCornerShape (16 .dp)
133
+ ),
134
+ action = {
135
+ data.visuals.actionLabel?.let { actionLabel ->
136
+ TextButton (
137
+ onClick = { data.performAction() },
138
+ colors = ButtonDefaults .textButtonColors( // Custom background color
139
+ containerColor = Color .Yellow , // Change action button bg color
140
+ contentColor = Color .Black // Change text color
141
+ )
142
+ ) {
143
+ Text (
144
+ text = actionLabel,
145
+ fontWeight = FontWeight .Bold
146
+ )
147
+ }
148
+ }
149
+ },
150
+
151
+ dismissAction = if (data.visuals.withDismissAction) {
152
+ {
153
+ TextButton (
154
+ onClick = { data.dismiss() },
155
+ colors = ButtonDefaults .textButtonColors( // Custom dismiss bg color
156
+ containerColor = Color .Red , // Change dismiss button bg color
157
+ contentColor = Color .White // Change text color
158
+ )
159
+ ) {
160
+ Text (text = " Dismiss" , fontWeight = FontWeight .Bold )
161
+ }
162
+ }
163
+ } else null ,
164
+ shape = RoundedCornerShape (16 .dp),
165
+ containerColor = MaterialTheme .colorScheme.secondary,
166
+ contentColor = Color .White ,
167
+ actionContentColor = Color .Red ,
168
+ dismissActionContentColor = Color .Green ,
169
+ ) {
170
+ Column {
171
+ Text (
172
+ text = data.visuals.message,
173
+ style = MaterialTheme .typography.bodyLarge
174
+ )
175
+ Spacer (modifier = Modifier .height(4 .dp)) // Add spacing
176
+
177
+ // Custom Dismiss Button (Optional)
178
+ /* TextButton(
179
+ onClick = { data.dismiss() } // Explicitly dismiss snackbar
180
+ ) {
181
+ Text(
182
+ text = "Dismiss",
183
+ color = Color.Green,
184
+ fontWeight = FontWeight.Bold
185
+ )
186
+ }*/
187
+ }
188
+ }
189
+ }
190
+ },
191
+ ) { innerPadding ->
192
+ val navController = rememberNavController()
193
+ NavHost (navController = navController, startDestination = Screens .AddTweet ) {
194
+ composable<Screens .AddTweet >() {
195
+ val tweets by viewModel.tweetsFlow./* collectAsState()*/ collectAsStateWithLifecycle()
196
+ AddTweet (
197
+ this @MainActivity,
198
+ innerPadding,
199
+ viewModel::addTweet,
200
+ tweets,
201
+ viewModel.tweetText,
202
+ viewModel.category,
203
+ viewModel,
204
+ snackbarHostState,
205
+ scope,
206
+ navController = navController
207
+ )
208
+ }
209
+ composable<Screens .ScreenB >() {
210
+ ScreenB ()
211
+ }
212
+ }
70
213
}
71
214
}
72
215
}
73
216
}
74
217
}
75
218
219
+
220
+ sealed class Screens {
221
+ @Serializable
222
+ data object AddTweet : Screens ()
223
+
224
+ @Serializable
225
+ data object ScreenB : Screens ()
226
+ }
227
+
228
+
76
229
@Composable
77
230
fun AddTweet (
231
+ context : Context ,
78
232
paddingValues : PaddingValues ,
79
233
addOnclick : (Tweet ) -> Unit ,
80
234
tweets : List <Tweet >,
81
235
tweet : StateFlow <String >,
82
236
category : StateFlow <String >,
83
237
viewModel : MainViewModel ,
238
+ snackbarHostState : SnackbarHostState ,
239
+ scope : CoroutineScope = rememberCoroutineScope(),
240
+ navController : NavHostController ,
84
241
) {
85
242
86
243
// val category by category.collectAsStateWithLifecycle()
87
244
// val tweetText by tweet.collectAsStateWithLifecycle()
88
245
val category by remember { viewModel.category }.collectAsStateWithLifecycle()
89
246
val tweetText by remember { viewModel.tweetText }.collectAsStateWithLifecycle()
247
+
90
248
Column (
91
249
modifier = Modifier
92
250
.padding(paddingValues)
@@ -108,10 +266,59 @@ fun AddTweet(
108
266
addOnclick(Tweet (category, tweetText))
109
267
viewModel.resetFields()
110
268
}
269
+ else {
270
+ scope.launch {
271
+ SnackBarController .sendEvent(
272
+ event = SnackBarEvent (
273
+ message = " Deleted the data" ,
274
+ withDismissAction = false ,
275
+ backgroundColor = Color .White ,
276
+ action = SnackBarAction (
277
+ label = " Undo" ,
278
+ action = {
279
+ println (" Action performed" )
280
+ SnackBarController .sendEvent(
281
+ event = SnackBarEvent (
282
+ message = " Undo deleted data" ,
283
+ backgroundColor = Color .Red
284
+ )
285
+ )
286
+ },
287
+ dismissAction = {
288
+ println (" Dismissed" )
289
+ // viewModel.resetFields()
290
+ SnackBarController .sendEvent(
291
+ event = SnackBarEvent (
292
+ message = " Successfully deleted" ,
293
+ backgroundColor = Color .Green ,
294
+ )
295
+ )
296
+ }
297
+ )
298
+ )
299
+ )
300
+ }
301
+ }
111
302
}
112
303
) {
113
304
Text (text = " Add" )
114
305
}
306
+ Button (onClick = {
307
+ // navController.navigate(Screens.ScreenB)
308
+ // Toast.makeText(context, "nav b", Toast.LENGTH_SHORT).show()
309
+ scope.launch {
310
+ SnackBarController .sendEvent(
311
+ event = SnackBarEvent (
312
+ message = " Custom Background Color!" ,
313
+ backgroundColor = Color (0xFF00FF00 ) // Explicit green color
314
+ )
315
+ )
316
+ }
317
+
318
+
319
+ }) {
320
+ Text (" nav b" )
321
+ }
115
322
Spacer (
116
323
modifier = Modifier
117
324
.padding(horizontal = 10 .dp)
@@ -121,6 +328,16 @@ fun AddTweet(
121
328
}
122
329
}
123
330
331
+ @Composable
332
+ fun ScreenB () {
333
+ Box (
334
+ modifier = Modifier .fillMaxSize(),
335
+ contentAlignment = Alignment .Center
336
+ ) {
337
+ Text (text = " Screen B" )
338
+ }
339
+ }
340
+
124
341
@Composable
125
342
fun CustomOutlinedTextField (value : String , labelText : String , onValueChange : (String ) -> Unit ) {
126
343
OutlinedTextField (
@@ -171,14 +388,18 @@ fun AddTweetPreview() {
171
388
val dummyCategory = MutableStateFlow (" Sample Category" )
172
389
val dummyTweetText = MutableStateFlow (" Sample Tweet" )
173
390
val fakeViewModel = MainViewModel () // Temporary ViewModel for Preview
174
-
391
+ val context = Application ()
175
392
AddTweet (
393
+ context = context,
176
394
paddingValues = PaddingValues (10 .dp),
177
395
addOnclick = {},
178
396
tweets = listOf (Tweet (" test" , " tweet" )),
179
397
tweet = dummyTweetText,
180
398
category = dummyCategory,
181
- viewModel = fakeViewModel
399
+ viewModel = fakeViewModel,
400
+ snackbarHostState = SnackbarHostState (),
401
+ navController = rememberNavController()
402
+
182
403
)
183
404
}
184
405
0 commit comments