<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[kotlin android]]></title><description><![CDATA[kotlin android]]></description><link>https://kotlinlang.dev</link><generator>RSS for Node</generator><lastBuildDate>Thu, 16 Apr 2026 13:56:59 GMT</lastBuildDate><atom:link href="https://kotlinlang.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Model View Intent arch in Jetpack Compose UI]]></title><description><![CDATA[Why MVI Architecture
There are already MVC, MVP and MVVM for building mobile applications.
Why MVI, what's new in that? Touted as better than MVVM, but how?
Here are a few things recommended about MVI presentational arch pattern.
Supports Unidirectio...]]></description><link>https://kotlinlang.dev/model-view-intent-arch-in-jetpack-compose-ui</link><guid isPermaLink="true">https://kotlinlang.dev/model-view-intent-arch-in-jetpack-compose-ui</guid><category><![CDATA[compose]]></category><category><![CDATA[MVI]]></category><category><![CDATA[Android]]></category><category><![CDATA[android app development]]></category><dc:creator><![CDATA[Tejaswi B]]></dc:creator><pubDate>Wed, 05 Mar 2025 17:59:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1741197416634/2e69f8c3-2393-4b46-b4cc-935e413de39c.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><strong>Why MVI Architecture</strong></p>
<p>There are already MVC, MVP and MVVM for building mobile applications.</p>
<p>Why MVI, what's new in that? Touted as better than MVVM, but how?</p>
<p>Here are a few things recommended about MVI presentational arch pattern.</p>
<p><strong>Supports Unidirectional Data Flow</strong>: due to flow of data from data layer, domain layer into UI layer.<br /><strong>Immutability</strong>: State, if immutable, helps in reducing bugs, when data is passed around.<br /><strong>Separation of Concerns</strong>: Clear separation between UI, state, and logic.<br /><strong>Reactive UI</strong>: Works well with reactive programming (e.g., Kotlin Flow, RxJava).<br /><strong>Testability</strong>: Reducers(Pure functions) and clear state transitions make testing easier.<br /><strong>Predictable State Management</strong>:All updates are handled through the state, making the flow easy to debug.</p>
<p><strong>When to Use</strong>:when Complex state management is required involving lot of data Apps with heavy user interaction &amp; those with requiring real-time updates, more significantly when state change is predictable and error free as in case of financial applications.</p>
<p>Ex: ecomm &amp; social media apps including chat screens that frequently change data &amp; needing predictable, error free state updates.</p>
<p>Let's begin:<br />MVI Architecture is a presentational architecture pattern(MVx) for building complex user interfaces.</p>
<p><strong>Key components involved</strong>:</p>
<p><strong>Essential</strong>: Intent, Model, State, ViewModel ,Action, Reducer (Simple implementations can use just these)</p>
<p><strong>Optional</strong>: Effect, and Middleware.(Advanced use cases might need these)</p>
<p>•<strong>Model</strong>: The state of the UI at any given time.</p>
<p>•<strong>View</strong>: Renders the UI based on the current state.</p>
<p>•<strong>Intent</strong>: Represents user actions (e.g., button clicks).</p>
<p>•<strong>ViewModel</strong>: Manages the state and business logic.</p>
<p>• <strong>Action</strong>: Represents actions that trigger state changes.</p>
<p>•<strong>Reducer</strong>: Pure function that takes the current state and an action to produce a new state.</p>
<p>•<strong>Effect</strong>: Side effects (e.g., network calls, animations) triggered by actions.</p>
<p>•<strong>Middleware</strong>: Intercepts actions to perform additional logic (e.g., logging, analytics).</p>
<p><strong>Code for the base viewmodel class</strong></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4f2z6d9cioa59gkl2d5.png" alt="Image description" /></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/eif6s2gt84vzobzhzua1.png" alt="Image description" /></p>
<p>Example of middleware intercepting intents:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gqq583e2fiarho4dopiu.png" alt="Image description" /></p>
<p>Here, middleware is integrated into the BaseViewModel as an open function that can be overridden by subclasses.</p>
<p><strong>Middleware</strong> can:</p>
<p>Log intents for debugging or analytics.</p>
<p>Validate or block intents based on conditions.</p>
<p>Modify intents before they are processed.</p>
<p>The processIntent function ensures that intents are passed through middleware before being handled.</p>
<p>This approach makes the BaseViewModel highly flexible and reusable for various use cases.</p>
<p>Let's get into the details:</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zzwx8z8escwrp6jyev9s.png" alt="Image description" /></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1gyp4z1evm4u09d3b3wk.png" alt="Image description" /></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lwlcna91rmm0eni4dlw1.png" alt="Image description" /></p>
<p>Let’s see how this works in a <code>ChatViewModel</code> that handles real-time updates and background tasks.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/evtledo3ghpfat7bsdx6.png" alt="Image description" /></p>
<p><em>How Dispatchers Are Used in the Example connectToChat</em>:</p>
<p>Uses runOnBackground to simulate a network call on <a target="_blank" href="http://Dispatchers.IO">Dispatchers.IO</a>.</p>
<p>Updates the state and emits effects on the main thread using updateState and emitEffect.</p>
<p>sendMessage:</p>
<p>Simulates sending a message on <a target="_blank" href="http://Dispatchers.IO">Dispatchers.IO</a>.</p>
<p>Updates the state on the main thread.</p>
<p>receiveMessage:</p>
<p>Updates the state and emits effects on the main thread.</p>
<p>Benefits of Using Dispatchers for Responsive UI:</p>
<p>Ensures that UI updates and side effects always happen on the main thread.</p>
<p><strong>Efficient Background Tasks</strong>:</p>
<p>Offloads heavy operations (e.g., network calls) to background threads.</p>
<p><strong>Thread Safety</strong>:</p>
<p>Prevents crashes or undefined behavior caused by updating the UI from a background thread.</p>
<p><em>Few things to note wrt to why StateFlow is used to represent UI State and not SharedFlow and SharedFlow for Intent, Actions and Events and StateFlow for State Why?</em></p>
<p>State represents the current UI state (e.g., loading, success, error).</p>
<p><strong>StateFlow is designed for state persistence</strong>:</p>
<p>It holds the latest value and replays it to new collectors (e.g., the UI).</p>
<p>Ensures the UI always has the latest state when it (re)subscribes (e.g., after rotation).</p>
<p>Example: If the UI is in a "loading" state, new subscribers (like a recreated Activity) immediately see the loading state.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rt4ykew9um13z9iho6bg.png" alt="Image description" /></p>
<p>SharedFlow for Events (Intents, Actions, Effects) Why?</p>
<p>Intents, Actions, and Effects represent events (e.g., button clicks, navigation, toasts).</p>
<p><strong>SharedFlow is designed for event streaming</strong>:</p>
<p>Events are short lived and should not be replayed to new collectors.</p>
<p>Avoids duplicate processing (e.g., showing a toast twice after rotation).</p>
<p>Example: A "Show Toast" effect should trigger only once, even if the UI resubscribes.</p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/c393hqfdmaeh8232x2gm.png" alt="Image description" /></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nijgciumd24azvw9ixaj.png" alt="Image description" /></p>
<p><strong>Key Differences</strong></p>
<p><img src="https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3zfrbnkuyl36pjuht35p.png" alt="Image description" /></p>
<p><strong>Why This Matters in MVI</strong> StateFlow ensures the UI always reflects the latest state (e.g., after screen rotation).</p>
<p>SharedFlow ensures events are processed exactly once (no duplicates).</p>
<p>The main purpose of this article was to provide with the MVI framework implementation:</p>
<p>**</p>
<h3 id="heading-for-code-with-a-demo-app-please-check">For code with a demo app, please check</h3>
<p>MainActivity.kt, DomainData.kt &amp; app/build.gradle.kts files from my <a target="_blank" href="https://github.com/btejaswi91/MyApplication"><strong><mark>github repo</mark></strong></a></p>
<p>**</p>
]]></content:encoded></item><item><title><![CDATA[Take Control of Linear gradient with positions array]]></title><description><![CDATA[Applying gradient through XML, has a limitation on number of colors to three, and the position of colors like startColor,centerColor and endColor, whereas with LinearGradient object applied to a shader for Paint in a canvas, we can overcome them.
Let...]]></description><link>https://kotlinlang.dev/control-linear-gradient-positions</link><guid isPermaLink="true">https://kotlinlang.dev/control-linear-gradient-positions</guid><category><![CDATA[Android]]></category><category><![CDATA[android app development]]></category><dc:creator><![CDATA[Tejaswi B]]></dc:creator><pubDate>Sat, 22 Jan 2022 19:30:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1642875481172/rwrbjovI7.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Applying gradient through XML, has a limitation on number of colors to three, and the position of colors like startColor,centerColor and endColor, whereas with LinearGradient object applied to a shader for Paint in a canvas, we can overcome them.</p>
<p>Let’s see how what can we do with it.</p>
<p>In canvas, a Paint object takes a shader/gradient for applying multiple colors [colors array]. </p>
<p>Here’s the Api of LinearGradient from android.graphics package: </p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642875481172/rwrbjovI7.png" alt="image.png" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642875575089/29Tuj9ErP.png" alt="image.png" /></p>
<p>A linear gradient object takes a nullable array.</p>
<p>The topic under discussion here is the “positions” array. </p>
<p>If one specifies null for positions, the colors are evenly distributed [linear growth of gradient] along the length of the gradient as show below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642875666440/WvSRCHREy.png" alt="even distribution.png" /></p>
<p>The above effect is achieved using 4 colors used as gradient, and with <strong>null</strong> passed as argument for positions. If you closely observe, each color has got equal portion, here 25% of length, along the  rect
Here's the code for it:</p>
<pre><code>class CustomView @JvmOverloads <span class="hljs-function"><span class="hljs-keyword">constructor</span>(<span class="hljs-params">
    context: Context, attrs: AttributeSet? = null,
    defStyleAttr: Int = <span class="hljs-number">0</span>
</span>) : <span class="hljs-title">View</span>(<span class="hljs-params">context, attrs, defStyleAttr</span>) </span>{
    <span class="hljs-keyword">private</span> val trackHeight<span class="hljs-operator">=</span>50f
    <span class="hljs-comment">// set left, top, right, bottom positions/bounds of the rectangle</span>
    <span class="hljs-keyword">private</span> val rect<span class="hljs-operator">=</span>RectF(100f,100f,600f,150f)
    <span class="hljs-keyword">private</span> val paint <span class="hljs-operator">=</span> Paint(Paint.ANTI_ALIAS_FLAG).apply {
        strokeWidth <span class="hljs-operator">=</span> trackHeight
    }
    <span class="hljs-keyword">private</span> val colorsArray <span class="hljs-operator">=</span>
        intArrayOf(Color.RED, Color.BLUE, Color.YELLOW,
            Color.parseColor(<span class="hljs-string">"#FFA500"</span>))<span class="hljs-comment">// orange color hex code</span>

    <span class="hljs-comment">// apply gradient from left to right of the rectangle along X axis , from top to bottom along Y axis</span>
    <span class="hljs-keyword">private</span> val paintShader <span class="hljs-operator">=</span> LinearGradient(
        rect.left,
        rect.top,
        rect.right,
        rect.bottom,
        colorsArray,
        null,<span class="hljs-comment">//evenly distribute colors along the whole length of gradient</span>
        Shader.TileMode.CLAMP
    )
</code></pre><p>Now, what if we need to specify which color should occupy how much portion of the available space i.e. distribution of colors? / non-linear growth of gradient?
Here's the position values array comes into play.</p>
<p>Suppose, I want red color to have 60% of length from starting X co-ordinate, then blue color to have next 10% of space from previous color's ending X co-ordinate, then yellow color to occupy next 15% of space and then Orange color to have 15% of the next space till the end, this is how you specify the positions array:</p>
<pre><code><span class="hljs-keyword">private</span> val colorsArray <span class="hljs-operator">=</span>
        intArrayOf(Color.RED, Color.BLUE, Color.YELLOW,
            Color.parseColor(<span class="hljs-string">"#FFA500"</span>))<span class="hljs-comment">// orange color hex code</span>

 <span class="hljs-keyword">private</span> val colorPositions<span class="hljs-operator">=</span> floatArrayOf(<span class="hljs-number">0</span>.6f,<span class="hljs-number">0</span>.7f,<span class="hljs-number">0</span>.85f,1f)
</code></pre><p><strong>Note</strong>: No of Colors in <strong>colorsArray</strong> and values in <strong>positions</strong> array must be of equal length, increasing in value and only even no of entries in both of them. 
[can't specify 1, or 3 or so on no of colors in odd no entries]</p>
<p>Non conforming to the above requirement throws an exception!</p>
<p>Let's pass <code>colorPositions</code> to LinearGradient as shader to paint instead of null as follows:</p>
<pre><code><span class="hljs-comment">// apply gradient from left to right of the rectangle along X axis , from top to bottom along Y axis</span>
    <span class="hljs-keyword">private</span> val paintShader <span class="hljs-operator">=</span> LinearGradient(
        rect.left,
        rect.top,
        rect.right,
        rect.bottom,
        colorsArray,
        colorPositions,<span class="hljs-comment">// distribution of colors along the length of gradient.</span>
        Shader.TileMode.CLAMP
    )
</code></pre><p>This is what you would notice:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642877640534/wVgL_o3A8.png" alt="image.png" /></p>
<p>Also, instead of using multiple colors, we can use a multiple opacity values for a single color code.</p>
<p>In this case too, if you pass null, you'd get even distribution of colors. 
But for requirement having non-linear growth of the opacity of a single color you can specify the positions array with the requirement.</p>
<p>Here's how we would do it:</p>
<pre><code><span class="hljs-comment">//pink color, also show with two colors too.</span>
    <span class="hljs-keyword">private</span> val singleColorWithVaryingOpacitiesArray <span class="hljs-operator">=</span> intArrayOf(
        Color.parseColor(<span class="hljs-string">"#00BF6793"</span>),<span class="hljs-comment">//pink with 0% opacity</span>
        Color.parseColor(<span class="hljs-string">"#CCBF6793"</span>),<span class="hljs-comment">//pink with 80% opacity</span>
        Color.parseColor(<span class="hljs-string">"#FFBF6793"</span>)<span class="hljs-comment">//pink with 100% opacity</span>
    )
    <span class="hljs-keyword">private</span> val colorPositions<span class="hljs-operator">=</span> floatArrayOf(<span class="hljs-number">0</span>.6f,<span class="hljs-number">0</span>.9f,1f)

    <span class="hljs-comment">// apply gradient from left to right of the rectangle along X axis , from top to bottom along Y axis</span>
    <span class="hljs-keyword">private</span> val paintShader <span class="hljs-operator">=</span> LinearGradient(
        rect.left,
        rect.top,
        rect.right,
        rect.bottom,
        singleColorWithVaryingOpacitiesArray,
        colorPositions,<span class="hljs-comment">// distribution of colors along the length of gradient.</span>
        Shader.TileMode.CLAMP
    )
    <span class="hljs-keyword">override</span> fun onDraw(canvas: Canvas?) {
        <span class="hljs-built_in">super</span>.onDraw(canvas)
        canvas?.drawRect(rect, paint.apply {
            color <span class="hljs-operator">=</span> Color.BLACK
        })

            canvas?.drawRect(rect, paint.apply {
            shader <span class="hljs-operator">=</span> paintShader
       <span class="hljs-comment">// Used blending with porter-duff mode so that underlying rectangle's black color is visible for </span>
      <span class="hljs-comment">//portion having for 0% opacity values</span>
            xfermode <span class="hljs-operator">=</span> PorterDuffXfermode(PorterDuff.Mode.SRC_OVER)
        })
</code></pre><p>Had we passed null, the output would be like:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642878247029/1wEcNd_yy.png" alt="image.png" />
and if we passed <strong>positions</strong> with requirement : like till 60% of space from starting X co-ordinate has 0% opacity(not visible), followed by 80% opacity till next 30% of space, and the 100% opacity(fully visible) for remaining 10% of space or till the end, the output would have been:
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1642878729096/t4wQg4xLk.png" alt="image.png" /></p>
]]></content:encoded></item></channel></rss>