{"id":214,"date":"2024-03-23T21:43:00","date_gmt":"2024-03-23T13:43:00","guid":{"rendered":"https:\/\/blog.bexonbai.com\/?p=214"},"modified":"2026-03-02T21:44:42","modified_gmt":"2026-03-02T13:44:42","slug":"a-template-for-viewmodel-flow","status":"publish","type":"post","link":"https:\/\/blog.bexonbai.com\/?p=214","title":{"rendered":"A Template for ViewModel + Flow"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Whether you\u2019re using MVI or MVVN, ViewModel is now the best way to store application-related data.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">ViewModel for Android can be implemented using the Lifecycle library and Kotlin Flow.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Related Dependencies<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n<strong>val<\/strong> lifecycleVersion = \"2.4.0\"\n<strong>implementation<\/strong>(\"androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleVersion\")\n<strong>implementation<\/strong>(\"androidx.lifecycle:lifecycle-livedata-ktx:$lifecycleVersion\")\n<strong>implementation<\/strong>(\"androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion\")\n<strong>implementation<\/strong>(\"androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion\")\n<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code><strong>import<\/strong> android.app.Application\n<strong>import<\/strong> androidx.lifecycle.AndroidViewModel\n<strong>import<\/strong> androidx.lifecycle.viewModelScope\n<strong>import<\/strong> kotlinx.coroutines.CompletableDeferred\n<strong>import<\/strong> kotlinx.coroutines.flow.MutableSharedFlow\n<strong>import<\/strong> kotlinx.coroutines.flow.MutableStateFlow\n<strong>import<\/strong> kotlinx.coroutines.flow.asSharedFlow\n<strong>import<\/strong> kotlinx.coroutines.flow.asStateFlow\n<strong>import<\/strong> kotlinx.coroutines.flow.distinctUntilChanged\n<strong>import<\/strong> kotlinx.coroutines.flow.launchIn\n<strong>import<\/strong> kotlinx.coroutines.flow.map\n<strong>import<\/strong> kotlinx.coroutines.flow.onEach\n<strong>import<\/strong> kotlinx.coroutines.flow.update\n\n<strong>abstract<\/strong> <strong>class<\/strong> <strong>BaseViewModel<\/strong>&lt;<strong>ViewState<\/strong>, <strong>ViewEvent<\/strong>&gt;(\n    initialViewState: <strong>ViewState<\/strong>,\n    application: <strong>Application<\/strong>\n) : <strong>AndroidViewModel<\/strong>(application) {\n    <strong>private<\/strong> <strong>val<\/strong> _viewStateFlow: <strong>MutableStateFlow<\/strong>&lt;<strong>ViewState<\/strong>&gt; = <strong>MutableStateFlow<\/strong>(initialViewState)\n    <strong>val<\/strong> viewStateFlow = _viewStateFlow.<strong>asStateFlow<\/strong>()\n\n    <strong>private<\/strong> <strong>val<\/strong> _viewEventFlow: <strong>MutableSharedFlow<\/strong>&lt;<strong>ViewEvent<\/strong>&gt; = <strong>MutableSharedFlow<\/strong>()\n    <strong>val<\/strong> viewEventFlow = _viewEventFlow.<strong>asSharedFlow<\/strong>()\n\n    <strong>private<\/strong> <strong>var<\/strong> viewEventFlowValve: <strong>CompletableDeferred<\/strong>&lt;<strong>Unit<\/strong>&gt; = <strong>CompletableDeferred<\/strong>()\n\n    <strong>init<\/strong> {\n        _viewEventFlow.subscriptionCount\n            .<strong>map<\/strong> { it &gt; 0 }\n            .<strong>distinctUntilChanged<\/strong>()\n            .<strong>onEach<\/strong> {\n                <strong>if<\/strong> (it) {\n                    viewEventFlowValve.<strong>complete<\/strong>(<strong>Unit<\/strong>)\n                } <strong>else<\/strong> {\n                    viewEventFlowValve = <strong>CompletableDeferred<\/strong>()\n                }\n            }\n            .<strong>launchIn<\/strong>(viewModelScope)\n    }\n\n    <strong>protected<\/strong> <strong>fun<\/strong> <strong>updateViewState<\/strong>(updateFunction: (<strong>ViewState<\/strong>) -&gt; <strong>ViewState<\/strong>) {\n        _viewStateFlow.<strong>update<\/strong> {\n            <strong>updateFunction<\/strong>(it)\n        }\n    }\n\n    <strong>protected<\/strong> <strong>suspend<\/strong> <strong>fun<\/strong> <strong>sendViewEvent<\/strong>(event: <strong>ViewEvent<\/strong>) {\n        viewEventFlowValve.<strong>await<\/strong>()\n        _viewEventFlow.<strong>emit<\/strong>(event)\n    }\n\n}<\/code><\/pre>\n","protected":false},"excerpt":{"rendered":"<p>Whether you&#8217;re using MVI or MVVN, ViewModel is now the best way to store application-related data.<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[7],"tags":[],"class_list":["post-214","post","type-post","status-publish","format-standard","hentry","category-develop"],"_links":{"self":[{"href":"https:\/\/blog.bexonbai.com\/index.php?rest_route=\/wp\/v2\/posts\/214","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.bexonbai.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.bexonbai.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.bexonbai.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.bexonbai.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=214"}],"version-history":[{"count":1,"href":"https:\/\/blog.bexonbai.com\/index.php?rest_route=\/wp\/v2\/posts\/214\/revisions"}],"predecessor-version":[{"id":215,"href":"https:\/\/blog.bexonbai.com\/index.php?rest_route=\/wp\/v2\/posts\/214\/revisions\/215"}],"wp:attachment":[{"href":"https:\/\/blog.bexonbai.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=214"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.bexonbai.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=214"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.bexonbai.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=214"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}