<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Aalaa Fahiem </title>
    <description>The latest articles on DEV Community by Aalaa Fahiem  (@itsaalaa7).</description>
    <link>https://dev.to/itsaalaa7</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3859289%2Fae7b77d0-955c-4c2f-b8d1-43ddb3012a83.png</url>
      <title>DEV Community: Aalaa Fahiem </title>
      <link>https://dev.to/itsaalaa7</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/itsaalaa7"/>
    <language>en</language>
    <item>
      <title>I Didn't Memorize the Android ViewModel, I Derived It From 8 Problems( Interview Prep)</title>
      <dc:creator>Aalaa Fahiem </dc:creator>
      <pubDate>Thu, 16 Jul 2026 12:24:22 +0000</pubDate>
      <link>https://dev.to/itsaalaa7/i-didnt-memorize-the-android-viewmodel-i-derived-it-from-8-problems-interview-prep-23el</link>
      <guid>https://dev.to/itsaalaa7/i-didnt-memorize-the-android-viewmodel-i-derived-it-from-8-problems-interview-prep-23el</guid>
      <description>&lt;p&gt;Every ViewModel tutorial I read handed me the finished file: &lt;code&gt;@HiltViewModel&lt;/code&gt;, a private &lt;code&gt;MutableStateFlow&lt;/code&gt;, a public &lt;code&gt;StateFlow&lt;/code&gt;, &lt;code&gt;viewModelScope.launch&lt;/code&gt;, a sealed class. It compiled. I copied it. I understood &lt;strong&gt;nothing&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The problem with starting from finished code is that it looks &lt;em&gt;arbitrary&lt;/em&gt;. Why two properties instead of one? Why &lt;code&gt;init&lt;/code&gt;? Why start at Loading?&lt;/p&gt;

&lt;p&gt;So I did it backwards. I threw the code away and asked: &lt;strong&gt;what problems is this thing actually solving?&lt;/strong&gt; There are 8. Stack the 8 answers and the file writes itself — nothing in it is a style choice.&lt;/p&gt;

&lt;p&gt;Here's the version I wish I had.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 6-year-old version
&lt;/h2&gt;

&lt;p&gt;Imagine a whiteboard on the wall of a room. Anyone in the room can &lt;strong&gt;read&lt;/strong&gt; the whiteboard, but only &lt;strong&gt;you&lt;/strong&gt; hold the marker. When you change what's written, everyone looking at it instantly sees the new thing — nobody has to keep asking "did it change yet?"&lt;/p&gt;

&lt;p&gt;That whiteboard is &lt;strong&gt;StateFlow&lt;/strong&gt;. The rule that only you hold the marker is why there are two properties instead of one. And the whiteboard hangs on the &lt;em&gt;wall&lt;/em&gt; (the ViewModel), not on a person — so when someone leaves the room, the writing survives.&lt;/p&gt;

&lt;p&gt;Now the real thing.&lt;/p&gt;

&lt;h2&gt;
  
  
  First: what can a screen actually be doing?
&lt;/h2&gt;

&lt;p&gt;Before the ViewModel, one observation: a list screen is &lt;strong&gt;always&lt;/strong&gt; in exactly one of three situations. It's loading, it succeeded, or it failed. That's it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;PokemonListUiState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Loading&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PokemonListUiState&lt;/span&gt;
    &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokemonResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PokemonListUiState&lt;/span&gt;
    &lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PokemonListUiState&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;sealed&lt;/code&gt; means &lt;strong&gt;closed set&lt;/strong&gt; — "these are the only 3, nothing else can join." The compiler knows the complete list, so when the screen does &lt;code&gt;when (state)&lt;/code&gt;, it &lt;em&gt;forces&lt;/em&gt; you to handle all three. You can't forget the error case.&lt;/p&gt;

&lt;p&gt;Why not an &lt;code&gt;enum&lt;/code&gt;? Because each case carries &lt;strong&gt;different cargo&lt;/strong&gt;: Success holds a list, Error holds a message, Loading holds nothing. Enums can't do that cleanly. And notice &lt;code&gt;Loading&lt;/code&gt; is an &lt;code&gt;object&lt;/code&gt;, not a &lt;code&gt;data class&lt;/code&gt; — it carries no data, so one shared instance is enough.&lt;/p&gt;

&lt;p&gt;Now, the 8 problems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 1: The screen is fragile
&lt;/h2&gt;

&lt;p&gt;Rotate the phone and Android &lt;strong&gt;destroys and rebuilds&lt;/strong&gt; your screen. If the screen held the data, it's gone — you'd refetch from the network on every rotation. You need something that &lt;strong&gt;outlives the screen&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; a ViewModel. That is literally its entire job.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PokemonListViewModel&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Problem 2: The ViewModel needs a repository — but shouldn't build one
&lt;/h2&gt;

&lt;p&gt;Building one means building Retrofit, which means OkHttp, which means a connection pool… all to ask for a list of Pokémon. We just want to &lt;strong&gt;ask&lt;/strong&gt; for the thing, not assemble it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; let Hilt hand it over.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@HiltViewModel&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PokemonListViewModel&lt;/span&gt; &lt;span class="nd"&gt;@Inject&lt;/span&gt; &lt;span class="k"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PokemonRepository&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note it asks for &lt;code&gt;PokemonRepository&lt;/code&gt; — the &lt;strong&gt;interface&lt;/strong&gt;, not the implementation. The ViewModel shouldn't know or care whether that data came from the network or a local database.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 3: The data arrives LATER — how does the screen find out?
&lt;/h2&gt;

&lt;p&gt;The screen already drew itself while the network was still working. Two options: the screen keeps polling ("done yet? done yet?"), or the screen &lt;strong&gt;subscribes&lt;/strong&gt; and gets &lt;em&gt;told&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; hold the state in something watchable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokemonListUiState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set &lt;code&gt;.value&lt;/code&gt; and everyone watching is instantly notified. Push, not poll. That's the whiteboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 4: But if the screen can watch it, can it also change it?
&lt;/h2&gt;

&lt;p&gt;That would be a disaster. Any screen could write garbage: &lt;code&gt;viewModel.uiState.value = Success(fakeList)&lt;/code&gt;. State would mutate from anywhere in the app, and when a bug shows up you'd have to hunt every file that &lt;em&gt;might&lt;/em&gt; have written to it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; expose reading, hide writing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;_uiState&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokemonListUiState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nc"&gt;PokemonListUiState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;uiState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokemonListUiState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_uiState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asStateFlow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;MutableStateFlow&lt;/code&gt; has a settable &lt;code&gt;.value&lt;/code&gt;. &lt;code&gt;asStateFlow()&lt;/code&gt; returns a &lt;strong&gt;read-only view of the same data&lt;/strong&gt; — no setter exposed. Now there is exactly &lt;strong&gt;one&lt;/strong&gt; place in the codebase where state can change: inside this ViewModel. That single source of truth is the real payoff — it's a &lt;strong&gt;debuggability&lt;/strong&gt; feature.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This is the answer to "why two properties instead of one."&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 5: When should the fetch start?
&lt;/h2&gt;

&lt;p&gt;The moment the user opens the screen. Not on a button press, not later.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; run it the instant the ViewModel is born.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Problem 6: The repository function is &lt;code&gt;suspend&lt;/code&gt; — you can't just call it
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;suspend&lt;/code&gt; functions only run inside a coroutine. But &lt;em&gt;which&lt;/em&gt; coroutine? Pick a long-lived scope (like &lt;code&gt;GlobalScope&lt;/code&gt;) and the coroutine keeps running after the user has left the screen — holding references, updating state for something that no longer exists. That's a &lt;strong&gt;memory leak&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; use a scope whose life is tied to the ViewModel's.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two words worth separating: &lt;strong&gt;scope&lt;/strong&gt; = &lt;em&gt;where the coroutine lives&lt;/em&gt; (&lt;code&gt;viewModelScope&lt;/code&gt;, which Android auto-cancels when the ViewModel dies). &lt;strong&gt;builder&lt;/strong&gt; = &lt;em&gt;what starts it&lt;/em&gt; (&lt;code&gt;launch&lt;/code&gt;). You don't create the scope; the library hands you one pre-wired to the right lifetime.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem 7: The network can fail
&lt;/h2&gt;

&lt;p&gt;No wifi. Server down. Timeout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; try it, and if it explodes, catch it and show the failure instead of crashing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Problem 8: What does the user stare at before the answer arrives?
&lt;/h2&gt;

&lt;p&gt;A blank screen? An empty list?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; start the state at Loading, before anything happens.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokemonListUiState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nc"&gt;PokemonListUiState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The user sees a spinner &lt;strong&gt;instantly&lt;/strong&gt;, and it's the truth: &lt;em&gt;"I'm working on it."&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack the 8 and the file writes itself
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@HiltViewModel&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PokemonListViewModel&lt;/span&gt; &lt;span class="nd"&gt;@Inject&lt;/span&gt; &lt;span class="k"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PokemonRepository&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;_uiState&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokemonListUiState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="nc"&gt;PokemonListUiState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;uiState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokemonListUiState&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_uiState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asStateFlow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;_uiState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPokemonList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nc"&gt;PokemonListUiState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;PokemonListUiState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="s"&gt;"Something went wrong"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read it as a sentence: &lt;strong&gt;survive the screen → ask for the repo → hold state watchably → hide the writing → start at Loading → fetch at birth → in a scope that dies with you → handle failure.&lt;/strong&gt; Not one line is arbitrary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Gotchas I hit
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;"MutableStateFlow makes it survive rotation."&lt;/strong&gt; Nope — I merged two separate jobs. &lt;strong&gt;The ViewModel survives&lt;/strong&gt; (Problem 1). &lt;strong&gt;StateFlow notifies&lt;/strong&gt; (Problem 3). They just happen to live in the same file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;Loading&lt;/code&gt; vs &lt;code&gt;Success(emptyList())&lt;/code&gt;.&lt;/strong&gt; I thought &lt;code&gt;Loading&lt;/code&gt; existed to prevent a crash. It doesn't — nothing would crash. It's about &lt;strong&gt;not lying to the user&lt;/strong&gt;. &lt;code&gt;Loading&lt;/code&gt; means &lt;em&gt;"I'm working."&lt;/em&gt; &lt;code&gt;Success(emptyList())&lt;/code&gt; means &lt;em&gt;"I finished and found zero Pokémon."&lt;/em&gt; Those are completely different claims, and one of them is false before the network answers.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;e.message&lt;/code&gt; is nullable.&lt;/strong&gt; &lt;code&gt;Error(e.message)&lt;/code&gt; won't compile against a &lt;code&gt;String&lt;/code&gt;. You need a fallback: &lt;code&gt;e.message ?: "Something went wrong"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Exposing the mutable flow "just for now."&lt;/strong&gt; The moment it's public, state can change from anywhere, and your single source of truth is gone. It never stays "just for now."&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Interview-ready takeaways
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: Why two properties — a private &lt;code&gt;_uiState&lt;/code&gt; and a public &lt;code&gt;uiState&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
So the screen can read state but never write it. &lt;code&gt;asStateFlow()&lt;/code&gt; exposes a read-only view of the same data, keeping the ViewModel the single place state can change. With one public &lt;code&gt;MutableStateFlow&lt;/code&gt;, any file could mutate it and bugs become untraceable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Why &lt;code&gt;viewModelScope.launch&lt;/code&gt; and not &lt;code&gt;GlobalScope.launch&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;viewModelScope&lt;/code&gt; is auto-cancelled when the ViewModel is destroyed. &lt;code&gt;GlobalScope&lt;/code&gt; outlives the screen — the coroutine keeps running and holding references for a screen the user already left. That's a memory leak.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Why start at &lt;code&gt;Loading&lt;/code&gt; instead of &lt;code&gt;Success(emptyList())&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
Because you haven't succeeded yet — you haven't even asked. &lt;code&gt;Success(emptyList())&lt;/code&gt; tells the user "done, found nothing," which is a lie. &lt;code&gt;Loading&lt;/code&gt; says "I'm working," which is true, and gives them a spinner instead of a fake-empty screen.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Why a &lt;code&gt;sealed&lt;/code&gt; interface for UiState instead of an enum or booleans?&lt;/strong&gt;&lt;br&gt;
Sealed = closed set, so &lt;code&gt;when&lt;/code&gt; is exhaustive and you can't forget a state. Unlike an enum, each case carries its own data (Success → a list, Error → a message). Unlike &lt;code&gt;isLoading&lt;/code&gt;/&lt;code&gt;isError&lt;/code&gt; booleans, you can't represent impossible combinations like loading &lt;em&gt;and&lt;/em&gt; error at once.&lt;/p&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;A ViewModel isn't a pile of ceremony — it's 8 answers stacked. Survive rotation (ViewModel), get dependencies handed to you (Hilt), make state watchable (StateFlow), keep the marker to yourself (private mutable + public read-only), start honest (Loading), fetch on birth (&lt;code&gt;init&lt;/code&gt;), stay in a scope that dies with you (&lt;code&gt;viewModelScope&lt;/code&gt;), and expect failure (try/catch).&lt;/p&gt;

&lt;p&gt;Memorize the &lt;strong&gt;why&lt;/strong&gt; of each. Look up the syntax — nobody remembers how to spell &lt;code&gt;asStateFlow()&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>mobile</category>
      <category>beginners</category>
      <category>interview</category>
    </item>
    <item>
      <title>I'm Starting the 100 Days of Cloud Challenge and I Don't Want to Do It Alone</title>
      <dc:creator>Aalaa Fahiem </dc:creator>
      <pubDate>Thu, 09 Jul 2026 13:43:06 +0000</pubDate>
      <link>https://dev.to/itsaalaa7/im-starting-the-100-days-of-cloud-challenge-and-i-dont-want-to-do-it-alone-bkp</link>
      <guid>https://dev.to/itsaalaa7/im-starting-the-100-days-of-cloud-challenge-and-i-dont-want-to-do-it-alone-bkp</guid>
      <description>&lt;p&gt;Let me be honest with you.&lt;br&gt;
I'm an Android engineer. Kotlin is my home. Jetpack Compose is where I live. The cloud has always felt like someone else's territory — the backend people, the DevOps people, the people who actually know what a VPC is.&lt;br&gt;
But I'm building something that needs a cloud backend. And I applied for something that reminded me: the gap between "I should learn cloud" and "I am learning cloud" is just a decision.&lt;br&gt;
So I made the decision. And now I need a friend.&lt;br&gt;
 At First thank you &lt;a class="mentioned-user" href="https://dev.to/hemapriya_kanagala"&gt;@hemapriya_kanagala&lt;/a&gt; that you sharing that useful source i really appreciate your effort for those Articles &lt;/p&gt;

&lt;p&gt;What is the 100 Days of Cloud?&lt;br&gt;
It's a free challenge by KodeKloud: 100 hands-on tasks covering real AWS and Azure skills — IAM, storage, networking, compute, serverless, monitoring, and more. Not videos to watch. Actual tasks in real cloud environments, with automated validation.&lt;br&gt;
One task a day. Sequential. You can't skip. You can retry as many times as you need.&lt;br&gt;
By day 100 you have a portfolio of 100 things you actually built, and a verified badge to show for it.&lt;br&gt;
👉 &lt;a href="https://kodekloud.com/100-days-of-cloud" rel="noopener noreferrer"&gt;https://kodekloud.com/100-days-of-cloud&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why I'm doing this&lt;br&gt;
I'm building an app addressing real infrastructure in Cairo, and phase two of that project requires cloud. I'm not learning cloud as an abstract future goal. I have a specific deadline in mind.&lt;/p&gt;

&lt;p&gt;Why I need a study buddy&lt;br&gt;
Because accountability is the only thing that has ever actually made me follow through on a 100-day anything.&lt;br&gt;
Not pressure. Just: "did you do today's task?" — and knowing someone else is going to ask me the same thing.&lt;br&gt;
If you're an Android dev curious about cloud, a complete beginner who keeps putting this off, or honestly anyone who wants a reason to finally start — I'd love to do this alongside you.&lt;br&gt;
We don't have to be at the same level. We just have to start on the same day.&lt;/p&gt;

&lt;p&gt;Drop a comment if you're in. We'll figure out a check-in rhythm together — maybe a weekly thread, maybe just comments on each other's posts. Whatever actually works.&lt;br&gt;
The start date is whenever you reply. I'll wait.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>cloud</category>
      <category>beginners</category>
      <category>career</category>
    </item>
    <item>
      <title>My API Functions Had No Code Inside Them. So Who Was Actually Fetching the Data? (Interview Prep)</title>
      <dc:creator>Aalaa Fahiem </dc:creator>
      <pubDate>Tue, 07 Jul 2026 16:27:30 +0000</pubDate>
      <link>https://dev.to/itsaalaa7/my-api-functions-had-no-code-inside-them-so-who-was-actually-fetching-the-data-interview-prep-4i31</link>
      <guid>https://dev.to/itsaalaa7/my-api-functions-had-no-code-inside-them-so-who-was-actually-fetching-the-data-interview-prep-4i31</guid>
      <description>&lt;p&gt;I wrote the network layer of my Pokedex app, and then I sat there staring at it, genuinely confused.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;PokeApi&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokemon"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokemonList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"offset"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;PokemonListResponse&lt;/span&gt;

    &lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokemon/{name}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokemonDetail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;PokemonDetail&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The functions are &lt;strong&gt;empty&lt;/strong&gt;. There's no code inside them. &lt;code&gt;getPokemonList&lt;/code&gt; doesn't open a connection, doesn't send anything, doesn't parse anything. It's just a name and a return type. And yet — this is the thing that fetches every Pokemon in the app.&lt;/p&gt;

&lt;p&gt;My first guess was wrong, so let me tell you the wrong version first, because you might be thinking it too. I thought: &lt;em&gt;"maybe the data models fetch the data themselves."&lt;/em&gt; Then I looked at a data model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;PokemonResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's no networking code in there either. It's two empty slots. A data model can't open the internet — it's a &lt;strong&gt;plate&lt;/strong&gt;, not a cook. It just holds food that someone else made.&lt;/p&gt;

&lt;p&gt;So the puzzle stood: the interface has no body, the models can't fetch — &lt;strong&gt;who writes the actual networking code?&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The answer: Retrofit writes it for you
&lt;/h2&gt;

&lt;p&gt;Here's the mental model that fixed it for me. Think of a restaurant.&lt;/p&gt;

&lt;p&gt;Your &lt;code&gt;interface&lt;/code&gt; is the &lt;strong&gt;menu&lt;/strong&gt;. It lists what you can order — "list of Pokemon," "one Pokemon's details." A menu doesn't contain recipes and it doesn't cook anything. It just declares what's &lt;em&gt;orderable&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Retrofit is the kitchen.&lt;/strong&gt; And this one line (which lives in your Hilt module) is where the magic happens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;api&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;retrofit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PokeApi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;create()&lt;/code&gt; reads your menu and &lt;strong&gt;generates a hidden class that implements your interface&lt;/strong&gt; — a real cook. That generated class is where all the actual code lives: build the URL, attach the query parameters, hand it to OkHttp, wait for the response, pass the JSON to Gson. &lt;strong&gt;That generated implementation is the "body" that looked missing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;So the full chain is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Interface&lt;/strong&gt; — the menu. &lt;em&gt;What&lt;/em&gt; you can request. No code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;retrofit.create()&lt;/code&gt;&lt;/strong&gt; — builds the cook. &lt;em&gt;This&lt;/em&gt; writes the real networking code, at runtime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gson&lt;/strong&gt; — plates the JSON response onto your data model.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data model&lt;/strong&gt; — the plate. Holds the result. Never fetches anything.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You declare &lt;em&gt;what&lt;/em&gt; to fetch. Retrofit generates the &lt;em&gt;how&lt;/em&gt;. The models only &lt;em&gt;hold&lt;/em&gt; the result.&lt;/p&gt;




&lt;h2&gt;
  
  
  How one function becomes one URL
&lt;/h2&gt;

&lt;p&gt;Your &lt;code&gt;baseUrl&lt;/code&gt; (set in Hilt) is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://pokeapi.co/api/v2/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retrofit glues the &lt;code&gt;@GET("...")&lt;/code&gt; path onto the end. Three annotations do the work:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@GET("pokemon")&lt;/code&gt; — the path stuck onto the base → &lt;code&gt;.../pokemon&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Query("limit") limit: Int&lt;/code&gt; — adds &lt;code&gt;?limit=...&lt;/code&gt; to the URL&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@Path("name") name: String&lt;/code&gt; — fills a &lt;code&gt;{name}&lt;/code&gt; blank inside the path&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And &lt;code&gt;suspend&lt;/code&gt; is there because a network call takes time. &lt;code&gt;suspend&lt;/code&gt; lets it run on a coroutine, so it never freezes the screen while it waits.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;a class="mentioned-user" href="https://dev.to/path"&gt;@path&lt;/a&gt; vs @Query — the part everyone mixes up
&lt;/h2&gt;

&lt;p&gt;This confused me for a while, so here's the analogy that finally made it stick: a &lt;strong&gt;library&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A &lt;code&gt;@Path&lt;/code&gt; is a book's shelf address.&lt;/strong&gt; Every book sits at a fixed spot. If you know the spot, you walk straight to that one book. Every book has its &lt;em&gt;own&lt;/em&gt; unique address.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pokemon/bulbasaur     ← bulbasaur is the address of one exact Pokemon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;bulbasaur&lt;/code&gt; is baked into the path with a &lt;code&gt;/&lt;/code&gt;. It &lt;em&gt;identifies&lt;/em&gt; one specific thing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A &lt;code&gt;@Query&lt;/code&gt; is a word you hand to the search desk.&lt;/strong&gt; There's one search desk. You fill a slip — "topic: headphones" — and the librarian searches. Tomorrow someone hands the same desk a different word. One desk, many words.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pokemon?limit=20&amp;amp;offset=0     ← options handed to the list endpoint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;limit&lt;/code&gt; and &lt;code&gt;offset&lt;/code&gt; come after the &lt;code&gt;?&lt;/code&gt;. They don't change &lt;em&gt;which&lt;/em&gt; endpoint — they just tune the same list.&lt;/p&gt;

&lt;p&gt;The rule I use now:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does the value have its &lt;strong&gt;own address&lt;/strong&gt;? → &lt;code&gt;@Path&lt;/code&gt; (which exact thing).&lt;br&gt;
Is it a &lt;strong&gt;word/option you hand to a shared endpoint&lt;/strong&gt;, after a &lt;code&gt;?&lt;/code&gt;? → &lt;code&gt;@Query&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Interview questions on the network layer
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Q: Why is the Retrofit API an interface and not a class?&lt;/strong&gt;&lt;br&gt;
Because you only declare &lt;em&gt;what&lt;/em&gt; requests exist. You don't implement them. Retrofit generates the implementation at runtime via &lt;code&gt;retrofit.create()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: The functions have no body — where does the networking code come from?&lt;/strong&gt;&lt;br&gt;
Retrofit's &lt;code&gt;create()&lt;/code&gt; builds a hidden class that implements the interface. That generated class contains the real code: URL building, OkHttp call, Gson parsing. The interface is just the menu.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Difference between &lt;a class="mentioned-user" href="https://dev.to/path"&gt;@path&lt;/a&gt; and @Query?&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;@Path&lt;/code&gt; fills a &lt;code&gt;{placeholder}&lt;/code&gt; inside the URL path — it identifies one specific resource (&lt;code&gt;pokemon/bulbasaur&lt;/code&gt;). &lt;code&gt;@Query&lt;/code&gt; adds a &lt;code&gt;?key=value&lt;/code&gt; option to the URL — it tunes a request (&lt;code&gt;pokemon?limit=20&lt;/code&gt;). Identity vs option.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: Why is the function &lt;code&gt;suspend&lt;/code&gt;?&lt;/strong&gt;&lt;br&gt;
A network call is slow. &lt;code&gt;suspend&lt;/code&gt; makes it a coroutine-friendly call that runs off the main thread, so the UI doesn't freeze while waiting. Without it, you'd block the main thread (or need a &lt;code&gt;Call&amp;lt;T&amp;gt;&lt;/code&gt; and manual callbacks).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: You rename &lt;code&gt;@Path("name")&lt;/code&gt; to &lt;code&gt;@Path("pokemonName")&lt;/code&gt; but leave the path as &lt;code&gt;pokemon/{name}&lt;/code&gt;. Compile error or runtime error?&lt;/strong&gt;&lt;br&gt;
Runtime. The compiler sees valid Kotlin and valid strings — it never checks that the &lt;code&gt;@Path&lt;/code&gt; label matches the &lt;code&gt;{name}&lt;/code&gt; blank. Retrofit checks that at runtime and throws because it can't find a placeholder named &lt;code&gt;pokemonName&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Q: &lt;code&gt;@GET("Pokemon")&lt;/code&gt; with a capital P compiles fine but returns nothing. Why didn't the compiler catch it?&lt;/strong&gt;&lt;br&gt;
Because the path is a string, and URLs are case-sensitive. The compiler doesn't validate URL strings. &lt;code&gt;Pokemon&lt;/code&gt; ≠ &lt;code&gt;pokemon&lt;/code&gt;, so the server returns a 404 — but only at runtime.&lt;/p&gt;




&lt;p&gt;None of this was about memorizing Retrofit annotations. It was about one shift: &lt;strong&gt;you're not writing the networking code — you're writing a description that Retrofit turns into networking code.&lt;/strong&gt; The interface is a menu. Retrofit is the kitchen. And the compiler is only checking your grammar, not your order.&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>retrofit</category>
      <category>interview</category>
    </item>
    <item>
      <title>Gson silent bug that Never Said a Word(Interview Prep)</title>
      <dc:creator>Aalaa Fahiem </dc:creator>
      <pubDate>Sun, 05 Jul 2026 15:44:16 +0000</pubDate>
      <link>https://dev.to/itsaalaa7/gson-silent-bug-that-never-said-a-wordinterview-prep-2818</link>
      <guid>https://dev.to/itsaalaa7/gson-silent-bug-that-never-said-a-wordinterview-prep-2818</guid>
      <description>&lt;p&gt;Here's a bug that looks impossible until you understand Gson.&lt;/p&gt;

&lt;p&gt;You have this data class in your Pokedex app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;PokemonStat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nd"&gt;@SerializedName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"base_stat"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;baseStat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StatInfo&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A teammate cleans up the code and deletes what looks like a redundant line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;PokemonStat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;baseStat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// @SerializedName removed&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StatInfo&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It &lt;strong&gt;compiles&lt;/strong&gt;. No red errors. He runs the app — and every Pokemon's &lt;code&gt;baseStat&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt;. Not the real 45 or 49. Just &lt;code&gt;0&lt;/code&gt;. Everywhere. And Gson never threw an error, never logged a warning, never said a single word.&lt;/p&gt;

&lt;p&gt;If you can explain &lt;em&gt;why&lt;/em&gt; this happens, you understand Gson better than most juniors. This is also a favorite interview question. Let's break it fully.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Gson actually does
&lt;/h2&gt;

&lt;p&gt;When the JSON comes back from the server, Gson goes key by key:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read a key from the JSON — say &lt;code&gt;base_stat&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Look for a Kotlin property with the &lt;strong&gt;exact same name&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Found it? Pour the value in.&lt;/li&gt;
&lt;li&gt;Not found? Leave that property at its &lt;strong&gt;default value&lt;/strong&gt; and move on.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's the entire matching game — &lt;strong&gt;exact name match, or nothing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Now look at the "cleaned up" class. The JSON key is &lt;code&gt;base_stat&lt;/code&gt;. The Kotlin property is &lt;code&gt;baseStat&lt;/code&gt;. Those are &lt;strong&gt;not the same string&lt;/strong&gt;. Gson looks for a property called &lt;code&gt;base_stat&lt;/code&gt;, doesn't find one, shrugs, and leaves &lt;code&gt;baseStat&lt;/code&gt; at its default.&lt;/p&gt;

&lt;p&gt;The default for an &lt;code&gt;Int&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt;. That's your bug.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;@SerializedName("base_stat")&lt;/code&gt; was never redundant. It was the sticky note telling Gson: &lt;em&gt;"this property is called &lt;code&gt;baseStat&lt;/code&gt; in Kotlin, but look for &lt;code&gt;base_stat&lt;/code&gt; in the JSON."&lt;/em&gt; Delete the note, and Gson stops matching.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two ways to fix it:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Rename the property to &lt;code&gt;base_stat&lt;/code&gt; — works, but breaks Kotlin's camelCase convention.&lt;/li&gt;
&lt;li&gt;Put &lt;code&gt;@SerializedName("base_stat")&lt;/code&gt; back — keeps the clean name &lt;em&gt;and&lt;/em&gt; matches. This is the right one.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  But why &lt;code&gt;0&lt;/code&gt;? Why not a crash?
&lt;/h2&gt;

&lt;p&gt;This is the part that surprised me. A missing field feels like it &lt;em&gt;should&lt;/em&gt; be an error. It isn't.&lt;/p&gt;

&lt;p&gt;Gson treats a missing key as &lt;strong&gt;allowed&lt;/strong&gt;. It builds your object, fills the fields it found, and leaves the rest at their defaults. Think of Gson filling out a paper form: leaving a box &lt;strong&gt;blank&lt;/strong&gt; is fine. It doesn't tear up the form over an empty box.&lt;/p&gt;

&lt;p&gt;Here are the defaults you get for a missing key:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Kotlin type&lt;/th&gt;
&lt;th&gt;Default when the key is missing&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Int&lt;/code&gt;, &lt;code&gt;Long&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;Double&lt;/code&gt;, &lt;code&gt;Float&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;0.0&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Boolean&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;String&lt;/code&gt;, any object, &lt;code&gt;List&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;null&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So a missing &lt;code&gt;Int&lt;/code&gt; silently becomes &lt;code&gt;0&lt;/code&gt;. A missing &lt;code&gt;String&lt;/code&gt; silently becomes &lt;code&gt;null&lt;/code&gt;. No noise either way.&lt;/p&gt;

&lt;h2&gt;
  
  
  So when &lt;em&gt;does&lt;/em&gt; Gson throw?
&lt;/h2&gt;

&lt;p&gt;It's strict about two things — just not about missing keys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Malformed JSON&lt;/strong&gt; — the text itself is broken (a missing &lt;code&gt;}&lt;/code&gt;, a stray comma). Like handing in a form that's been torn in half. Gson can't parse it → it throws.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Type clash&lt;/strong&gt; — the key exists but holds the wrong type, like &lt;code&gt;"base_stat": "hello"&lt;/code&gt; (text where an &lt;code&gt;Int&lt;/code&gt; belongs). Like writing "banana" in the Age box. Gson tries to convert, fails → it throws.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the rule to remember: &lt;strong&gt;Gson is lenient about &lt;em&gt;missing&lt;/em&gt; data and strict about &lt;em&gt;broken&lt;/em&gt; or &lt;em&gt;wrong-typed&lt;/em&gt; data.&lt;/strong&gt; A blank box is fine. A torn form or a banana-in-a-number-box is not.&lt;/p&gt;




&lt;h2&gt;
  
  
  The scary part: Gson vs Kotlin null-safety
&lt;/h2&gt;

&lt;p&gt;Kotlin's biggest promise is null-safety. When you write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;   &lt;span class="c1"&gt;// no question mark&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kotlin &lt;em&gt;guarantees&lt;/em&gt; this can never be null. The compiler enforces it. You never have to null-check it. That guarantee is the reason Kotlin kills most &lt;code&gt;NullPointerException&lt;/code&gt; crashes.&lt;/p&gt;

&lt;p&gt;Here's the problem: &lt;strong&gt;Gson doesn't respect that guarantee.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Gson doesn't build your object the normal Kotlin way. It sneaks in through reflection and shoves values straight into fields, skipping Kotlin's checks entirely. So if the JSON key is missing, Gson will quietly put &lt;code&gt;null&lt;/code&gt; into your non-null &lt;code&gt;String&lt;/code&gt; — no error, the "can never be null" label still sitting right there.&lt;/p&gt;

&lt;p&gt;Then later, somewhere far away, your code does the obvious thing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;length&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;   &lt;span class="c1"&gt;// name was secretly null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💥 &lt;code&gt;NullPointerException&lt;/code&gt; — and it crashes nowhere near the JSON parsing that actually caused it. Good luck tracing that at 1 AM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;That's the gotcha: Kotlin promised the field could never be null, and Gson broke the promise behind its back.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This is exactly why fields that come from JSON are safer as nullable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Sprites&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nd"&gt;@SerializedName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"front_default"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;frontDefault&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;   &lt;span class="c1"&gt;// honest&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;?&lt;/code&gt; tells Kotlin the truth — &lt;em&gt;this might actually be null&lt;/em&gt; — so the compiler forces you to handle the empty case instead of trusting a promise Gson didn't keep. You defuse the bomb before it's armed.&lt;/p&gt;




&lt;h2&gt;
  
  
  The interview version
&lt;/h2&gt;

&lt;p&gt;If someone asks you about Gson and Kotlin, here's the whole thing in a few lines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Q: How does Gson match JSON to Kotlin?&lt;/strong&gt; Exact property-name match, or &lt;code&gt;@SerializedName&lt;/code&gt; to map a different JSON key. No match → default value.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Q: A field comes back as &lt;code&gt;0&lt;/code&gt; / &lt;code&gt;null&lt;/code&gt; and there's no error. Why?&lt;/strong&gt; The JSON key didn't match the property name. A missing key isn't an error in Gson — the field just keeps its default (&lt;code&gt;0&lt;/code&gt; for &lt;code&gt;Int&lt;/code&gt;, &lt;code&gt;null&lt;/code&gt; for &lt;code&gt;String&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Q: When does Gson actually throw?&lt;/strong&gt; Malformed JSON, or a type mismatch — not a missing key.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Q: What's the danger with non-null Kotlin types?&lt;/strong&gt; Gson bypasses Kotlin null-safety via reflection and can put &lt;code&gt;null&lt;/code&gt; into a non-null field, causing a delayed crash. Model JSON fields as nullable when the API might omit them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of this is about memorizing Gson. It's about understanding that Gson stays silent exactly where you'd expect it to shout — and that silence is what makes these bugs sneaky.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>android</category>
      <category>json</category>
      <category>interview</category>
    </item>
    <item>
      <title>How to derive data classes for any API</title>
      <dc:creator>Aalaa Fahiem </dc:creator>
      <pubDate>Sun, 05 Jul 2026 15:06:42 +0000</pubDate>
      <link>https://dev.to/itsaalaa7/how-to-derive-data-classes-for-any-api-144o</link>
      <guid>https://dev.to/itsaalaa7/how-to-derive-data-classes-for-any-api-144o</guid>
      <description>&lt;p&gt;Let me be honest about a mistake I made this week.&lt;/p&gt;

&lt;p&gt;I was building the data layer of a Pokedex app. I opened the JSON from the API, and I ended up writing two data classes for the list screen — &lt;code&gt;PokemonListResponse&lt;/code&gt; and &lt;code&gt;PokemonResult&lt;/code&gt;. And in my head, I invented a reason for why there were two.&lt;/p&gt;

&lt;p&gt;"One of them," I told myself, "is the data coming from the server. The other one is the data I show in the UI."&lt;/p&gt;

&lt;p&gt;It sounded smart. It was completely wrong.&lt;/p&gt;

&lt;p&gt;Both classes describe the &lt;strong&gt;server's JSON&lt;/strong&gt;. Neither one is "the UI data." I had looked at two classes and &lt;em&gt;invented&lt;/em&gt; a structure that wasn't there — instead of reading the structure that actually was.&lt;/p&gt;

&lt;p&gt;That mistake taught me something more useful than the fix: &lt;strong&gt;you don't decide how many data classes you need. The JSON decides for you.&lt;/strong&gt; You just have to learn to read it.&lt;/p&gt;

&lt;p&gt;Here's the method I use now for any API.&lt;/p&gt;




&lt;h2&gt;
  
  
  The one rule everything comes from
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Your data classes mirror the shape of the JSON.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's it. Not "one for server, one for UI." Not a magic number someone told you. The classes are a &lt;strong&gt;mould&lt;/strong&gt; shaped like the data, so Gson can pour the JSON into them. Match the shape — it works. Miss the shape — it breaks.&lt;/p&gt;

&lt;p&gt;Everything below is just &lt;em&gt;how to read the shape&lt;/em&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Step 0 — Look at the real JSON first
&lt;/h2&gt;

&lt;p&gt;Never guess. Open the actual endpoint in your browser and stare at the real response. You cannot model a shape you haven't seen.&lt;/p&gt;

&lt;p&gt;Here's the Pokedex list endpoint (&lt;code&gt;/api/v2/pokemon?limit=20&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1302&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"results"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bulbasaur"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://pokeapi.co/api/v2/pokemon/1/"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ivysaur"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://pokeapi.co/api/v2/pokemon/2/"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 1 — Every &lt;code&gt;{ }&lt;/code&gt; object becomes one data class
&lt;/h2&gt;

&lt;p&gt;A curly-brace block is one mould. Count the &lt;em&gt;distinct&lt;/em&gt; &lt;code&gt;{ }&lt;/code&gt; shapes and you've counted your classes.&lt;/p&gt;

&lt;p&gt;In the JSON above there are two distinct shapes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the outer &lt;code&gt;{ count, results }&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;the little &lt;code&gt;{ name, url }&lt;/code&gt; that repeats inside the list&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two shapes → two classes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2 — For each key, ask ONE question: "what's on the right?"
&lt;/h2&gt;

&lt;p&gt;This is the whole skill. For every key inside an object, look at its value:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;strong&gt;primitive&lt;/strong&gt; — &lt;code&gt;"text"&lt;/code&gt;, &lt;code&gt;45&lt;/code&gt;, &lt;code&gt;true&lt;/code&gt; → a normal field: &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Int&lt;/code&gt;, &lt;code&gt;Boolean&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Another &lt;strong&gt;&lt;code&gt;{ }&lt;/code&gt;&lt;/strong&gt; → a field whose type is &lt;em&gt;another data class&lt;/em&gt; (a box inside a box)&lt;/li&gt;
&lt;li&gt;A &lt;strong&gt;&lt;code&gt;[ ]&lt;/code&gt;&lt;/strong&gt; → a field of type &lt;code&gt;List&amp;lt;X&amp;gt;&lt;/code&gt; — then look &lt;em&gt;inside&lt;/em&gt; the brackets to find what &lt;code&gt;X&lt;/code&gt; is&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Say it out loud every time: &lt;strong&gt;&lt;code&gt;[ ]&lt;/code&gt; means a list. &lt;code&gt;{ }&lt;/code&gt; means a single object.&lt;/strong&gt; That one sentence saved me from three different bugs this week.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3 — If the same shape repeats, reuse one class
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;{ name, url }&lt;/code&gt; shape shows up for every Pokemon in the list. You don't write a class per Pokemon — you write it &lt;strong&gt;once&lt;/strong&gt; and use &lt;code&gt;List&amp;lt;PokemonResult&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 4 — Only model what you need
&lt;/h2&gt;

&lt;p&gt;The real response also has &lt;code&gt;next&lt;/code&gt; and &lt;code&gt;previous&lt;/code&gt; keys. I didn't model them. Gson only fills the fields you &lt;em&gt;declare&lt;/em&gt; and quietly ignores the rest — so take what your app uses and skip the rest.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 — Name it, and file it
&lt;/h2&gt;

&lt;p&gt;Name each class after what it holds. Keep tiny helper classes in the same file as their parent; give standalone models their own file. This is only about &lt;em&gt;you&lt;/em&gt; finding things later — the compiler doesn't care.&lt;/p&gt;




&lt;h2&gt;
  
  
  Watch the method build the classes
&lt;/h2&gt;

&lt;p&gt;Applying steps 1–2 to that list JSON:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;PokemonListResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                     &lt;span class="c1"&gt;// primitive → Int&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokemonResult&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;    &lt;span class="c1"&gt;// [ ] → List of the inner shape&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;PokemonResult&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                   &lt;span class="c1"&gt;// primitive → String&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I didn't &lt;em&gt;decide&lt;/em&gt; on two classes. I read two &lt;code&gt;{ }&lt;/code&gt; shapes and the classes fell out.&lt;/p&gt;

&lt;p&gt;Now the harder one — the Pokemon &lt;strong&gt;detail&lt;/strong&gt; endpoint (&lt;code&gt;/api/v2/pokemon/bulbasaur&lt;/code&gt;), trimmed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bulbasaur"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sprites"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"front_default"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://.../1.png"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"slot"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"grass"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"stats"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"base_stat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;45&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"stat"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"hp"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Walk it key by key with the one question:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt; → primitive → &lt;code&gt;Int&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt; → primitive → &lt;code&gt;String&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sprites&lt;/code&gt; → &lt;code&gt;{ }&lt;/code&gt; → single object → its own class &lt;code&gt;Sprites&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;types&lt;/code&gt; → &lt;code&gt;[ ]&lt;/code&gt; → &lt;code&gt;List&amp;lt;...&amp;gt;&lt;/code&gt;, and each item is &lt;code&gt;{ slot, type }&lt;/code&gt; → a class, and &lt;code&gt;type&lt;/code&gt; inside it is &lt;code&gt;{ }&lt;/code&gt; → &lt;em&gt;another&lt;/em&gt; class&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;stats&lt;/code&gt; → &lt;code&gt;[ ]&lt;/code&gt; → &lt;code&gt;List&amp;lt;...&amp;gt;&lt;/code&gt;, same nesting story&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which produces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;PokemonDetail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;sprites&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Sprites&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;types&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokemonType&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;stats&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokemonStat&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;Sprites&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;front_default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;PokemonType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;slot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TypeInfo&lt;/span&gt;        &lt;span class="c1"&gt;// { } → single object&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;TypeInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;PokemonStat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;base_stat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;stat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StatInfo&lt;/span&gt;        &lt;span class="c1"&gt;// { } → single object&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;StatInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what happened. The list JSON was shallow, so it gave me flat classes. The detail JSON was nested, so it gave me nested classes. &lt;strong&gt;Same rule. Different input. Different-looking output.&lt;/strong&gt; I used to think those were two different "approaches." They're not — it's one rule meeting two shapes.&lt;/p&gt;




&lt;h2&gt;
  
  
  The mental shift
&lt;/h2&gt;

&lt;p&gt;The whole thing fits on a sticky note:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;For each key, ask: what's on the right?
  primitive → String / Int / Boolean
  { }       → another data class
  [ ]       → List&amp;lt;that class&amp;gt;

Same shape twice? Reuse it.
Don't need it? Skip it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I stopped asking "how many data classes does this API need?" — because that question has no answer until you look. The JSON has already decided. Your job isn't to invent the structure. It's to &lt;em&gt;read&lt;/em&gt; it.&lt;/p&gt;

&lt;p&gt;And the next time you catch yourself inventing a reason for why your code looks the way it does — go back and check the data. It'll tell you the truth.&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>beginners</category>
      <category>json</category>
    </item>
    <item>
      <title>I Thought Coroutines Were Enough. (Flow, StateFlow, MutableStateFlow)</title>
      <dc:creator>Aalaa Fahiem </dc:creator>
      <pubDate>Tue, 23 Jun 2026 12:56:05 +0000</pubDate>
      <link>https://dev.to/itsaalaa7/i-thought-coroutines-were-enough-flow-stateflow-mutablestateflow-39f</link>
      <guid>https://dev.to/itsaalaa7/i-thought-coroutines-were-enough-flow-stateflow-mutablestateflow-39f</guid>
      <description>&lt;p&gt;I had coroutines working. The data loaded. The screen showed something. I felt like I finally understood async in Android.&lt;/p&gt;

&lt;p&gt;Then I added a search bar that should filter results as you type.&lt;/p&gt;

&lt;p&gt;I called my suspend function on every keystroke. Every single character triggered a fresh network call. The results flickered. Old responses arrived after newer ones. The UI was a mess.&lt;/p&gt;

&lt;p&gt;That's when I realized — coroutines give you one value, once. But some data doesn't work that way. Some data is alive. It changes. It &lt;strong&gt;flows&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That's when I learned Flow. And then StateFlow. And the way data actually moves through an Android app finally made sense.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem With &lt;code&gt;suspend&lt;/code&gt; for Live Data
&lt;/h2&gt;

&lt;p&gt;A suspend function is like ordering food. You place the order, you wait, you get your food. Done. One request, one result.&lt;/p&gt;

&lt;p&gt;But what if you're tracking a live score? What if you want to react every time the database changes? What if the user is typing and you want to filter results in real time?&lt;/p&gt;

&lt;p&gt;You don't want one answer. You want a &lt;strong&gt;stream&lt;/strong&gt; of answers over time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// One value, one time — then silence&lt;/span&gt;
&lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokemon&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Pokemon&lt;/span&gt;

&lt;span class="c1"&gt;// A new value every time something changes&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokemonUpdates&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Flow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokemon&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the difference. &lt;code&gt;suspend&lt;/code&gt; is a bottle of water. &lt;code&gt;Flow&lt;/code&gt; is a pipe.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Is a Flow?
&lt;/h2&gt;

&lt;p&gt;A Flow is a stream of values that arrive over time. You create it, you collect it, and every time a new value is emitted — your code runs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;countDown&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Flow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;flow&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;delay&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;emit()&lt;/code&gt; is how you push a value into the stream. Each emit is one item. The collector receives them one by one.&lt;/p&gt;

&lt;p&gt;And here's the important part — a Flow does nothing until someone collects it. It's &lt;strong&gt;lazy&lt;/strong&gt;. It just sits there, waiting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;countDown&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 3... 2... 1...&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The moment you call &lt;code&gt;collect&lt;/code&gt;, the flow starts running.&lt;/p&gt;




&lt;h2&gt;
  
  
  Flow Operators — Where It Gets Useful
&lt;/h2&gt;

&lt;p&gt;This is the part I wish I'd paid attention to earlier.&lt;/p&gt;

&lt;p&gt;You don't have to collect raw values. You can transform the stream before it ever reaches your UI — filter it, map it, reshape it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPokemonList&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"fire"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;uppercase&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;textView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reads like a sentence. Each operator takes the stream, does something to it, and passes it along.&lt;/p&gt;

&lt;p&gt;The ones you'll use most:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operator&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;map&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Transform each value into something else&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;filter&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Only let values through that match a condition&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;onEach&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Do something with each value without changing it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;take(n)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Stop after n values&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The Problem With Regular Flow in the UI
&lt;/h2&gt;

&lt;p&gt;Here's something I did wrong for longer than I'd like to admit.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// In ViewModel&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokemon&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Flow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokemon&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPokemon&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="c1"&gt;// In Activity&lt;/span&gt;
&lt;span class="n"&gt;lifecycleScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPokemon&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;textView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Looks fine. It's not.&lt;/p&gt;

&lt;p&gt;Every time the Activity collects this flow — a new one starts from scratch. New network call. New everything. And when the phone rotates? The collection stops, the flow restarts, and the last value is gone. The screen flashes.&lt;/p&gt;

&lt;p&gt;You need something that holds the current value and gives it immediately to anyone who starts watching — even if they started watching late.&lt;/p&gt;

&lt;p&gt;That's StateFlow.&lt;/p&gt;




&lt;h2&gt;
  
  
  StateFlow — Flow That Remembers
&lt;/h2&gt;

&lt;p&gt;StateFlow is a flow with one extra superpower: it always holds the latest value in memory.&lt;/p&gt;

&lt;p&gt;Think of a scoreboard at a football match. Whether you look at it the moment a goal is scored or ten minutes later — you see the current score. You don't wait for the next goal to know what's happening.&lt;/p&gt;

&lt;p&gt;That's StateFlow. It always knows what the current value is, and it tells you immediately.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PokemonViewModel&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;_pokemon&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokemon&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokemon&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_pokemon&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;loadPokemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Dispatchers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPokemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;_pokemon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice — no &lt;code&gt;emit()&lt;/code&gt;. StateFlow uses &lt;code&gt;.value =&lt;/code&gt; to update. And it lives in the ViewModel, so it survives rotation.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Private/Public Pattern (Please Don't Skip This)
&lt;/h2&gt;

&lt;p&gt;You'll see this in every professional Android project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;_pokemon&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokemon&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;(&lt;/span&gt;&lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;// you write to this&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokemon&lt;/span&gt;&lt;span class="p"&gt;?&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_pokemon&lt;/span&gt;              &lt;span class="c1"&gt;// UI reads from this&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The underscore one is private and mutable — only the ViewModel can change it. The public one is read-only — the UI can observe it but never modify it directly.&lt;/p&gt;

&lt;p&gt;This isn't just style. It's a rule. The UI should never control the state. It just reacts to it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Collecting StateFlow in the UI
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;lifecycleScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;repeatOnLifecycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Lifecycle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;State&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;STARTED&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;textView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;repeatOnLifecycle(STARTED)&lt;/code&gt; is the piece most tutorials skip. It automatically pauses collection when the app goes to background and resumes when it comes back. Without it, your flow keeps running even when the user can't see the screen — wasting battery, risking crashes.&lt;/p&gt;

&lt;p&gt;Always use it when collecting in an Activity or Fragment.&lt;/p&gt;




&lt;h2&gt;
  
  
  Flow vs StateFlow — The Simple Version
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Flow&lt;/th&gt;
&lt;th&gt;StateFlow&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Holds last value?&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Starts automatically?&lt;/td&gt;
&lt;td&gt;No — needs a collector&lt;/td&gt;
&lt;td&gt;Yes — always running&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Where it lives&lt;/td&gt;
&lt;td&gt;Repository&lt;/td&gt;
&lt;td&gt;ViewModel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use it for&lt;/td&gt;
&lt;td&gt;DB queries, data streams&lt;/td&gt;
&lt;td&gt;UI state&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The rule I follow: data comes from the repository as &lt;code&gt;Flow&lt;/code&gt;. Data goes to the UI as &lt;code&gt;StateFlow&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Repository — emits a stream&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokemonList&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Flow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokemon&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt;

&lt;span class="c1"&gt;// ViewModel — holds the current state for the UI&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;_list&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokemon&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="nf"&gt;emptyList&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;list&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;StateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pokemon&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_list&lt;/span&gt;

&lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPokemonList&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;collect&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;_list&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Full Picture
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Repository
   └── Flow&amp;lt;T&amp;gt;               ← stream from data source, lazy

ViewModel
   └── MutableStateFlow&amp;lt;T&amp;gt;   ← holds current value, survives rotation
   └── StateFlow&amp;lt;T&amp;gt;          ← read-only, exposed to UI

UI
   └── collect { }           ← reacts to every change
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once I drew this out, everything clicked. The data flows in one direction — from the source, through the ViewModel, to the screen. Each layer has one job.&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Wish I Knew Earlier
&lt;/h2&gt;

&lt;p&gt;Flow and StateFlow aren't two separate tools that compete. They're two parts of the same pipeline.&lt;/p&gt;

&lt;p&gt;Flow is for the journey. StateFlow is for the destination.&lt;/p&gt;

&lt;p&gt;Once you think of it that way — your Repository emitting changes, your ViewModel holding the latest version of truth, your UI just reacting — the code writes itself.&lt;/p&gt;

&lt;p&gt;And that flickering search bar I mentioned at the start? Fixed. One Flow, debounced, collected properly. No more race conditions. No more flashing.&lt;/p&gt;

</description>
      <category>android</category>
      <category>beginners</category>
      <category>flow</category>
      <category>stateflow</category>
    </item>
    <item>
      <title>My App Froze Every Time It Loaded Data. That's When I Finally Understood Coroutines.</title>
      <dc:creator>Aalaa Fahiem </dc:creator>
      <pubDate>Mon, 22 Jun 2026 09:56:15 +0000</pubDate>
      <link>https://dev.to/itsaalaa7/my-app-froze-every-time-it-loaded-data-thats-when-i-finally-understood-coroutines-1ah5</link>
      <guid>https://dev.to/itsaalaa7/my-app-froze-every-time-it-loaded-data-thats-when-i-finally-understood-coroutines-1ah5</guid>
      <description>&lt;p&gt;I didn't know my app was broken.&lt;/p&gt;

&lt;p&gt;It worked. The data showed up. The UI updated. I thought I was doing great.&lt;/p&gt;

&lt;p&gt;Then someone ran it on a slow connection and sent me a screen recording. The app was completely frozen for 3 seconds. Nothing moved. The button didn't respond. It looked dead.&lt;/p&gt;

&lt;p&gt;I was doing network calls on the Main Thread. I had no idea that was even a thing I could do wrong.&lt;/p&gt;

&lt;p&gt;That's when I started actually learning coroutines — not copy-pasting them, &lt;strong&gt;learning&lt;/strong&gt; them.&lt;/p&gt;




&lt;h2&gt;
  
  
  First, Why Does the Main Thread Even Matter?
&lt;/h2&gt;

&lt;p&gt;Your Android app runs on one main thread. That thread does everything you see — drawing buttons, responding to taps, updating text. It's the only thread that can touch the UI.&lt;/p&gt;

&lt;p&gt;If you give it slow work to do — like waiting for an API response — it can't do anything else. It just stands there. Waiting. While your users stare at a frozen screen.&lt;/p&gt;

&lt;p&gt;Think of it like a waiter who goes to the kitchen, stands there until the food is ready, and refuses to take any other orders in the meantime. The whole restaurant stops.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Main Thread: [waiting for network response.........] [show result]
                    ☠️ App is completely frozen here
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the problem coroutines solve.&lt;/p&gt;




&lt;h2&gt;
  
  
  So What Even Is a Coroutine?
&lt;/h2&gt;

&lt;p&gt;A coroutine is a piece of work that can pause itself &lt;strong&gt;without blocking the thread&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The waiter analogy again — but this time, the waiter puts in the order, goes back to serving other tables, and comes back when the food is ready. The restaurant keeps running. Nobody is waiting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Coroutine:    [start] → [pause: waiting for network] → [resume] → [done]
Main Thread:                  ↑ free to handle UI here ↑
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The keyword that makes this possible is &lt;code&gt;suspend&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;suspend&lt;/code&gt; — The One Keyword That Changes Everything
&lt;/h2&gt;

&lt;p&gt;A suspend function is a function that can pause and resume. That's the whole thing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokemon&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;Pokemon&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchPokemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pikachu"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// pauses here, frees the thread&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It looks exactly like a normal function. It returns a value like a normal function. The difference is what happens underneath — instead of blocking the thread, it pauses and lets the thread go do other things.&lt;/p&gt;

&lt;p&gt;Two rules to remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You can only call a suspend function from another suspend function, or from inside a coroutine.&lt;/li&gt;
&lt;li&gt;That's it. That's the rule.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Starting a Coroutine — &lt;code&gt;launch&lt;/code&gt; vs &lt;code&gt;async&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;You can't just call a suspend function from anywhere. You need to launch a coroutine first. Think of it as opening a room where suspend functions are allowed to live.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;launch&lt;/code&gt; — when you don't need a result back&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pokemon&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getPokemon&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// suspend call — pauses here&lt;/span&gt;
    &lt;span class="n"&gt;_uiState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt;   &lt;span class="c1"&gt;// resumes here when done&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;&lt;code&gt;async&lt;/code&gt; — when you need a result&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pokemonDeferred&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;getPokemon&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pokemon&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pokemonDeferred&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;await&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// wait for the result&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Most of the time you'll use &lt;code&gt;launch&lt;/code&gt;. Use &lt;code&gt;async&lt;/code&gt; when you want to run two things at the same time and wait for both.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dispatchers — Who Does the Work?
&lt;/h2&gt;

&lt;p&gt;Just because you're in a coroutine doesn't mean the slow work goes to the right thread automatically. You have to tell it where to run. That's what Dispatchers are for.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dispatcher&lt;/th&gt;
&lt;th&gt;Use it for&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Dispatchers.Main&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;UI updates — text, buttons, anything visible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Dispatchers.IO&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Network calls, database reads, file access&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Dispatchers.Default&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Heavy CPU work — sorting, parsing large data&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Dispatchers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// network work on IO&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pokemon&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchPokemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pikachu"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;withContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Dispatchers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;      &lt;span class="c1"&gt;// switch to Main for UI&lt;/span&gt;
        &lt;span class="n"&gt;textView&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;withContext&lt;/code&gt; is how you switch dispatchers mid-coroutine. It's clean. It's readable. You'll use it constantly.&lt;/p&gt;




&lt;h2&gt;
  
  
  Scopes — The Lifetime of a Coroutine
&lt;/h2&gt;

&lt;p&gt;Every coroutine lives inside a scope. When the scope dies, all its coroutines die too. This is how Android prevents memory leaks automatically.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Scope&lt;/th&gt;
&lt;th&gt;Dies when&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;viewModelScope&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The ViewModel is cleared&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lifecycleScope&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The Activity or Fragment is destroyed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GlobalScope&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Never — avoid this one&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As a junior dev, you'll mostly use &lt;code&gt;viewModelScope&lt;/code&gt;. It's the safe default.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PokemonViewModel&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;loadPokemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;// auto-cancelled when ViewModel dies&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pokemon&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPokemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;_pokemon&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pokemon&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You didn't write any cleanup code. You didn't manage any threads manually. The scope handled all of it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Error Handling
&lt;/h2&gt;

&lt;p&gt;One more thing before you go build something — handle your errors.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;pokemon&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetchPokemon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;_uiState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UiState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pokemon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;_uiState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;UiState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Something went wrong"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Network calls fail. Always wrap them.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Mental Model
&lt;/h2&gt;

&lt;p&gt;This is the whole thing in one picture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Dispatchers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt; &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt;  &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="nf"&gt;coroutine&lt;/span&gt; &lt;span class="nc"&gt;PAUSES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;thread&lt;/span&gt; &lt;span class="n"&gt;goes&lt;/span&gt; &lt;span class="n"&gt;free&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;
&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="n"&gt;arrives&lt;/span&gt;  &lt;span class="err"&gt;→&lt;/span&gt;  &lt;span class="n"&gt;coroutine&lt;/span&gt; &lt;span class="nc"&gt;RESUMES&lt;/span&gt;
&lt;span class="err"&gt;│&lt;/span&gt;
&lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="nf"&gt;withContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Dispatchers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="nc"&gt;UI&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scope&lt;/strong&gt; controls the lifetime.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dispatcher&lt;/strong&gt; controls the thread.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;suspend&lt;/code&gt;&lt;/strong&gt; controls the pause.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What I Wish I Knew Earlier
&lt;/h2&gt;

&lt;p&gt;Coroutines aren't magic. They're a structured way to write async code that looks synchronous. No callbacks. No nested hell. Just top-to-bottom code that knows when to pause and when to come back.&lt;/p&gt;

&lt;p&gt;Once that clicked for me, a lot of the "why does this feel wrong" in my code started making sense.&lt;/p&gt;

&lt;p&gt;If your app ever froze and you didn't know why — now you do.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>android</category>
      <category>mobile</category>
      <category>coroutines</category>
    </item>
    <item>
      <title>As a Junior Android Dev Should I Memorize All Retrofit Code or Just Ask AI?(Interview Questions)</title>
      <dc:creator>Aalaa Fahiem </dc:creator>
      <pubDate>Wed, 17 Jun 2026 09:24:34 +0000</pubDate>
      <link>https://dev.to/itsaalaa7/as-a-junior-android-dev-should-i-memorize-all-retrofit-code-or-just-ask-aiinterview-questions-2104</link>
      <guid>https://dev.to/itsaalaa7/as-a-junior-android-dev-should-i-memorize-all-retrofit-code-or-just-ask-aiinterview-questions-2104</guid>
      <description>&lt;p&gt;I was learning Retrofit and staring at a wall of boilerplate code thinking:&lt;br&gt;
"Am I supposed to have all of this memorized?"&lt;/p&gt;
&lt;h2&gt;
  
  
  So I asked — and the honest answer surprised me. Most juniors are afraid to raise this question. Here's what I found out.
&lt;/h2&gt;
&lt;h2&gt;
  
  
  Should You Write It All From Memory?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;No. And yes. It depends on what part.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Think of it like learning to drive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You don't need to know how to build a car engine from scratch&lt;/li&gt;
&lt;li&gt;But you &lt;strong&gt;do&lt;/strong&gt; need to know how to drive, and what to do when something
breaks
There are &lt;strong&gt;3 zones&lt;/strong&gt; — and once you understand them, the whole learning process feels way less overwhelming.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Zone 1 — Let AI Write It
&lt;/h2&gt;

&lt;p&gt;Boilerplate. Nobody memorizes this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This setup in AppModule.kt? AI writes it. Every time.&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;okHttpClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OkHttpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connectionSpecs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;spec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ConnectionSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CLEARTEXT&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;protocols&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;HTTP_1_1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dns&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="err"&gt;: &lt;/span&gt;&lt;span class="nc"&gt;Dns&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;InetAddress&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Dns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SYSTEM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;Inet4Address&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ifEmpty&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Dns&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SYSTEM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lookup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hostname&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connectTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SECONDS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nobody writes this from memory. Senior devs copy-paste it from their last project. Use AI or docs — zero shame.&lt;/p&gt;




&lt;p&gt;Zone 2 — You MUST Know This Cold&lt;br&gt;
This will be tested in interviews. Learn it by heart.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Writing the API interface yourself
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;    &lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;PokeApi&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokemon"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokemonList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"offset"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;PokemonList&lt;/span&gt;

    &lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokemon/{name}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokemonInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Pokemon&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It's only ~10 lines, and it's the heart of Retrofit. Write it without help.&lt;/p&gt;
&lt;h3&gt;
  
  
  2. The basic Retrofit builder
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nc"&gt;Retrofit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://pokeapi.co/api/v2/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addConverterFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GsonConverterFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PokeApi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;4 lines. Know these by heart.&lt;/p&gt;
&lt;h3&gt;
  
  
  3. The data flow in your head
&lt;/h3&gt;

&lt;p&gt;Be able to explain this out loud without hesitation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UI → ViewModel → Repository → API interface → Retrofit builds URL → OkHttp sends it → Gson parses response → back up the chain
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4. What the annotations do
&lt;/h3&gt;

&lt;p&gt;-&lt;code&gt;@GET&lt;/code&gt; — Sends a GET request to the given endpoint&lt;br&gt;
-&lt;code&gt;@POST&lt;/code&gt; — Sends a POST request&lt;br&gt;
-&lt;code&gt;@Query&lt;/code&gt; — Adds ?key=value to the end of the URL&lt;br&gt;
-&lt;code&gt;@Path&lt;/code&gt; — Fills a {slot} inside the URL&lt;br&gt;
-&lt;code&gt;@Body&lt;/code&gt; — Sends an object as the request body (JSON)&lt;/p&gt;
&lt;h3&gt;
  
  
  5. Why &lt;code&gt;suspend&lt;/code&gt;?
&lt;/h3&gt;

&lt;blockquote&gt;
&lt;p&gt;"Network calls block the thread. &lt;code&gt;suspend&lt;/code&gt; runs them in a coroutine so the UI doesn't freeze."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Key points to remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;suspend&lt;/code&gt; means the function can pause while waiting (for network, DB, etc.) and resume later without blocking the app&lt;/li&gt;
&lt;li&gt;It does &lt;strong&gt;not&lt;/strong&gt; create a new thread — it just allows pausing and resuming&lt;/li&gt;
&lt;li&gt;You call it from a &lt;strong&gt;coroutine scope&lt;/strong&gt; like &lt;code&gt;viewModelScope.launch { }&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Retrofit API calls are &lt;code&gt;suspend&lt;/code&gt; because network requests take time&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;
  
  
  Zone 3 — Understand It, Don't Memorize It
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;
&lt;span class="c1"&gt;// Resource wrapper — understand WHY it exists&lt;/span&gt;
&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;You should be able to explain:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It wraps the result so the ViewModel can handle success, error, and loading states cleanly."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You don't need to write it from memory — but if an interviewer shows it to you, you need to explain it confidently.&lt;/p&gt;

&lt;p&gt;The mental model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;suspend              → gets data safely from slow operations
Resource             → tells the app what happened with the data
ViewModel + StateFlow → sends the new state to the UI
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Real Interview Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  🟢 Easy — Answer instantly
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;"What is Retrofit?"&lt;/strong&gt;&lt;br&gt;
A type-safe HTTP client that turns your interface methods into network calls.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What is &lt;code&gt;@GET&lt;/code&gt;?"&lt;/strong&gt;&lt;br&gt;
Annotation that makes the function send a GET request to that endpoint.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Difference between &lt;code&gt;@Query&lt;/code&gt; and &lt;code&gt;@Path&lt;/code&gt;?"&lt;/strong&gt;&lt;br&gt;
&lt;code&gt;@Path&lt;/code&gt; fills a &lt;code&gt;{slot}&lt;/code&gt; in the URL, &lt;code&gt;@Query&lt;/code&gt; adds &lt;code&gt;?key=value&lt;/code&gt; at the end.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"What is Gson converter?"&lt;/strong&gt;&lt;br&gt;
Converts JSON from the server into Kotlin/Java objects automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Why &lt;code&gt;suspend&lt;/code&gt;?"&lt;/strong&gt;&lt;br&gt;
Network calls are slow — suspend runs them off the main thread so the UI doesn't freeze.&lt;/p&gt;


&lt;h3&gt;
  
  
  🟡 Medium — Think 5 seconds, then answer
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;"What is OkHttp and how does it relate to Retrofit?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Retrofit sits &lt;em&gt;on top of&lt;/em&gt; OkHttp. Retrofit handles the interface, annotations, and conversion. OkHttp is the actual engine that opens the socket and sends/receives bytes. You can customize OkHttp (timeouts, interceptors, headers) and plug it into Retrofit.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;"What is an Interceptor?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A piece of code that runs on every request before it's sent (or every response before it arrives). Used for adding auth headers, logging, modifying requests.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;"What is the Repository pattern and why use it?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A middle layer between the ViewModel and the data source (API/database). The ViewModel doesn't care whether data comes from the internet or a local DB — it just asks the Repository. Makes testing easier and keeps code clean.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;"What does @SerializedName do?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Tells Gson to map a specific JSON key to a Kotlin field, even if the names are different. Example: JSON has &lt;code&gt;"base_experience"&lt;/code&gt; but Kotlin uses &lt;code&gt;baseExperience&lt;/code&gt;.&lt;/p&gt;


&lt;h3&gt;
  
  
  🔴 Hard — Bonus points if you nail these
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;"What happens if you don't add &lt;code&gt;suspend&lt;/code&gt; to a Retrofit function?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before Retrofit 2.6, you used &lt;code&gt;Call&amp;lt;T&amp;gt;&lt;/code&gt;. With &lt;code&gt;suspend&lt;/code&gt;, Retrofit uses coroutines natively and runs on a background thread automatically. Without &lt;code&gt;suspend&lt;/code&gt; in a coroutine-based project, you'd get a crash or have to handle &lt;code&gt;Call&amp;lt;T&amp;gt;&lt;/code&gt; manually.&lt;/p&gt;



&lt;p&gt;&lt;strong&gt;"How would you add an auth token to every request?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Add an OkHttp Interceptor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addInterceptor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;request&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;newBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Bearer $token"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;proceed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"What is &lt;a class="mentioned-user" href="https://dev.to/singleton"&gt;@singleton&lt;/a&gt; in Hilt and why does Retrofit need it?"&lt;/p&gt;

&lt;p&gt;Creating a Retrofit instance is expensive — it sets up connection pools, parsers, and configuration. &lt;a class="mentioned-user" href="https://dev.to/singleton"&gt;@singleton&lt;/a&gt; tells Hilt to create it once and reuse the same instance everywhere.&lt;/p&gt;

&lt;p&gt;Key points:&lt;/p&gt;

&lt;p&gt;Without &lt;a class="mentioned-user" href="https://dev.to/singleton"&gt;@singleton&lt;/a&gt;, Hilt may create multiple Retrofit objects — wasteful and slow&lt;/p&gt;

&lt;h2&gt;
  
  
  In Android apps, we keep one Retrofit instance for the whole app and inject it wherever needed
&lt;/h2&gt;

&lt;p&gt;TL;DR&lt;br&gt;
Zone 1 — Boilerplate → Let AI write it. Always.&lt;br&gt;
Zone 2 — Core concepts → Memorize it. Interviews will ask.&lt;br&gt;
Zone 3 — Patterns → Understand the why, not the exact syntax.&lt;br&gt;
The goal isn't to be a human compiler. It's to understand the system well enough to drive it, debug it, and explain it — even when you don't know every line by heart.&lt;/p&gt;

&lt;p&gt;Still learning Kotlin and Android as a junior dev — writing about it as I go. If this helped, I'd love to hear what you're building! 👇&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>beginners</category>
      <category>retrofit</category>
      <category>android</category>
    </item>
    <item>
      <title>The Complete Retrofit Lifecycle in a Real Android App</title>
      <dc:creator>Aalaa Fahiem </dc:creator>
      <pubDate>Tue, 16 Jun 2026 15:34:39 +0000</pubDate>
      <link>https://dev.to/itsaalaa7/the-complete-retrofit-lifecycle-in-a-real-android-app-4je2</link>
      <guid>https://dev.to/itsaalaa7/the-complete-retrofit-lifecycle-in-a-real-android-app-4je2</guid>
      <description>&lt;p&gt;Most Retrofit tutorials show you isolated code snippets. This article traces the &lt;strong&gt;entire lifecycle&lt;/strong&gt; of a Retrofit call — from app startup to data appearing on screen — using real code from a Pokedex app built with Kotlin, Hilt, Coroutines, and Jetpack Compose.&lt;/p&gt;

&lt;h2&gt;
  
  
  By the end, you'll understand every link in the chain!
&lt;/h2&gt;

&lt;h2&gt;
  
  
  What Is Retrofit?
&lt;/h2&gt;

&lt;p&gt;Retrofit is a type-safe HTTP client for Android and Java. Instead of writing raw HTTP requests, you describe your API as a Kotlin &lt;strong&gt;interface&lt;/strong&gt;, and Retrofit generates the networking code for you behind the scenes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;PokeApi&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokemon"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokemonList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"offset"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;PokemonList&lt;/span&gt;

    &lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokemon/{name}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokemonInfo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;@Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Pokemon&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No manual &lt;code&gt;HttpURLConnection&lt;/code&gt;, no manual JSON parsing. Retrofit handles building the request and Gson (or Moshi) handles converting the JSON response into your Kotlin objects.&lt;/p&gt;




&lt;h2&gt;
  
  
  The 3 Building Blocks
&lt;/h2&gt;

&lt;p&gt;Every Retrofit setup needs exactly three things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The Interface&lt;/strong&gt; — what requests can be made&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Builder&lt;/strong&gt; — where to send them and how to parse responses&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Response model&lt;/strong&gt; — what shape the data comes back in
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;retrofit&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Retrofit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://pokeapi.co/api/v2/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addConverterFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GsonConverterFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;api&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;retrofit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PokeApi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The Full Lifecycle, Step by Step
&lt;/h2&gt;

&lt;p&gt;Here's the entire request lifecycle traced through a real app architecture: &lt;strong&gt;Compose UI → ViewModel → Repository → Retrofit → OkHttp → Server → Gson → back up the chain.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1 — App Starts, Hilt Wakes Up
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@HiltAndroidApp&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PokedexApplication&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onCreate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nc"&gt;Timber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;plant&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Timber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;DebugTree&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@HiltAndroidApp&lt;/code&gt; tells Hilt to start managing dependency injection across the app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2 — Hilt Builds Retrofit Once (Singleton)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Module&lt;/span&gt;
&lt;span class="nd"&gt;@InstallIn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;SingletonComponent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;AppModule&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@Singleton&lt;/span&gt;
    &lt;span class="nd"&gt;@Provides&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;providePokeApi&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;PokeApi&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;okHttpClient&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;OkHttpClient&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connectTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SECONDS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TimeUnit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;SECONDS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;retryOnConnectionFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addInterceptor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;request&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;newBuilder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"User-Agent"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Mozilla/5.0 ..."&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Accept"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="n"&gt;chain&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;proceed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Retrofit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;baseUrl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Constants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;BASE_URL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;okHttpClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addConverterFactory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GsonConverterFactory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PokeApi&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;@Singleton&lt;/span&gt;
    &lt;span class="nd"&gt;@Provides&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;providePokemonRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PokeApi&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PokemonRepository&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Building a Retrofit instance is expensive — connection pools, thread pools, parsers. &lt;code&gt;@Singleton&lt;/code&gt; ensures it's built &lt;strong&gt;once&lt;/strong&gt; and reused everywhere, instead of recreated per screen.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3 — ViewModel Is Created, Requests Data Immediately
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@HiltViewModel&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PokemonListViewModel&lt;/span&gt; &lt;span class="nd"&gt;@Inject&lt;/span&gt; &lt;span class="k"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PokemonRepository&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ViewModel&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;pokemonList&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;mutableStateOf&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokedexListEntry&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;(&lt;/span&gt;&lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="py"&gt;isLoading&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;mutableStateOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nf"&gt;init&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;loadPokemonPaginated&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;loadPokemonPaginated&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
            &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;repository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPokemonList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;PAGE_SIZE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;curPage&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt; &lt;span class="nc"&gt;PAGE_SIZE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="c1"&gt;// handle result below&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hilt injects the &lt;code&gt;PokemonRepository&lt;/code&gt; automatically — no manual instantiation needed. &lt;code&gt;viewModelScope.launch&lt;/code&gt; starts a coroutine so the network call doesn't block the main thread.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4 — Repository Calls the API and Handles Failure
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@Singleton&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PokemonRepository&lt;/span&gt; &lt;span class="nd"&gt;@Inject&lt;/span&gt; &lt;span class="k"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;api&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;PokeApi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokemonList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PokemonList&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;response&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getPokemonList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Couldn't load Pokemon"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Repository pattern decouples the ViewModel from the data source. The ViewModel doesn't know — or care — whether data comes from a network call, a cache, or a database.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 5 — Retrofit Builds the URL and OkHttp Sends It
&lt;/h3&gt;

&lt;p&gt;Given:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"pokemon"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getPokemonList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"limit"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;limit&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;@Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"offset"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;PokemonList&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Calling &lt;code&gt;getPokemonList(20, 0)&lt;/code&gt; produces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://pokeapi.co/api/v2/pokemon?limit=20&amp;amp;offset=0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OkHttp (the engine underneath Retrofit) opens the connection, attaches any interceptor headers, and sends the request over the wire.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 6 — Server Responds With JSON
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1302&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"next"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://pokeapi.co/api/v2/pokemon?offset=20&amp;amp;limit=20"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"previous"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"results"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bulbasaur"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://pokeapi.co/api/v2/pokemon/1/"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 7 — Gson Converts JSON Into Kotlin Objects
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;PokemonList&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nd"&gt;@SerializedName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"count"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;@SerializedName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"next"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;next&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;@SerializedName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"previous"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;previous&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Any&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;@SerializedName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"results"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;results&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;@SerializedName&lt;/code&gt; maps a JSON key to a Kotlin property — essential when the server uses &lt;code&gt;snake_case&lt;/code&gt; and your code uses &lt;code&gt;camelCase&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 8 — Result Flows Back Up, Wrapped in a Sealed Class
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;sealed&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Success&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Loading&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;T&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This wrapper lets the ViewModel handle success, error, and loading as explicit states rather than nullable guesswork.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 9 — ViewModel Updates State, Compose Redraws
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;when&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Success&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;pokemonList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;results&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="cm"&gt;/* map to UI model */&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;is&lt;/span&gt; &lt;span class="nc"&gt;Resource&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;loadError&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="o"&gt;!!&lt;/span&gt;
        &lt;span class="n"&gt;isLoading&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;pokemonList&lt;/code&gt; is a Compose &lt;code&gt;mutableStateOf&lt;/code&gt;, the moment it changes, any composable reading it recomposes automatically. No manual UI refresh needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Full Chain, Visualized
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;App starts → Hilt builds Retrofit + Repository (once)
    ↓
ViewModel created → Repository injected → init{} fires
    ↓
viewModelScope.launch → repository.getPokemonList()
    ↓
api.getPokemonList() → Retrofit builds the URL
    ↓
OkHttp attaches headers, sends the HTTP request
    ↓
Server responds with JSON
    ↓
Gson converts JSON → Kotlin data class
    ↓
Repository wraps it in Resource.Success / Resource.Error
    ↓
ViewModel updates mutableStateOf
    ↓
Compose recomposes → user sees the data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frpiqvdha75jggu0nnw88.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frpiqvdha75jggu0nnw88.jpg" alt=" " width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>retrofit</category>
      <category>mobile</category>
      <category>android</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Jetpack Compose Navigation(Interview Prep)</title>
      <dc:creator>Aalaa Fahiem </dc:creator>
      <pubDate>Wed, 10 Jun 2026 17:58:39 +0000</pubDate>
      <link>https://dev.to/itsaalaa7/jetpack-compose-navigationinterview-prep-2bc7</link>
      <guid>https://dev.to/itsaalaa7/jetpack-compose-navigationinterview-prep-2bc7</guid>
      <description>&lt;p&gt;About this article: This is based on hands-on review of a real project. The goal is understanding over memorization — because interviewers can always tell the difference.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Screen.Details.route vs Screen.Details.createRoute(name) — What's the Difference?
This trips up a lot of people because both look like they're "the route." They're not.
Screen.Details.route is a template — it contains a placeholder:
"details/{pokemonName}"
You use this when registering the destination inside NavHost, so the navigation system knows the shape of the route and which argument to extract.
Screen.Details.createRoute(name) produces a real URL like:
"details/Pikachu"
You use this when navigating — navController.navigate(...) needs an actual address with real data embedded.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;⚠️ What breaks if you use .route in both places?&lt;br&gt;
NavHost would still match the route, but the extracted argument would be the literal string "{pokemonName}" instead of the actual Pokémon name. No crash — just silent wrong data.&lt;/p&gt;

&lt;p&gt;Key Rule → Template for registration. Real URL for navigation.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Defining a Route in NavConstants Doesn't Register It
If NavConstants.kt defines Search and Settings but they never appear in AppNavigation, what happens when you call navController.navigate(Screen.Search.route)?
The app crashes at runtime:
IllegalArgumentException:
"navigation destination 'search' is unknown to this NavController"
Here's why this confuses people:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;NavConstants.kt is just a dictionary of strings — defining a route there costs nothing and guarantees nothing.&lt;br&gt;
AppNavigation.kt is where destinations actually exist — a route only becomes real when it has a matching composable() registered inside NavHost.&lt;/p&gt;

&lt;p&gt;NavConstants protects you from typos. It cannot protect you from forgetting to register the destination. Both mistakes crash the app.&lt;br&gt;
Key Rule → You need both — the string AND the composable() registration.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;nullable = false and if (name != null) Are Not Contradicting Each Other&lt;br&gt;
You might see navArgument("pokemonName") { nullable = false } declared, and then right below it, the code still does ?. and if (name != null). That looks contradictory. It's not.&lt;br&gt;
They operate at completely different layers:&lt;br&gt;
nullable = falseif (name != null)LayerNavigation systemKotlin type systemWhen it actsRuntime navigationCompile timeWhat it doesCrashes if arg is missingSatisfies the compiler&lt;br&gt;
Bundle.getString() always returns String? regardless of your navigation contract — the Kotlin compiler has no idea about navArgument rules. The null check is there to satisfy the compiler, not because you genuinely expect null at runtime.&lt;br&gt;
Key Rule → Navigation contract and Kotlin types are separate systems that don't talk to each other.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;popBackStack() vs navigate(Home) — Never Substitute One for the Other&lt;br&gt;
Both can result in the user seeing the Home screen. But what they do to the back stack is completely different.&lt;br&gt;
popBackStack() removes the current screen and reveals what was beneath it:&lt;br&gt;
Before: [Home, Details]&lt;br&gt;
After:  [Home]           ← Details removed, Home revealed&lt;br&gt;
navigate(Screen.Home.route) pushes a new Home screen on top:&lt;br&gt;
Before: [Home, Details]&lt;br&gt;
After:  [Home, Details, Home]  ← new Home added&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;⚠️ Why it matters: After navigate(), pressing Back lands the user on Details — a screen they thought they left. Press again, they're on the original Home. The back stack grows silently and creates a broken experience.&lt;/p&gt;

&lt;p&gt;Key Rule → Use popBackStack() to go back. Use navigate() to go forward. Never substitute.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;When Do You Actually Need backStackEntry?&lt;br&gt;
Inside NavHost, the lambda for composable() can optionally name its parameter backStackEntry. Some composables do, some don't. Why?&lt;br&gt;
backStackEntry gives access to arguments embedded in the route. Screens with static routes like "home" or "profile" have no arguments — there's nothing to extract, so backStackEntry is ignored and not named.&lt;br&gt;
Details has {pokemonName} in its route. When navController.navigate("details/Pikachu") was called, the navigation system stored "Pikachu" in the backStackEntry's argument bundle. backStackEntry.arguments?.getString("pokemonName") pulls it back out.&lt;br&gt;
Key Rule → You only need backStackEntry when the route carries arguments.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The Three Layers of Compose Navigation&lt;br&gt;
Understanding these three layers separately makes everything else click.&lt;br&gt;
🔵 Navigation System Layer&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Rules enforced at runtime by NavHost/NavController&lt;br&gt;
Every route navigated to must be registered with composable() — otherwise crash&lt;br&gt;
nullable = false is a contract here — violate it, it throws&lt;br&gt;
Doesn't know Kotlin types&lt;/p&gt;

&lt;p&gt;🟣 Kotlin Type System Layer&lt;/p&gt;

&lt;p&gt;Rules enforced at compile time by the compiler&lt;br&gt;
Bundle.getString() returns String? — compiler forces null handling&lt;br&gt;
Has no awareness of navigation contracts or navArgument rules&lt;br&gt;
Doesn't know your back stack state&lt;/p&gt;

&lt;p&gt;🟢 Back Stack Layer&lt;/p&gt;

&lt;p&gt;Runtime state — the history of where the user has been&lt;br&gt;
navigate() always pushes a new destination&lt;br&gt;
popBackStack() always removes the top destination&lt;br&gt;
System Back button also pops it&lt;br&gt;
Keeps growing until something pops it&lt;/p&gt;

&lt;p&gt;A quick comparison between the two that are most often confused:&lt;br&gt;
Kotlin Type SystemBack StackNatureCompile-time rulesRuntime stateCares aboutNullability, typesNavigation historyExamplegetString() returns String?[Home, Details] grows with navigate()&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigation Hoisting — Screens Get Lambdas, Not NavController
Navigation hoisting means screen composables receive navigation actions as lambda parameters instead of having direct access to navController.
kotlin// AppNavigation owns navController, passes actions as lambdas
HomeScreen(
onNavigateToProfile = { navController.navigate(Screen.Profile.route) },
onPokemonClick = { name -&amp;gt; navController.navigate(Screen.Details.createRoute(name)) }
)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;// HomeScreen knows nothing about navController&lt;br&gt;
@Composable&lt;br&gt;
fun HomeScreen(&lt;br&gt;
    onNavigateToProfile: () -&amp;gt; Unit,&lt;br&gt;
    onPokemonClick: (String) -&amp;gt; Unit&lt;br&gt;
)&lt;br&gt;
Why this pattern exists:&lt;/p&gt;

&lt;p&gt;Separates UI logic from navigation logic&lt;br&gt;
HomeScreen becomes testable without a real NavController&lt;br&gt;
Screens stay reusable — they don't care how navigation is implemented&lt;/p&gt;

&lt;p&gt;Notice the subtle difference between the two lambdas above:&lt;/p&gt;

&lt;p&gt;onNavigateToProfile: () -&amp;gt; Unit — no data needed; Profile renders itself without input&lt;br&gt;
onPokemonClick: (String) -&amp;gt; Unit — carries the Pokémon name; Details needs it to know what to show&lt;/p&gt;

&lt;p&gt;Key Rule → Screens get lambdas, not navController.&lt;/p&gt;

&lt;p&gt;Quick Reference Card&lt;br&gt;
ConceptRule.route vs createRoute()Template to register, real URL to navigateUnregistered routeCrashes at runtime with IllegalArgumentExceptionnullable = falseNavigation contract — not Kotlin null safetypopBackStack() vs navigate()Go back vs go forward — never substitutebackStackEntryOnly needed when the route carries argumentsNavigation hoistingScreens get lambdas, not navControllerNavConstantsPrevents typos, not missing registrations&lt;/p&gt;

&lt;p&gt;Found this useful? These notes came from reviewing a real Compose project end-to-end — the kind of hands-on work that makes interview answers actually stick.&lt;/p&gt;

</description>
      <category>interview</category>
      <category>android</category>
      <category>navigation</category>
      <category>beginners</category>
    </item>
    <item>
      <title>I Thought Passing Data Between Screens Would Be Easy. It Was Not.</title>
      <dc:creator>Aalaa Fahiem </dc:creator>
      <pubDate>Tue, 09 Jun 2026 18:10:50 +0000</pubDate>
      <link>https://dev.to/itsaalaa7/i-thought-passing-data-between-screens-would-be-easy-it-was-not-2ek4</link>
      <guid>https://dev.to/itsaalaa7/i-thought-passing-data-between-screens-would-be-easy-it-was-not-2ek4</guid>
      <description>&lt;p&gt;Let me tell you exactly how my week went.&lt;/p&gt;

&lt;p&gt;Day one: I tried to pass a full Product object through navigation instead of just the ID. The app compiled fine. Then it crashed the moment I tapped a product. No helpful message. Just gone.&lt;/p&gt;

&lt;p&gt;Day two: I passed the ID correctly this time — I'd learned from the last article — but after navigating to the detail screen, I had this weird feeling: where is the data actually living right now? I couldn't explain it. I just trusted that &lt;code&gt;backStackEntry.arguments&lt;/code&gt; would have it, and sometimes it did, and sometimes I got null, and I had no idea why.&lt;/p&gt;

&lt;p&gt;Day three: I figured out how to send data forward fine. But then I needed to send data back — back to the previous screen after the user made a selection. And I stared at the screen for a very long time because nothing in what I'd learned prepared me for that direction.&lt;/p&gt;

&lt;p&gt;All three of these happened to me within the same week on the same project. And they all came from the same root problem: I was guessing at how navigation handles data instead of actually understanding it.&lt;/p&gt;

&lt;p&gt;This article is me stopping the guessing — for both of us.&lt;/p&gt;

&lt;p&gt;We're continuing with the e-commerce app from the last article: Product List → Product Detail → Cart. If you haven't read that one, the short version is: we have a sealed class for routes, a NavHost, and a NavController. That setup stays exactly the same. What we're doing today is understanding everything that happens when data moves between those screens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why You Can't Just Pass a Full Object
&lt;/h2&gt;

&lt;p&gt;This is where most people start — and where most people hit their first wall.&lt;/p&gt;

&lt;p&gt;You have a Product. The detail screen needs a Product. Why not just... pass it?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This feels logical. Don't do it.&lt;/span&gt;
&lt;span class="n"&gt;navController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ProductDetail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's why it doesn't work.&lt;/p&gt;

&lt;p&gt;Navigation routes are strings. That's the core of the system. When you call &lt;code&gt;navController.navigate(...)&lt;/code&gt;, you're navigating to a string address — like a URL. &lt;code&gt;"product_detail/1"&lt;/code&gt; is a valid address. &lt;code&gt;"product_detail/Product(id=1, name=Air Max 90, price=120.0)"&lt;/code&gt; is not.&lt;/p&gt;

&lt;p&gt;The system wasn't built to serialize and deserialize objects through a URL. And even if you found a workaround — JSON encoding, for example — you'd be fighting the architecture instead of using it.&lt;/p&gt;

&lt;p&gt;There's also a deeper reason. Screens in a navigation stack can be recreated from their route alone. If Android kills your process and the user comes back, it needs to be able to rebuild the back stack. A string ID can do that. A full object floating in memory cannot.&lt;/p&gt;

&lt;p&gt;So the rule is simple: pass the minimum. Pass the ID.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// This is the right mental model.&lt;/span&gt;
&lt;span class="c1"&gt;// The ID is just an address. The screen goes and fetches the full data.&lt;/span&gt;
&lt;span class="n"&gt;navController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ProductDetail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Primitives: What's Actually Happening
&lt;/h2&gt;

&lt;p&gt;In the last article, we passed a &lt;code&gt;productId: Int&lt;/code&gt; and moved on. Let's slow down and actually read what's happening, because this is where the null mystery comes from.&lt;/p&gt;

&lt;p&gt;Here's the route definition:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;ProductDetail&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"product_detail/{productId}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;createRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"product_detail/$productId"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;{productId}&lt;/code&gt; in the route template is a placeholder. It's saying: "when this screen is registered, expect something called &lt;code&gt;productId&lt;/code&gt; to be embedded in the URL."&lt;/p&gt;

&lt;p&gt;When you navigate with &lt;code&gt;"product_detail/1"&lt;/code&gt;, the system parses the URL, matches the pattern, and extracts &lt;code&gt;1&lt;/code&gt; as the value of &lt;code&gt;productId&lt;/code&gt;. It holds onto it in a Bundle called &lt;code&gt;arguments&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That Bundle lives inside the &lt;code&gt;NavBackStackEntry&lt;/code&gt; — the entry for this specific screen in the back stack. It's tied to that entry's lifecycle. As long as the screen is in the back stack, the arguments are there.&lt;/p&gt;

&lt;p&gt;Here's the NavHost registration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;composable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ProductDetail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;navArgument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"productId"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;NavType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IntType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;backStackEntry&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;productId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;backStackEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;getInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"productId"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;?:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="nc"&gt;ProductDetailScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;productId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;productId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;navController&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;navController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;navArgument&lt;/code&gt; block isn't just decoration. It tells the system the type of the argument. This matters because the URL is a string — &lt;code&gt;"product_detail/1"&lt;/code&gt; — and the system needs to know to convert &lt;code&gt;"1"&lt;/code&gt; into an &lt;code&gt;Int&lt;/code&gt;, not leave it as &lt;code&gt;"1"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you forget the &lt;code&gt;arguments&lt;/code&gt; list, or name the argument differently here than in the route template, the system can't match them. The value doesn't get extracted. &lt;code&gt;backStackEntry.arguments?.getInt("productId")&lt;/code&gt; returns null. That's the mystery.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;?: 0&lt;/code&gt; at the end is a fallback — if for any reason the argument is missing, we default to &lt;code&gt;0&lt;/code&gt; instead of crashing. In a real app, you'd probably show an error state instead, but the pattern is the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optional Arguments and Default Values
&lt;/h2&gt;

&lt;p&gt;Here's something the documentation doesn't make obvious: arguments can be optional.&lt;/p&gt;

&lt;p&gt;By default, a &lt;code&gt;{placeholder}&lt;/code&gt; in a route is required. If it's not there, navigation fails. But sometimes you have a screen that works fine without certain data — a search screen that can open with or without an initial query, for example.&lt;/p&gt;

&lt;p&gt;In our e-commerce app, let's say the Cart screen can optionally receive a &lt;code&gt;couponCode&lt;/code&gt; that pre-fills a discount field:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;object&lt;/span&gt; &lt;span class="nc"&gt;Cart&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"cart?couponCode={couponCode}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;createRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;couponCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;couponCode&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="s"&gt;"cart?couponCode=$couponCode"&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="s"&gt;"cart"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things changed here. First, the syntax: optional arguments use query parameter format — &lt;code&gt;?key={value}&lt;/code&gt; — instead of path format — &lt;code&gt;/{value}&lt;/code&gt;. This is how the system knows an argument is optional.&lt;/p&gt;

&lt;p&gt;Second, &lt;code&gt;createRoute()&lt;/code&gt; now has two forms: one with a coupon code, one without. Both navigate to "the cart screen" — just with different context.&lt;/p&gt;

&lt;p&gt;The NavHost registration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;composable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;arguments&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;listOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nf"&gt;navArgument&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"couponCode"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;type&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;NavType&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;StringType&lt;/span&gt;
            &lt;span class="n"&gt;nullable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
            &lt;span class="n"&gt;defaultValue&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;backStackEntry&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;couponCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;backStackEntry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;arguments&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;getString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"couponCode"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nc"&gt;CartScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;couponCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;couponCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;navController&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;navController&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;nullable = true&lt;/code&gt; and &lt;code&gt;defaultValue = null&lt;/code&gt; are what make this argument optional. Without them, the system would require the argument to be present.&lt;/p&gt;

&lt;p&gt;Now you can navigate to the cart two ways:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// From ProductDetail — no coupon&lt;/span&gt;
&lt;span class="n"&gt;navController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRoute&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

&lt;span class="c1"&gt;// From a promotional banner — with a coupon pre-filled&lt;/span&gt;
&lt;span class="n"&gt;navController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;navigate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Cart&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;couponCode&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"SAVE20"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both work. The cart screen handles both cases based on whether &lt;code&gt;couponCode&lt;/code&gt; is null or not.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>android</category>
      <category>beginners</category>
      <category>navigation</category>
    </item>
    <item>
      <title>The Junior Dev Who Never Had to Google Anything — Is That a Superpower or a Problem?</title>
      <dc:creator>Aalaa Fahiem </dc:creator>
      <pubDate>Tue, 09 Jun 2026 17:54:30 +0000</pubDate>
      <link>https://dev.to/itsaalaa7/the-junior-dev-who-never-had-to-google-anything-is-that-a-superpower-or-a-problem-1hf3</link>
      <guid>https://dev.to/itsaalaa7/the-junior-dev-who-never-had-to-google-anything-is-that-a-superpower-or-a-problem-1hf3</guid>
      <description>&lt;p&gt;Let me tell you four things that happened to me.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One.&lt;/strong&gt; I sat in a mock interview. The question was basic — embarrassingly basic for someone who'd been coding for months. And I went completely blank. Not because I didn't know the concept. I did. I'd used it dozens of times. But every single time I'd used it, I'd asked AI first. I never had to hold the answer in my own head. So when someone asked me to pull it out of my head on the spot, there was nothing there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two.&lt;/strong&gt; I looked at a file in my own project — code I wrote, in a project I built — and I couldn't explain what one of the functions was doing. I remembered asking AI for it. I remembered copying it. I remembered it working. But the understanding? I never actually got it. I just got the output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Three.&lt;/strong&gt; A friend of mine — also a junior — lost internet access for two hours during a coding session. He sent me a message that I still think about: &lt;em&gt;"I don't know how to do anything without it."&lt;/em&gt; He wasn't joking. He sat there until the connection came back.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Four.&lt;/strong&gt; And me. All three of those stories are me.&lt;/p&gt;




&lt;p&gt;I want to be honest with you about something that nobody in the dev community is saying loudly enough:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AI tools are making some of us faster. And quietly making others of us hollow.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;And the terrifying part is — you can't always tell which one you are until it's too late.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Superpower Feeling Is Real
&lt;/h2&gt;

&lt;p&gt;I'm not going to pretend it isn't.&lt;/p&gt;

&lt;p&gt;The first time I used AI to help me with code, it felt like cheating in the best possible way. I described a problem in plain English. It gave me working code in seconds. I integrated it, it worked, I moved on.&lt;/p&gt;

&lt;p&gt;That speed is real. That efficiency is real. If you're a junior dev and you're not using AI tools at all, you're probably moving slower than you need to.&lt;/p&gt;

&lt;p&gt;But here's the thing about superpowers in movies — they always come with a cost the hero doesn't notice until a critical moment. And for a lot of junior devs right now, that cost is silently compounding in the background.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's Actually Happening When You Over-Rely on AI
&lt;/h2&gt;

&lt;p&gt;When you ask AI to write your code, explain your error, and structure your logic — every single time, without stopping to struggle first — you're skipping something that has no shortcut.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You're skipping the part where your brain builds the map.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Learning to code isn't just about collecting solutions. It's about building a mental model of how things connect. Why this pattern exists. What breaks when you do it differently. Why the error says what it says.&lt;/p&gt;

&lt;p&gt;That mental model only gets built through friction. Through sitting with a problem long enough that your brain has to construct a path. Through being wrong, running it, seeing it fail, and figuring out why.&lt;/p&gt;

&lt;p&gt;When AI removes all the friction, it also removes all the map-building.&lt;/p&gt;

&lt;p&gt;You end up with a project full of code that works. And a brain full of gaps that don't show up until someone asks you to think without a safety net.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Test That Reveals Everything
&lt;/h2&gt;

&lt;p&gt;There's a simple test I think every junior dev should run on themselves right now.&lt;/p&gt;

&lt;p&gt;Close the AI. Close Stack Overflow. Open a blank file.&lt;/p&gt;

&lt;p&gt;Now build the simplest thing you built last week. From memory. Without help.&lt;/p&gt;

&lt;p&gt;Can you do it?&lt;/p&gt;

&lt;p&gt;If the answer is yes — great. You're using AI as a tool and you're still the one doing the thinking.&lt;/p&gt;

&lt;p&gt;If the answer is "I don't even know where to start" — that's not a skill level problem. That's a dependency problem. And it's worth taking seriously.&lt;/p&gt;

&lt;p&gt;I ran this test on myself after that interview. The results were uncomfortable. I could describe what the code should do. I couldn't write it without reaching for help every three minutes.&lt;/p&gt;

&lt;p&gt;That's not a junior dev using AI well. That's a junior dev who learned to prompt AI well. Those are not the same thing.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Nobody Talks About This
&lt;/h2&gt;

&lt;p&gt;Part of why this conversation doesn't happen enough is that the output looks identical.&lt;/p&gt;

&lt;p&gt;Your code works. Your project runs. Your GitHub is active. From the outside — and honestly, from the inside too — everything looks fine.&lt;/p&gt;

&lt;p&gt;The gap only becomes visible in high-pressure moments. An interview. A pair programming session. A production bug at 2am when the AI gives you three different conflicting answers and you need to actually &lt;em&gt;understand&lt;/em&gt; what's happening to fix it.&lt;/p&gt;

&lt;p&gt;That's when you find out what you actually know versus what you only knew how to ask for.&lt;/p&gt;




&lt;h2&gt;
  
  
  I'm Not Saying Stop Using AI
&lt;/h2&gt;

&lt;p&gt;That would be a ridiculous take and I won't make it.&lt;/p&gt;

&lt;p&gt;AI is here. It's useful. The developers who learn to work &lt;em&gt;with&lt;/em&gt; it effectively will have a real edge. That's just true.&lt;/p&gt;

&lt;p&gt;But there's a version of using AI that makes you better — and a version that quietly replaces your thinking without you noticing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The version that makes you better looks like this:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You try first. You sit with the problem for at least fifteen minutes. You form a hypothesis. You write something, even if it's wrong. &lt;em&gt;Then&lt;/em&gt; you ask AI — not for the answer, but to check your reasoning, fill a specific gap, or explain why your approach broke.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The version that hollows you out looks like this:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You see the problem. You open AI. You paste the output. You move on.&lt;/p&gt;

&lt;p&gt;One of those builds a developer. The other builds someone who can only code when the internet is on.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Uncomfortable Question
&lt;/h2&gt;

&lt;p&gt;If you're a junior dev reading this — and I'm talking to the version of you that's being honest right now, not the version that's performing confidence online — ask yourself this:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;When was the last time you solved something hard completely on your own?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Not with AI. Not with Stack Overflow. Not with a YouTube tutorial open in the background.&lt;/p&gt;

&lt;p&gt;Just you, the problem, and enough patience to sit with the discomfort until something clicked.&lt;/p&gt;

&lt;p&gt;If you can remember that moment clearly, you're probably okay.&lt;/p&gt;

&lt;p&gt;If you're struggling to remember the last time it happened — that's the answer.&lt;/p&gt;




&lt;p&gt;The junior dev who never has to Google anything isn't a 10x developer.&lt;/p&gt;

&lt;p&gt;They might just be someone who got very good at delegating their thinking.&lt;/p&gt;

&lt;p&gt;And that's worth examining — before an interview, a job, or a real production problem does it for you.&lt;/p&gt;




&lt;p&gt;Drop your honest answer in the comments. When was the last time you solved something without AI? I'm genuinely asking — not to judge, because I had to think hard about my own answer too.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>discuss</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
