{"id":321350,"date":"2021-04-13T15:00:06","date_gmt":"2021-04-13T15:00:06","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=321350"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=321350","title":{"rendered":"\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f Undo \u0432 Snackbar \u043d\u0430 Jetpack Compose"},"content":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<p>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u043e\u043f\u044b\u0442 (UX \u2014 User Experience) &#8212; \u044d\u0442\u043e \u0442\u043e, \u043a\u0430\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0432\u043e\u0441\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0442 \u043f\u0440\u043e\u0434\u0443\u043a\u0442 \u0438 \u043a\u0430\u043a\u0438\u0435 \u0432\u043f\u0435\u0447\u0430\u0442\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442 \u043e\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u043d\u0438\u043c.<\/p>\n<p>\u0412 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432\u0430\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0430\u043a\u0446\u0435\u043d\u0442 \u043d\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u043c \u043e\u043f\u044b\u0442\u0435 \u0432 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f\u0445, \u043a\u043e\u0433\u0434\u0430 \u043e\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u043a\u0430\u043a\u0438\u0445-\u043b\u0438\u0431\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 (\u0438\u043b\u0438, \u043d\u0430\u043e\u0431\u043e\u0440\u043e\u0442, \u043e\u0442\u043c\u0435\u043d\u0430 \u0443\u0436\u0435 \u0441\u043e\u0432\u0435\u0440\u0448\u0451\u043d\u043d\u043e\u0433\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f). \u0415\u0441\u043b\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u044c AlertDialog \u043f\u043e \u043f\u043e\u0432\u043e\u0434\u0443 \u0438 \u0431\u0435\u0437, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u044d\u0442\u043e \u0432\u0440\u044f\u0434 \u043b\u0438 \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f.<\/p>\n<p>\u0412 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0434\u0438\u0437\u0430\u0439\u043d\u0430 \u0435\u0441\u0442\u044c <a href=\"https:\/\/material.io\/components\/snackbars\" rel=\"noopener noreferrer nofollow\">Snackbar<\/a> \u2014  \u0432\u0438\u0434\u0436\u0435\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0432 \u043d\u0438\u0436\u043d\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0427\u0435\u043c \u0445\u043e\u0440\u043e\u0448 \u0441\u043d\u044d\u043a\u0431\u0430\u0440:<\/p>\n<ul>\n<li>\n<p>\u0438\u043d\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043e \u0441\u0442\u0430\u0442\u0443\u0441\u0435 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043b\u043e \u0438\u043b\u0438 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c,<\/p>\n<\/li>\n<li>\n<p>\u043d\u0435 \u043f\u0440\u0435\u0440\u044b\u0432\u0430\u0435\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c,<\/p>\n<\/li>\n<li>\n<p>\u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u043a\u043d\u043e\u043f\u043a\u0443 \u0434\u043b\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439.<\/p>\n<\/li>\n<\/ul>\n<h3>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c Snackbar<\/h3>\n<p>\u0412 Jetpack Compose \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0441\u043d\u044d\u043a\u0431\u0430\u0440\u0430. \u0412 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043d\u0438\u0436\u0435 \u043e\u043d \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443:<\/p>\n<pre><code class=\"kotlin\">@Composable fun SnackbarSample() {     val snackbarHostState = remember { SnackbarHostState() }     val coroutineScope = rememberCoroutineScope()     val modifier = Modifier      Box(modifier.fillMaxSize()) {         Button(onClick = {             coroutineScope.launch {                 snackbarHostState.showSnackbar(message = \"This is a Snackbar\")             }         }) {             Text(text = \"Click me!\")         }          SnackbarHost(             hostState = snackbarHostState,             modifier = Modifier.align(Alignment.BottomCenter)         )     } } <\/code><\/pre>\n<p>\u0412 \u043a\u043e\u0434\u0435 \u0434\u0432\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430: <em>SnackbarHost<\/em> \u0438 <em>SnackbarHostState<\/em>. \u0418\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c, \u0441\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0438 \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0441\u043d\u044d\u043a\u0431\u0430\u0440 \u2014 \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u0433\u0430\u0439\u0434\u043b\u0430\u0439\u043d\u0430\u043c\u0438 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0434\u0438\u0437\u0430\u0439\u043d\u0430.<\/p>\n<p>\u0415\u0434\u0438\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e <a href=\"https:\/\/material.io\/components\/snackbars#usage\" rel=\"noopener noreferrer nofollow\">\u043c\u043e\u0436\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u043e\u0434\u0438\u043d \u0441\u043d\u044d\u043a\u0431\u0430\u0440<\/a> \u2014 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0436\u0434\u0430\u0442\u044c \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u0438.<\/p>\n<h3>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c Undo \u0432 Snackbar<\/h3>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043f\u0440\u0438\u043c\u0435\u0440 \u043f\u043e\u0441\u043b\u043e\u0436\u043d\u0435\u0435. \u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0441\u043d\u044d\u043a\u0431\u0430\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u043c\u0435\u043d\u044f\u0435\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0412 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0431\u0443\u0434\u0435\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u0434\u0430\u0447 \u0438 \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u0437\u0430 \u0435\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435\u043c. \u0412 \u044d\u0442\u043e\u043c \u043d\u0430\u043c \u043f\u043e\u043c\u043e\u0436\u0435\u0442 <em>ViewModel<\/em>.<\/p>\n<p>\u0423\u043f\u0440\u043e\u0449\u0451\u043d\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code class=\"kotlin\">@Composable fun HomeList(taskViewModel: ListViewModel = viewModel()) {     Scaffold {         val list by remember(taskViewModel) {             taskViewModel.taskList         }.collectAsState()          LazyColumn {             items(                 items = list,                 itemContent = { task -&gt;                     ListItem(                         task = task,                         onCheckedChange = taskViewModel::onCheckedChange                     )                 }             )         }     } }  @Composable private fun ListItem(task: Task, onCheckedChange: (Task) -&gt; Unit) {     Row(         modifier = Modifier             .fillMaxWidth()             .height(64.dp)             .padding(8.dp),         verticalAlignment = Alignment.CenterVertically     ) {         Checkbox(checked = task.isCompleted, onCheckedChange = { onCheckedChange(task) })         Spacer(Modifier.width(8.dp))         Text(text = task.title)     } }<\/code><\/pre>\n<p>\u041d\u0430\u0448\u0430 composable-\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043d\u0430 \u0432\u0445\u043e\u0434 <em>ViewModel<\/em>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439, \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u043f\u043e\u0434\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u0434\u0430\u0447 (<em>viewModel.taskList<\/em>) \u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0438\u0445 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 (<em>viewModel.onCheckedChange<\/em>).<\/p>\n<p><a href=\"https:\/\/developer.android.com\/jetpack\/compose\/layout#constraints\" rel=\"noopener noreferrer nofollow\">Scaffold<\/a> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u043b\u044d\u0439\u0430\u0443\u0442\u0430. \u0414\u0430\u043b\u0435\u0435 \u043f\u043e \u0442\u0435\u043a\u0441\u0442\u0443 \u0441\u0442\u0430\u043d\u0435\u0442 \u043f\u043e\u043d\u044f\u0442\u043d\u043e, \u043a\u0430\u043a \u043e\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u0441\u0432\u044f\u0437\u0430\u043d\u0430 \u0441\u043e \u0441\u043d\u044d\u043a\u0431\u0430\u0440\u043e\u043c. \u0410 \u043f\u043e\u043a\u0430 \u043c\u044b \u0438\u043c\u0435\u0435\u043c \u0442\u0430\u043a\u043e\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c3c\/a03\/154\/c3ca031549c4ccf79371a1aff9682069.png\" width=\"814\" height=\"874\"><figcaption><\/figcaption><\/figure>\n<p>\u041d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435 \u0431\u0438\u0437\u043d\u0435\u0441-\u043f\u0440\u0430\u0432\u0438\u043b\u043e: \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u044e\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043d\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438. \u041f\u043e\u0441\u043b\u0435 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043d\u0430 \u0447\u0435\u043a\u0431\u043e\u043a\u0441, \u0437\u0430\u0434\u0430\u0447\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0441\u0440\u0430\u0437\u0443 \u0436\u0435 \u0443\u0434\u0430\u043b\u044f\u0442\u044c\u0441\u044f \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430. \u0417\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u044d\u0442\u043e\u0439 \u043b\u043e\u0433\u0438\u043a\u0438 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 <em>ViewModel<\/em>.<\/p>\n<p>\u0410 \u0442\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043e\u0442\u043c\u0435\u043d\u044f\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435. \u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043b\u044f\u043c\u0431\u0434\u0430-\u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0432\u043c\u0435\u0441\u0442\u0435 \u0441\u043e \u0441\u043d\u044d\u043a\u0431\u0430\u0440\u043e\u043c, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u0442 \u043d\u0430 \u0447\u0435\u043a\u0431\u043e\u043a\u0441.<\/p>\n<pre><code class=\"kotlin\">@Composable fun HomeList(taskViewModel: ListViewModel = viewModel()) {     val coroutineScope = rememberCoroutineScope()     val scaffoldState = rememberScaffoldState()     val onShowSnackbar: (Task) -&gt; Unit = { task -&gt;         coroutineScope.launch {             val snackbarResult = scaffoldState.snackbarHostState.showSnackbar(                 message = \"${task.title} completed\",                 actionLabel = \"Undo\"             )             when (snackbarResult) {                 SnackbarResult.Dismissed -&gt; Timber.d(\"Snackbar dismissed\")                 SnackbarResult.ActionPerformed -&gt; taskViewModel.onCheckedChange(task)             }         }     }   ... }<\/code><\/pre>\n<p>\u0420\u0430\u0437\u0431\u0435\u0440\u0451\u043c \u043a\u043e\u0434:<\/p>\n<ul>\n<li>\n<p>\u0421\u0442\u0440\u043e\u043a\u0430 #3: \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c <em>CoroutineScope<\/em> (\u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0437\u0436\u0435 \u043f\u0440\u0438 \u043f\u043e\u043a\u0430\u0437\u0435 \u0441\u043d\u044d\u043a\u0431\u0430\u0440\u0430)<\/p>\n<\/li>\n<li>\n<p>\u0421\u0442\u0440\u043e\u043a\u0430 #4: \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c <em>ScaffoldState<\/em>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 <em>SnackbarHostState<\/em>.<\/p>\n<\/li>\n<li>\n<p>\u0421\u0442\u0440\u043e\u043a\u0430 #6: \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u043b\u044f\u043c\u0431\u0434\u0430-\u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u0442 \u043d\u0430 \u0447\u0435\u043a\u0431\u043e\u043a\u0441. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0432\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u043e\u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0443, \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u0443\u044e \u0432 \u0441\u043f\u0438\u0441\u043a\u0435.<\/p>\n<\/li>\n<li>\n<p>\u0421\u0442\u0440\u043e\u043a\u0430 #8: \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c suspend-\u0444\u0443\u043d\u043a\u0446\u0438\u044e <a href=\"https:\/\/developer.android.com\/reference\/kotlin\/androidx\/compose\/material\/SnackbarHostState#showsnackbar\" rel=\"noopener noreferrer nofollow\"><em>showSnackbar()<\/em><\/a> \u0438 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0441\u043d\u044d\u043a\u0431\u0430\u0440. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <em>SnackbarResult<\/em>, \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u043c, \u043a\u0430\u043a\u0438\u0435 \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f (\u043a\u0430\u043a \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435).<\/p>\n<\/li>\n<li>\n<p>\u0421\u0442\u0440\u043e\u043a\u0438 #12-14: \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0434\u0432\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 (<a href=\"https:\/\/developer.android.com\/reference\/kotlin\/androidx\/compose\/material\/SnackbarResult#dismissed\" rel=\"noopener noreferrer nofollow\">Dismissed<\/a> \u0438 <a href=\"https:\/\/developer.android.com\/reference\/kotlin\/androidx\/compose\/material\/SnackbarResult#actionperformed\" rel=\"noopener noreferrer nofollow\">ActionPerformed<\/a>). \u0415\u0441\u043b\u0438 \u043a\u043d\u043e\u043f\u043a\u0443 \u043d\u0435 \u043d\u0430\u0436\u0430\u043b\u0438, \u043f\u0438\u0448\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0432 \u043b\u043e\u0433. \u0415\u0441\u043b\u0438 \u043a\u043d\u043e\u043f\u043a\u0443 \u043d\u0430\u0436\u0430\u043b\u0438, <em>ViewModel<\/em> \u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 boolean-\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 <em>isCompleted<\/em> \u043d\u0430 \u043f\u0440\u043e\u0442\u0438\u0432\u043e\u043f\u043e\u043b\u043e\u0436\u043d\u043e\u0435 (\u0441 false \u043d\u0430 true). \u041a\u043e\u0433\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 isCompleted \u043e\u043f\u044f\u0442\u044c \u0431\u0443\u0434\u0435\u0442 <em>false<\/em>, \u0437\u0430\u0434\u0430\u0447\u0430 \u0432\u0435\u0440\u043d\u0451\u0442\u0441\u044f \u0432 \u0441\u043f\u0438\u0441\u043e\u043a.<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 ViewModel<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"kotlin\">data class Task(val id: Long, val title: String, var isCompleted: Boolean)  class ListViewModel : ViewModel() {      private val list = mutableListOf(         Task(1L, \"Buy milk\", false),         Task(2L, \"Watch 'Call Me By Your Name'\", false),         Task(3L, \"Listen 'Local Natives'\", false),         Task(4L, \"Study about 'fakes instead of mocks'\", false),         Task(5L, \"Congratulate Rafael\", false),         Task(6L, \"Watch Kotlin YouTube Channel\", false)     )      private val _taskList: MutableStateFlow&lt;List&lt;Task&gt;&gt; = MutableStateFlow(list)      val taskList: StateFlow&lt;List&lt;Task&gt;&gt;         get() = _taskList.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), listOf())      fun onCheckedChange(task: Task) {         list.find { it.id == task.id }?.isCompleted = task.isCompleted.not()         _taskList.value = list.filter { it.isCompleted.not() }     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u043d\u0430\u0448 \u0441\u043d\u044d\u043a\u0431\u0430\u0440 \u0441 <em>Scaffold<\/em> \u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043b\u044f\u043c\u0431\u0434\u0430-\u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 <em>onCheckedChange<\/em>:<\/p>\n<pre><code class=\"kotlin\">Scaffold(scaffoldState = scaffoldState) {     ...     ListItem(         task = task,         onCheckedChange = { task -&gt;             taskViewModel.onCheckedChange(task)             onShowSnackbar(task)         }     ) }<\/code><\/pre>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0443 <em>ScaffoldState<\/em> \u0443\u0436\u0435 \u0435\u0441\u0442\u044c <em>SnackbarHostState<\/em>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438 \u0440\u0430\u043d\u0435\u0435, \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u0435\u0433\u043e \u0432 \u0432\u0438\u0434\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0436\u0435\u043b\u0430\u0435\u043c\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/622\/53d\/762\/62253d762137945c28d849b333313778.gif\" width=\"400\" height=\"844\"><figcaption><\/figcaption><\/figure>\n<h3>\u0418 \u0435\u0449\u0451 \u043a\u043e\u0435-\u0447\u0442\u043e<\/h3>\n<p>\u0412 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u044f \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u043b\u0441\u044f \u0441 \u0442\u0435\u043c, \u0447\u0442\u043e \u043f\u043e\u0441\u043b\u0435 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043d\u0430 \u0447\u0435\u043a\u0431\u043e\u043a\u0441 \u0441\u043d\u044d\u043a\u0431\u0430\u0440 \u043f\u043e\u044f\u0432\u043b\u044f\u043b\u0441\u044f \u0438 \u0442\u0443\u0442 \u0436\u0435 \u0438\u0441\u0447\u0435\u0437\u0430\u043b. \u0420\u0435\u0448\u0438\u0442\u044c \u0435\u0451 \u043c\u043d\u0435 \u043f\u043e\u043c\u043e\u0433 \u0410\u0434\u0430\u043c \u041f\u0430\u0443\u044d\u043b\u043b \u043d\u0430 <a href=\"https:\/\/kotlinlang.slack.com\/archives\/CJLTWPH7S\/p1617023013323700\" rel=\"noopener noreferrer nofollow\">Kotlinlang \u0432 Slack<\/a>.<\/p>\n<p>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0437\u0430\u043a\u043b\u044e\u0447\u0430\u043b\u0430\u0441\u044c \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c: \u0441\u043d\u044d\u043a\u0431\u0430\u0440 \u0443\u0434\u0430\u043b\u044f\u043b\u0441\u044f \u0438\u0437 \u043e\u0447\u0435\u0440\u0435\u0434\u0438, \u043a\u043e\u0433\u0434\u0430 \u0432\u044b\u0437\u043e\u0432 <em>showSnackbar<\/em> \u043e\u0442\u043c\u0435\u043d\u044f\u043b\u0441\u044f. \u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u044f \u043e\u0431\u044a\u044f\u0432\u043b\u044f\u043b <em>rememberCoroutineScope<\/em> \u0432 compose-\u0444\u0443\u043d\u043a\u0446\u0438\u0438 <em>ListItem<\/em>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043b\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0441\u043f\u0438\u0441\u043a\u0430 \u0437\u0430\u0434\u0430\u0447. \u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u0440\u0435\u0448\u0438\u043b \u043f\u0435\u0440\u0435\u043d\u043e\u0441 \u0435\u0451 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u044b\u0448\u0435 <em>Scaffold<\/em>.<\/p>\n<h3>\u0427\u0442\u043e \u0434\u0430\u043b\u044c\u0448\u0435?<\/h3>\n<p>\u041f\u043e\u043b\u043d\u044b\u0439 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u0434\u043b\u044f \u044d\u0442\u043e \u0441\u0442\u0430\u0442\u044c\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432 \u044d\u0442\u043e\u043c <a href=\"https:\/\/gist.github.com\/igorescodro\/c1188f7c2dd0ee15e28d38759ec3f0bd\" rel=\"noopener noreferrer nofollow\">gist-\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438<\/a> (\u0438 \u0432 \u043a\u043e\u043d\u0446\u0435 \u0441\u0442\u0430\u0442\u044c\u0438). \u041d\u0435 \u043f\u0438\u043d\u0430\u0439\u0442\u0435 \u0441\u0438\u043b\u044c\u043d\u043e \u0437\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e <em>ViewModel<\/em>. \u041e\u043d\u0430 \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u043d\u0430, \u0434\u0430, \u0438 \u043e\u043d\u0430 \u0437\u0434\u0435\u0441\u044c \u043b\u0438\u0448\u044c \u0434\u043b\u044f \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438 &#171;\u0436\u0438\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445&#187;. \u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u0441\u0442\u0443\u043f\u0430\u0442\u044c, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0438\u0437 <em>Flow<\/em>, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u0441 <em>Room<\/em>.<\/p>\n<p>\u0414\u043b\u044f \u0441\u0432\u043e\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e Undo \u0432 \u0441\u043d\u044d\u043a\u0431\u0430\u0440\u0435 \u044f \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u0432 <a href=\"https:\/\/github.com\/igorescodro\/alkaa\/pull\/160\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u043e\u043c \u043f\u0443\u043b-\u0440\u0435\u043a\u0432\u0435\u0441\u0442\u0435<\/a>. \u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u0436\u0435\u043b\u0430\u043d\u0438\u0435, \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435.<\/p>\n<p>\u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u044d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0432\u0430\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0442\u043c\u0435\u043d\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0432 \u0441\u043d\u044d\u043a\u0431\u0430\u0440\u0430\u0445. \u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0430 \u0410\u0434\u0430\u043c\u0443 \u041f\u0430\u0443\u044d\u043b\u043b\u0443 \u0437\u0430 \u043f\u043e\u043c\u043e\u0449\u044c \u0441 \u043a\u043e\u0440\u0443\u0442\u0438\u043d\u0430\u043c\u0438.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u043b\u043d\u044b\u0439 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"kotlin\">@Composable fun HomeList(taskViewModel: ListViewModel = viewModel()) {     val coroutineScope = rememberCoroutineScope()     val scaffoldState = rememberScaffoldState()      val onShowSnackbar: (Task) -&gt; Unit = { task -&gt;         coroutineScope.launch {             val snackbarResult = scaffoldState.snackbarHostState.showSnackbar(                 message = \"${task.title} completed\",                 actionLabel = \"Undo\"             )             when (snackbarResult) {                 SnackbarResult.Dismissed -&gt; Timber.d(\"Snackbar dismissed\")                 SnackbarResult.ActionPerformed -&gt; taskViewModel.onCheckedChange(task)             }         }     }      Scaffold(         scaffoldState = scaffoldState,         topBar = { HomeTopBar() }     ) {         val list by remember(taskViewModel) { taskViewModel.taskList }.collectAsState()          LazyColumn {             items(                 items = list,                 key = { it.id },                 itemContent = { task -&gt;                     ListItem(                         task = task,                         onCheckedChange = { task -&gt;                             taskViewModel.onCheckedChange(task)                             onShowSnackbar(task)                         }                     )                 }             )         }     } }  @Composable private fun ListItem(task: Task, onCheckedChange: (Task) -&gt; Unit) {     Row(         modifier = Modifier             .fillMaxWidth()             .height(64.dp)             .padding(8.dp),         verticalAlignment = Alignment.CenterVertically     ) {         Checkbox(             checked = task.isCompleted,             onCheckedChange = { onCheckedChange(task) }         )         Spacer(Modifier.width(8.dp))         Text(             text = task.title,             style = MaterialTheme.typography.body1,             overflow = TextOverflow.Ellipsis,             maxLines = 1         )     } }  @Composable private fun HomeTopBar() {     TopAppBar {         Box(modifier = Modifier.fillMaxSize()) {             Text(                 modifier = Modifier.align(Alignment.Center),                 style = MaterialTheme.typography.h5,                 text = \"My tasks\"             )         }     } }  data class Task(val id: Long, val title: String, var isCompleted: Boolean)  class ListViewModel : ViewModel() {      private val list = mutableListOf(         Task(1L, \"Buy milk\", false),         Task(2L, \"Watch 'Call Me By Your Name'\", false),         Task(3L, \"Listen 'Local Natives'\", false),         Task(4L, \"Study about 'fakes instead of mocks'\", false),         Task(5L, \"Congratulate Rafael\", false),         Task(6L, \"Watch Kotlin YouTube Channel\", false)     )      private val _taskList: MutableStateFlow&lt;List&lt;Task&gt;&gt; = MutableStateFlow(list)      val taskList: StateFlow&lt;List&lt;Task&gt;&gt;         get() = _taskList.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), listOf())      fun onCheckedChange(task: Task) {         list.find { it.id == task.id }?.isCompleted = task.isCompleted.not()         _taskList.value = list.filter { it.isCompleted.not() }     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<blockquote>\n<p>\u041e\u0442 \u043f\u0435\u0440\u0435\u0432\u043e\u0434\u0447\u0438\u043a\u0430: \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0438 \u0438 \u043f\u0440\u0430\u0432\u043a\u0438 \u043f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0442\u0441\u044f.<\/p>\n<\/blockquote>\n<\/div>\n<p> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/post\/551856\/\"> https:\/\/habr.com\/ru\/post\/551856\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"\n<div class=\"post__text post__text_v2\" id=\"post-content-body\">\n<p>\u041f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u0438\u0439 \u043e\u043f\u044b\u0442 (UX \u2014 User Experience) &#8212; \u044d\u0442\u043e \u0442\u043e, \u043a\u0430\u043a \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u0438 \u0432\u043e\u0441\u043f\u0440\u0438\u043d\u0438\u043c\u0430\u044e\u0442 \u043f\u0440\u043e\u0434\u0443\u043a\u0442 \u0438 \u043a\u0430\u043a\u0438\u0435 \u0432\u043f\u0435\u0447\u0430\u0442\u043b\u0435\u043d\u0438\u044f \u043f\u043e\u043b\u0443\u0447\u0430\u044e\u0442 \u043e\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f \u0441 \u043d\u0438\u043c.<\/p>\n<p>\u0412 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0432\u0430\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0430\u043a\u0446\u0435\u043d\u0442 \u043d\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u0441\u043a\u043e\u043c \u043e\u043f\u044b\u0442\u0435 \u0432 \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u044f\u0445, \u043a\u043e\u0433\u0434\u0430 \u043e\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043d\u0438\u0435 \u043a\u0430\u043a\u0438\u0445-\u043b\u0438\u0431\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 (\u0438\u043b\u0438, \u043d\u0430\u043e\u0431\u043e\u0440\u043e\u0442, \u043e\u0442\u043c\u0435\u043d\u0430 \u0443\u0436\u0435 \u0441\u043e\u0432\u0435\u0440\u0448\u0451\u043d\u043d\u043e\u0433\u043e \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f). \u0415\u0441\u043b\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0432\u043e\u0434\u0438\u0442\u044c AlertDialog \u043f\u043e \u043f\u043e\u0432\u043e\u0434\u0443 \u0438 \u0431\u0435\u0437, \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u044d\u0442\u043e \u0432\u0440\u044f\u0434 \u043b\u0438 \u043f\u043e\u043d\u0440\u0430\u0432\u0438\u0442\u0441\u044f.<\/p>\n<p>\u0412 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u043e\u0432 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0434\u0438\u0437\u0430\u0439\u043d\u0430 \u0435\u0441\u0442\u044c <a href=\"https:\/\/material.io\/components\/snackbars\" rel=\"noopener noreferrer nofollow\">Snackbar<\/a> \u2014  \u0432\u0438\u0434\u0436\u0435\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0439 \u0432 \u043d\u0438\u0436\u043d\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f.<\/p>\n<p>\u0427\u0435\u043c \u0445\u043e\u0440\u043e\u0448 \u0441\u043d\u044d\u043a\u0431\u0430\u0440:<\/p>\n<ul>\n<li>\n<p>\u0438\u043d\u0444\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u043e \u0441\u0442\u0430\u0442\u0443\u0441\u0435 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u043b\u043e \u0438\u043b\u0438 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u0442\u044c,<\/p>\n<\/li>\n<li>\n<p>\u043d\u0435 \u043f\u0440\u0435\u0440\u044b\u0432\u0430\u0435\u0442 \u0432\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f \u0441 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c,<\/p>\n<\/li>\n<li>\n<p>\u043c\u043e\u0436\u0435\u0442 \u0441\u043e\u0434\u0435\u0440\u0436\u0430\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u043a\u043d\u043e\u043f\u043a\u0443 \u0434\u043b\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439.<\/p>\n<\/li>\n<\/ul>\n<h3>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c Snackbar<\/h3>\n<p>\u0412 Jetpack Compose \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0441\u043d\u044d\u043a\u0431\u0430\u0440\u0430. \u0412 \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u043d\u0438\u0436\u0435 \u043e\u043d \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0435\u0442\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443:<\/p>\n<pre><code class=\"kotlin\">@Composable fun SnackbarSample() {     val snackbarHostState = remember { SnackbarHostState() }     val coroutineScope = rememberCoroutineScope()     val modifier = Modifier      Box(modifier.fillMaxSize()) {         Button(onClick = {             coroutineScope.launch {                 snackbarHostState.showSnackbar(message = \"This is a Snackbar\")             }         }) {             Text(text = \"Click me!\")         }          SnackbarHost(             hostState = snackbarHostState,             modifier = Modifier.align(Alignment.BottomCenter)         )     } } <\/code><\/pre>\n<p>\u0412 \u043a\u043e\u0434\u0435 \u0434\u0432\u0430 \u043e\u0441\u043d\u043e\u0432\u043d\u044b\u0445 \u043a\u043e\u043c\u043f\u043e\u043d\u0435\u043d\u0442\u0430: <em>SnackbarHost<\/em> \u0438 <em>SnackbarHostState<\/em>. \u0418\u0445 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c, \u0441\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0438 \u0437\u0430\u043a\u0440\u044b\u0432\u0430\u0442\u044c \u0441\u043d\u044d\u043a\u0431\u0430\u0440 \u2014 \u0432 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441 \u0433\u0430\u0439\u0434\u043b\u0430\u0439\u043d\u0430\u043c\u0438 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0434\u0438\u0437\u0430\u0439\u043d\u0430.<\/p>\n<p>\u0415\u0434\u0438\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e <a href=\"https:\/\/material.io\/components\/snackbars#usage\" rel=\"noopener noreferrer nofollow\">\u043c\u043e\u0436\u0435\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u043e\u0434\u0438\u043d \u0441\u043d\u044d\u043a\u0431\u0430\u0440<\/a> \u2014 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0436\u0434\u0430\u0442\u044c \u0432 \u043e\u0447\u0435\u0440\u0435\u0434\u0438.<\/p>\n<h3>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c Undo \u0432 Snackbar<\/h3>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043f\u0440\u0438\u043c\u0435\u0440 \u043f\u043e\u0441\u043b\u043e\u0436\u043d\u0435\u0435. \u0420\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0441\u043d\u044d\u043a\u0431\u0430\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0442\u043c\u0435\u043d\u044f\u0435\u0442 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044f. \u0412 \u043d\u0430\u0448\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u0440\u0435 \u0431\u0443\u0434\u0435\u043c \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u0434\u0430\u0447 \u0438 \u0441\u043b\u0435\u0434\u0438\u0442\u044c \u0437\u0430 \u0435\u0433\u043e \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0435\u043c. \u0412 \u044d\u0442\u043e\u043c \u043d\u0430\u043c \u043f\u043e\u043c\u043e\u0436\u0435\u0442 <em>ViewModel<\/em>.<\/p>\n<p>\u0423\u043f\u0440\u043e\u0449\u0451\u043d\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440:<\/p>\n<pre><code class=\"kotlin\">@Composable fun HomeList(taskViewModel: ListViewModel = viewModel()) {     Scaffold {         val list by remember(taskViewModel) {             taskViewModel.taskList         }.collectAsState()          LazyColumn {             items(                 items = list,                 itemContent = { task -&gt;                     ListItem(                         task = task,                         onCheckedChange = taskViewModel::onCheckedChange                     )                 }             )         }     } }  @Composable private fun ListItem(task: Task, onCheckedChange: (Task) -&gt; Unit) {     Row(         modifier = Modifier             .fillMaxWidth()             .height(64.dp)             .padding(8.dp),         verticalAlignment = Alignment.CenterVertically     ) {         Checkbox(checked = task.isCompleted, onCheckedChange = { onCheckedChange(task) })         Spacer(Modifier.width(8.dp))         Text(text = task.title)     } }<\/code><\/pre>\n<p>\u041d\u0430\u0448\u0430 composable-\u0444\u0443\u043d\u043a\u0446\u0438\u044f \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u043d\u0430 \u0432\u0445\u043e\u0434 <em>ViewModel<\/em>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439, \u0432 \u0441\u0432\u043e\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c, \u043f\u043e\u0434\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0437\u0430\u0434\u0430\u0447 (<em>viewModel.taskList<\/em>) \u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u0442 \u0444\u0443\u043d\u043a\u0446\u0438\u044e \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0438 \u0438\u0445 \u0441\u0442\u0430\u0442\u0443\u0441\u0430 (<em>viewModel.onCheckedChange<\/em>).<\/p>\n<p><a href=\"https:\/\/developer.android.com\/jetpack\/compose\/layout#constraints\" rel=\"noopener noreferrer nofollow\">Scaffold<\/a> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f \u043f\u043e\u0441\u0442\u0440\u043e\u0435\u043d\u0438\u044f \u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u043b\u044d\u0439\u0430\u0443\u0442\u0430. \u0414\u0430\u043b\u0435\u0435 \u043f\u043e \u0442\u0435\u043a\u0441\u0442\u0443 \u0441\u0442\u0430\u043d\u0435\u0442 \u043f\u043e\u043d\u044f\u0442\u043d\u043e, \u043a\u0430\u043a \u043e\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u0441\u0432\u044f\u0437\u0430\u043d\u0430 \u0441\u043e \u0441\u043d\u044d\u043a\u0431\u0430\u0440\u043e\u043c. \u0410 \u043f\u043e\u043a\u0430 \u043c\u044b \u0438\u043c\u0435\u0435\u043c \u0442\u0430\u043a\u043e\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u041d\u0430\u0448\u0435 \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u0434\u043e\u043b\u0436\u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u0435 \u0431\u0438\u0437\u043d\u0435\u0441-\u043f\u0440\u0430\u0432\u0438\u043b\u043e: \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u044e\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0435\u0437\u0430\u0432\u0435\u0440\u0448\u0451\u043d\u043d\u044b\u0435 \u0437\u0430\u0434\u0430\u0447\u0438. \u041f\u043e\u0441\u043b\u0435 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043d\u0430 \u0447\u0435\u043a\u0431\u043e\u043a\u0441, \u0437\u0430\u0434\u0430\u0447\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0441\u0440\u0430\u0437\u0443 \u0436\u0435 \u0443\u0434\u0430\u043b\u044f\u0442\u044c\u0441\u044f \u0438\u0437 \u0441\u043f\u0438\u0441\u043a\u0430. \u0417\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u044d\u0442\u043e\u0439 \u043b\u043e\u0433\u0438\u043a\u0438 \u043e\u0442\u0432\u0435\u0447\u0430\u0435\u0442 <em>ViewModel<\/em>.<\/p>\n<p>\u0410 \u0442\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u043e\u043d\u0430\u043b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044e \u043e\u0442\u043c\u0435\u043d\u044f\u0442\u044c \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435. \u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043b\u044f\u043c\u0431\u0434\u0430-\u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0432\u043c\u0435\u0441\u0442\u0435 \u0441\u043e \u0441\u043d\u044d\u043a\u0431\u0430\u0440\u043e\u043c, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u0442 \u043d\u0430 \u0447\u0435\u043a\u0431\u043e\u043a\u0441.<\/p>\n<pre><code class=\"kotlin\">@Composable fun HomeList(taskViewModel: ListViewModel = viewModel()) {     val coroutineScope = rememberCoroutineScope()     val scaffoldState = rememberScaffoldState()     val onShowSnackbar: (Task) -&gt; Unit = { task -&gt;         coroutineScope.launch {             val snackbarResult = scaffoldState.snackbarHostState.showSnackbar(                 message = \"${task.title} completed\",                 actionLabel = \"Undo\"             )             when (snackbarResult) {                 SnackbarResult.Dismissed -&gt; Timber.d(\"Snackbar dismissed\")                 SnackbarResult.ActionPerformed -&gt; taskViewModel.onCheckedChange(task)             }         }     }   ... }<\/code><\/pre>\n<p>\u0420\u0430\u0437\u0431\u0435\u0440\u0451\u043c \u043a\u043e\u0434:<\/p>\n<ul>\n<li>\n<p>\u0421\u0442\u0440\u043e\u043a\u0430 #3: \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c <em>CoroutineScope<\/em> (\u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u043f\u043e\u0437\u0436\u0435 \u043f\u0440\u0438 \u043f\u043e\u043a\u0430\u0437\u0435 \u0441\u043d\u044d\u043a\u0431\u0430\u0440\u0430)<\/p>\n<\/li>\n<li>\n<p>\u0421\u0442\u0440\u043e\u043a\u0430 #4: \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c <em>ScaffoldState<\/em>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 \u043d\u0430\u0441\u0442\u0440\u043e\u0435\u043d\u043d\u044b\u0439 <em>SnackbarHostState<\/em>.<\/p>\n<\/li>\n<li>\n<p>\u0421\u0442\u0440\u043e\u043a\u0430 #6: \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c \u043b\u044f\u043c\u0431\u0434\u0430-\u0444\u0443\u043d\u043a\u0446\u0438\u044e, \u043a\u043e\u0433\u0434\u0430 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0430\u0436\u0438\u043c\u0430\u0435\u0442 \u043d\u0430 \u0447\u0435\u043a\u0431\u043e\u043a\u0441. \u0412 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0432\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u043e\u043d\u0430 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u0442 \u0437\u0430\u0434\u0430\u0447\u0443, \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u0443\u044e \u0432 \u0441\u043f\u0438\u0441\u043a\u0435.<\/p>\n<\/li>\n<li>\n<p>\u0421\u0442\u0440\u043e\u043a\u0430 #8: \u0432\u044b\u0437\u044b\u0432\u0430\u0435\u043c suspend-\u0444\u0443\u043d\u043a\u0446\u0438\u044e <a href=\"https:\/\/developer.android.com\/reference\/kotlin\/androidx\/compose\/material\/SnackbarHostState#showsnackbar\" rel=\"noopener noreferrer nofollow\"><em>showSnackbar()<\/em><\/a> \u0438 \u043f\u043e\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0441\u043d\u044d\u043a\u0431\u0430\u0440. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f <em>SnackbarResult<\/em>, \u043f\u043e\u043d\u0438\u043c\u0430\u0435\u043c, \u043a\u0430\u043a\u0438\u0435 \u043f\u0440\u043e\u0438\u0437\u043e\u0448\u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f (\u043a\u0430\u043a \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u043e\u0441\u044c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435).<\/p>\n<\/li>\n<li>\n<p>\u0421\u0442\u0440\u043e\u043a\u0438 #12-14: \u043e\u0431\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u0435\u043c \u0434\u0432\u0430 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u044b\u0445 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 (<a href=\"https:\/\/developer.android.com\/reference\/kotlin\/androidx\/compose\/material\/SnackbarResult#dismissed\" rel=\"noopener noreferrer nofollow\">Dismissed<\/a> \u0438 <a href=\"https:\/\/developer.android.com\/reference\/kotlin\/androidx\/compose\/material\/SnackbarResult#actionperformed\" rel=\"noopener noreferrer nofollow\">ActionPerformed<\/a>). \u0415\u0441\u043b\u0438 \u043a\u043d\u043e\u043f\u043a\u0443 \u043d\u0435 \u043d\u0430\u0436\u0430\u043b\u0438, \u043f\u0438\u0448\u0435\u043c \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435 \u0432 \u043b\u043e\u0433. \u0415\u0441\u043b\u0438 \u043a\u043d\u043e\u043f\u043a\u0443 \u043d\u0430\u0436\u0430\u043b\u0438, <em>ViewModel<\/em> \u043c\u0435\u043d\u044f\u0435\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 boolean-\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 <em>isCompleted<\/em> \u043d\u0430 \u043f\u0440\u043e\u0442\u0438\u0432\u043e\u043f\u043e\u043b\u043e\u0436\u043d\u043e\u0435 (\u0441 false \u043d\u0430 true). \u041a\u043e\u0433\u0434\u0430 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 isCompleted \u043e\u043f\u044f\u0442\u044c \u0431\u0443\u0434\u0435\u0442 <em>false<\/em>, \u0437\u0430\u0434\u0430\u0447\u0430 \u0432\u0435\u0440\u043d\u0451\u0442\u0441\u044f \u0432 \u0441\u043f\u0438\u0441\u043e\u043a.<\/p>\n<\/li>\n<\/ul>\n<details class=\"spoiler\">\n<summary>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 ViewModel<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"kotlin\">data class Task(val id: Long, val title: String, var isCompleted: Boolean)  class ListViewModel : ViewModel() {      private val list = mutableListOf(         Task(1L, \"Buy milk\", false),         Task(2L, \"Watch 'Call Me By Your Name'\", false),         Task(3L, \"Listen 'Local Natives'\", false),         Task(4L, \"Study about 'fakes instead of mocks'\", false),         Task(5L, \"Congratulate Rafael\", false),         Task(6L, \"Watch Kotlin YouTube Channel\", false)     )      private val _taskList: MutableStateFlow&lt;List&lt;Task&gt;&gt; = MutableStateFlow(list)      val taskList: StateFlow&lt;List&lt;Task&gt;&gt;         get() = _taskList.stateIn(viewModelScope, SharingStarted.WhileSubscribed(), listOf())      fun onCheckedChange(task: Task) {         list.find { it.id == task.id }?.isCompleted = task.isCompleted.not()         _taskList.value = list.filter { it.isCompleted.not() }     } }<\/code><\/pre>\n<\/div>\n<\/details>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0441\u0432\u044f\u0437\u0430\u0442\u044c \u043d\u0430\u0448 \u0441\u043d\u044d\u043a\u0431\u0430\u0440 \u0441 <em>Scaffold<\/em> \u0438 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u043b\u044f\u043c\u0431\u0434\u0430-\u0444\u0443\u043d\u043a\u0446\u0438\u044e \u0432 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 <em>onCheckedChange<\/em>:<\/p>\n<pre><code class=\"kotlin\">Scaffold(scaffoldState = scaffoldState) {     ...     ListItem(         task = task,         onCheckedChange = { task -&gt;             taskViewModel.onCheckedChange(task)             onShowSnackbar(task)         }     ) }<\/code><\/pre>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0443 <em>ScaffoldState<\/em> \u0443\u0436\u0435 \u0435\u0441\u0442\u044c <em>SnackbarHostState<\/em>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u043b\u0438 \u0440\u0430\u043d\u0435\u0435, \u043c\u044b \u043f\u0440\u043e\u0441\u0442\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0451\u043c \u0435\u0433\u043e \u0432 \u0432\u0438\u0434\u0435 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430 \u0438 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0436\u0435\u043b\u0430\u0435\u043c\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<figure class=\"\"><figcaption><\/figcaption><\/figure>\n<h3>\u0418 \u0435\u0449\u0451 \u043a\u043e\u0435-\u0447\u0442\u043e<\/h3>\n<p>\u0412 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0435 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u044f \u0441\u0442\u043e\u043b\u043a\u043d\u0443\u043b\u0441\u044f \u0441 \u0442\u0435\u043c, \u0447\u0442\u043e \u043f\u043e\u0441\u043b\u0435 \u043d\u0430\u0436\u0430\u0442\u0438\u044f \u043d\u0430 \u0447\u0435\u043a\u0431\u043e\u043a\u0441 \u0441\u043d\u044d\u043a\u0431\u0430\u0440 \u043f\u043e\u044f\u0432\u043b\u044f\u043b\u0441\u044f \u0438 \u0442\u0443\u0442 \u0436\u0435 \u0438\u0441\u0447\u0435\u0437\u0430\u043b. \u0420\u0435\u0448\u0438\u0442\u044c \u0435\u0451 \u043c\u043d\u0435 \u043f\u043e\u043c\u043e\u0433 \u0410\u0434\u0430\u043c \u041f\u0430\u0443\u044d\u043b\u043b \u043d\u0430 <a href=\"https:\/\/kotlinlang.slack.com\/archives\/CJLTWPH7S\/p1617023013323700\" rel=\"noopener noreferrer nofollow\">Kotlinlang \u0432 Slack<\/a>.<\/p>\n<p>\u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0430 \u0437\u0430\u043a\u043b\u044e\u0447\u0430\u043b\u0430\u0441\u044c \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c: \u0441\u043d\u044d\u043a\u0431\u0430\u0440 \u0443\u0434\u0430\u043b\u044f\u043b\u0441\u044f \u0438\u0437 \u043e\u0447\u0435\u0440\u0435\u0434\u0438, \u043a\u043e\u0433\u0434\u0430 \u0432\u044b\u0437\u043e\u0432 <em>showSnackbar<\/em> \u043e\u0442\u043c\u0435\u043d\u044f\u043b\u0441\u044f. \u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u044f \u043e\u0431\u044a\u044f\u0432\u043b\u044f\u043b <em>rememberCoroutineScope<\/em> \u0432 compose-\u0444\u0443\u043d\u043a\u0446\u0438\u0438 <em>ListItem<\/em>, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0444\u043e\u0440\u043c\u0438\u0440\u043e\u0432\u0430\u043b\u0430 \u044d\u043b\u0435\u043c\u0435\u043d\u0442\u044b \u0441\u043f\u0438\u0441\u043a\u0430 \u0437\u0430\u0434\u0430\u0447. \u041f\u0440\u043e\u0431\u043b\u0435\u043c\u0443 \u0440\u0435\u0448\u0438\u043b \u043f\u0435\u0440\u0435\u043d\u043e\u0441 \u0435\u0451 \u043e\u0431\u044a\u044f\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u044b\u0448\u0435 <em>Scaffold<\/em>.<\/p>\n<h3>\u0427\u0442\u043e \u0434\u0430\u043b\u044c\u0448\u0435?<\/h3>\n<p>\u041f\u043e\u043b\u043d\u044b\u0439 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434 \u0434\u043b\u044f \u044d\u0442\u043e \u0441\u0442\u0430\u0442\u044c\u0438 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432 \u044d\u0442\u043e\u043c <a href=\"https:\/\/gist.github.com\/igorescodro\/c1188f7c2dd0ee15e28d38759ec3f0bd\" rel=\"noopener noreferrer nofollow\">gist-\u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u0438<\/a> (\u0438 \u0432 \u043a\u043e\u043d\u0446\u0435 \u0441\u0442\u0430\u0442\u044c\u0438). \u041d\u0435 \u043f\u0438\u043d\u0430\u0439\u0442\u0435 \u0441\u0438\u043b\u044c\u043d\u043e \u0437\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e <em>ViewModel<\/em>. \u041e\u043d\u0430 \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432\u043d\u0430, \u0434\u0430, \u0438 \u043e\u043d\u0430 \u0437\u0434\u0435\u0441\u044c \u043b\u0438\u0448\u044c \u0434\u043b\u044f \u0438\u043c\u0438\u0442\u0430\u0446\u0438\u0438 &#171;\u0436\u0438\u0432\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445&#187;. \u0412 \u0440\u0435\u0430\u043b\u044c\u043d\u043e\u043c \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043f\u043e\u0441\u0442\u0443\u043f\u0430\u0442\u044c, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0438\u0437 <em>Flow<\/em>, \u0441\u0432\u044f\u0437\u0430\u043d\u043d\u043e\u0433\u043e \u0441 <em>Room<\/em>.<\/p>\n<p>\u0414\u043b\u044f \u0441\u0432\u043e\u0435\u0433\u043e \u043f\u0440\u0438\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e Undo \u0432 \u0441\u043d\u044d\u043a\u0431\u0430\u0440\u0435 \u044f \u0434\u043e\u0431\u0430\u0432\u0438\u043b \u0432 <a href=\"https:\/\/github.com\/igorescodro\/alkaa\/pull\/160\" rel=\"noopener noreferrer nofollow\">\u044d\u0442\u043e\u043c \u043f\u0443\u043b-\u0440\u0435\u043a\u0432\u0435\u0441\u0442\u0435<\/a>. \u0415\u0441\u043b\u0438 \u0435\u0441\u0442\u044c \u0436\u0435\u043b\u0430\u043d\u0438\u0435, \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435.<\/p>\n<p>\u041d\u0430\u0434\u0435\u044e\u0441\u044c, \u044d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u043f\u043e\u043c\u043e\u0436\u0435\u0442 \u0432\u0430\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u0432\u043e\u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u043e\u0442\u043c\u0435\u043d\u044b \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0439 \u0432 \u0441\u043d\u044d\u043a\u0431\u0430\u0440\u0430\u0445. \u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435, \u0430 \u0410\u0434\u0430\u043c\u0443 \u041f\u0430\u0443\u044d\u043b\u043b\u0443 \u0437\u0430 \u043f\u043e\u043c\u043e\u0449\u044c \u0441 \u043a\u043e\u0440\u0443\u0442\u0438\u043d\u0430\u043c\u0438.<\/p>\n<details class=\"spoiler\">\n<summary>\u041f\u043e\u043b\u043d\u044b\u0439 \u0438\u0441\u0445\u043e\u0434\u043d\u044b\u0439 \u043a\u043e\u0434<\/summary>\n<div class=\"spoiler__content\">\n<pre><code class=\"kotlin\">@Composable fun HomeList(taskViewModel: ListViewModel = viewModel()) {     val coroutineScope = rememberCoroutineScope()     val scaffoldState = rememberScaffoldState()      val onShowSnackbar: (Task) -&gt; Unit = { task -&gt;         coroutineScope.launch {             val snackbarResult = scaffoldState.snackbarHostState.showSnackbar(                 message = \"${task.title} completed\",                 actionLabel = \"Undo\"             )             when (snackbarResult) {                 SnackbarResult.Dismissed -&gt; Timber.d(\"Snackbar dismissed\")                 SnackbarResult.ActionPerformed -&gt; taskViewModel.onCheckedChange(task)             }         }     }      Scaffold(         scaffoldState = scaffoldState,         topBar = { HomeTopBar() }     ) {         val list by remember(taskViewModel) { taskViewModel.taskList }.collectAsState()          LazyColumn {             items(                 items = list,                 key = { it.id },                 itemContent = { task -&gt;                     ListItem(                         task = task,                         onCheckedChange = { task -&gt;                             taskViewModel.onCheckedChange(task)                             onShowSnackbar(task)                         }                     )                 }             )         }     } }  @Composable private fun ListItem(task: Task, onCheckedChange: (Task) -&gt; Unit) {     Row(         modifier = Modifier             .fillMaxWidth()             .height(64.dp)             .padding(8.dp),         verticalAlignment = Alignment.CenterVertically     ) {         Checkbox(             checked = task.isCompleted,             onCheckedChange = { onCheckedChange(task) }         )         Spacer(Modifier.width(8.dp))         Text(             text = task.title,             style = MaterialTheme.typography.body1,             overflow = TextOverflow.Ellipsis,             maxLines = 1         )     } }  @Composable private fun HomeTopBar() {     TopAppBar {         Box(modifier = Modifier.fillMaxSize()) {             Text(                 modifier = Modifier.align(Alignment.Center),                 style = MaterialTheme.typography.h5,                 text = \"My tasks\"             )         }     } }  data class Task(val id: Long, val title: String, var isCompleted: Boolean)  class<\/code><\/pre>\n<\/div>\n<\/details>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-321350","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/321350","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=321350"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/321350\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=321350"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=321350"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=321350"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}