Lokik Soni
06/20/2022, 4:48 PMLokik Soni
06/20/2022, 4:48 PMfun NotesScreen(
navController: NavController,
viewModel: NoteViewModel
) {
val noteState by viewModel.noteState.collectAsState()
val scaffoldState = rememberScaffoldState()
noteState.errorMsg?.let { message ->
val undoMessageText = stringResource(R.string.undo)
LaunchedEffect(scaffoldState, undoMessageText, message) {
val result = scaffoldState.snackbarHostState.showSnackbar(
message = message,
actionLabel = undoMessageText
)
if (result == SnackbarResult.ActionPerformed) {
viewModel.onEvent(NotesEvent.RestoreNote)
}
viewModel.onEvent(NotesEvent.MessageShown)
}
}
Scaffold(
modifier = Modifier
.fillMaxSize(),
scaffoldState = scaffoldState,
floatingActionButton = {
FloatingActionButton(
onClick = {
// TODO dismiss snackbar before go to new screen if shown
navController.navigate(Screen.AddEditNote.route)
},
backgroundColor = MaterialTheme.colors.primary
) {
Icon(
imageVector = Icons.Rounded.Add,
contentDescription = stringResource(R.string.add_note)
)
}
},
) {
Column(
modifier = Modifier
.padding(16.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(R.string.your_note),
style = MaterialTheme.typography.h4
)
IconButton(
onClick = { viewModel.onEvent(NotesEvent.ToggleOrderSection) }
) {
Icon(
imageVector = Icons.Rounded.Sort,
contentDescription = stringResource(R.string.sort)
)
}
}
AnimatedVisibility(
visible = noteState.isOrderSectionVisible
) {
OrderSection(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
noteOrder = noteState.noteOrder,
onOrderChange = {
viewModel.onEvent(NotesEvent.Order(it))
}
)
}
Spacer(modifier = Modifier.height(16.dp))
LazyColumn {
items(noteState.notes) { note ->
NoteItem(
modifier = Modifier
.fillMaxWidth()
.clickable {
// TODO dismiss snackbar before go to new screen if shown
navController.navigate(
Screen.AddEditNote.route + "?noteId=${note.id}&colorId=${note.color}"
)
},
note = note,
onDeleteClick = {
viewModel.onEvent(NotesEvent.DeleteNote(note))
}
)
Spacer(modifier = Modifier.height(16.dp))
}
}
}
}
}
shaarawy
06/20/2022, 6:06 PMtad
06/20/2022, 7:38 PMSharedFlow
in the model, which ensures the message is collected and displayed only once.dorche
06/20/2022, 7:46 PMtad
06/20/2022, 7:47 PMdorche
06/20/2022, 7:50 PMtad
06/20/2022, 7:51 PMdorche
06/20/2022, 7:58 PMtad
06/20/2022, 8:03 PMSnackbarHostState.showSnackbar
would not exhibit the same issue, even across recompositions.dorche
06/20/2022, 8:04 PMtad
06/20/2022, 8:04 PMdorche
06/20/2022, 8:05 PM// TODO dismiss snackbar before go to new screen if shown
tad
06/20/2022, 8:05 PMtad
06/20/2022, 8:07 PMdorche
06/20/2022, 8:13 PMtad
06/21/2022, 2:53 AMtad
06/21/2022, 3:00 AMSnackbar
if messages are just state, because the API would be 10x easier if it was just a MutableState
field. Because with the current API you have to define the clock-edges of that state as events (function calls on SnackbarHostState), instead of just calling showSnackbar
and not worrying about it. It just screams "event-handling" API to me, and it's definitely a mismatch for hoisted state in compositions.Lokik Soni
06/21/2022, 7:10 AMclass NoteViewModel @Inject constructor(
private val _getNotes: GetNotesUseCase,
private val _addNoteUseCase: AddNoteUseCase,
private val _deleteNoteUseCase: DeleteNoteUseCase
): ViewModel() {
private val _noteState = MutableStateFlow(NoteState())
val noteState = _noteState.asStateFlow()
private var _getNotesJob: Job? = null
private var _note: Note? = null
init {
// Initially loads notes with default order
getNotes()
}
/**
* Handle events of [NotesScreen].
*/
fun onEvent(notesEvent: NotesEvent) {
when(notesEvent) {
is NotesEvent.DeleteNote -> {
viewModelScope.launch {
_deleteNoteUseCase(notesEvent.note)
_noteState.update { it.copy(errorMsg = "Note ${notesEvent.note.title} deleted.") }
_note = notesEvent.note
}
}
is NotesEvent.MessageShown -> {
_noteState.update { it.copy(errorMsg = null) }
}
}
}
}
Lokik Soni
06/21/2022, 7:20 AM