<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Ken Ruiz Inoue on Medium]]></title>
        <description><![CDATA[Stories by Ken Ruiz Inoue on Medium]]></description>
        <link>https://medium.com/@kenruizinoue?source=rss-ee0f8a90df77------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/2*fabwUWLRMME8RMkQ4pVOeg.png</url>
            <title>Stories by Ken Ruiz Inoue on Medium</title>
            <link>https://medium.com/@kenruizinoue?source=rss-ee0f8a90df77------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 04:29:42 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@kenruizinoue/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[How to Resume Your Mac’s Claude Code Sessions From Your Phone]]></title>
            <link>https://medium.com/deuk/how-to-resume-your-macs-claude-code-sessions-from-your-phone-177532438ec8?source=rss-ee0f8a90df77------2</link>
            <guid isPermaLink="false">https://medium.com/p/177532438ec8</guid>
            <category><![CDATA[claude-code]]></category>
            <category><![CDATA[apple]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[anthropic-claude]]></category>
            <dc:creator><![CDATA[Ken Ruiz Inoue]]></dc:creator>
            <pubDate>Thu, 09 Jul 2026 15:29:31 GMT</pubDate>
            <atom:updated>2026-07-09T15:29:31.052Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*EwM9zzyEXppwrD9ll67XVg.png" /></figure><p>I use Claude Code all day on my Mac. It writes code, reviews my pull requests, creates tests, refactors messy files, even generates PDFs. The thing is, that power was chained to my desk. The moment I stepped away, it stayed behind.</p><p>So I built a way to carry it in my pocket, and I open sourced it so you can too. My app, <a href="https://github.com/kenruizinoue/another_bridge_mobile">AnotherBridgeMobile</a>, turns your phone into a remote control for the Claude Code running on your Mac. You type a message on the phone, your Mac does the actual work, and the result streams back to you. You can be on the couch, on a walk, or waiting for a coffee, and your Mac is still working for you. You can ask it to review a pull request, write a test, refactor a file, or turn some notes into a PDF, and it happens on the Mac while your phone stays in your pocket.</p><p>Here is the part that makes it possible. The sync does not come from the official Claude app. That app cannot see the sessions living in your Mac’s terminal, and it cannot continue one to actually edit files on your machine. What powers this setup is a small server you run on your own Mac, called <a href="https://github.com/kenruizinoue/another_bridge">AnotherBridge</a>. It exposes the local Claude Code to your phone, so a session you started at your desk can keep going from anywhere, with full access to your real project files. That flexibility is the whole point, and it is something the native app simply does not offer.</p><p>A fair question came up recently: how can you trust that the phone is showing the real, up-to-date state of your work, with nothing skipped? That worry is the whole reason this app exists, because even one dropped line can throw off an entire session. The answer is that the phone never stores its own version of the conversation. It pulls directly from the record your Mac keeps as it works, and shows you that record as it grows. There is no shortened recap and no side channel. What you read on the phone is the exact same session running on your Mac. And if the Mac is still in the middle of a reply when you open the app, it does not hand you a half-finished message and stop. It tells you the Mac is still thinking, then fills in the rest as it arrives. Nothing gets dropped, and nothing gets shortened.</p><p>And when you sit back down at your desk, you can pick the session right back up with something likeclaude code --resume and keep going in your terminal. If you want that to feel automatic, you can set up your own command, something like /sync-mobile, to handle the syncing for you. It is not built in, but it is easy to add (just ask the agent to do it for you), and once you do, your terminal and your phone always land on the same session no matter where you left off.</p><p>This changed how I work more than I expected. I start a session at my computer and finish it from anywhere, and my Mac keeps going the whole time.</p><p>Both repos are open source under MIT, so you can run this today:</p><p>App: <a href="https://github.com/kenruizinoue/another_bridge_mobile">github.com/kenruizinoue/another_bridge_mobile</a><br>Bridge: <a href="https://github.com/kenruizinoue/another_bridge">github.com/kenruizinoue/another_bridge</a></p><p>Run the bridge on your Mac, point the app at it, and your Claude Code goes wherever you do. If the setup feels unfamiliar, you do not have to do it alone. Paste the README into Claude Code or any coding agent and ask it to walk you through the steps, or just reach out to me. And please read the security notes <a href="https://github.com/kenruizinoue/another_bridge/blob/dev/docs/SECURITY.md">here</a> before you expose it beyond your home network.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=177532438ec8" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deuk/how-to-resume-your-macs-claude-code-sessions-from-your-phone-177532438ec8">How to Resume Your Mac’s Claude Code Sessions From Your Phone</a> was originally published in <a href="https://medium.com/deuk">AnotherAgent</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Practice Algorithms and DS the Structured Way]]></title>
            <link>https://medium.com/deuk/practice-algorithms-and-ds-the-structured-way-147f881eb243?source=rss-ee0f8a90df77------2</link>
            <guid isPermaLink="false">https://medium.com/p/147f881eb243</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[python-programming]]></category>
            <category><![CDATA[data-structures]]></category>
            <category><![CDATA[algorithms]]></category>
            <category><![CDATA[data-structure-algorithm]]></category>
            <dc:creator><![CDATA[Ken Ruiz Inoue]]></dc:creator>
            <pubDate>Tue, 07 Jul 2026 19:34:52 GMT</pubDate>
            <atom:updated>2026-07-07T19:34:52.669Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*UmyHTdAH7_BnpHeNSAlQVA.png" /></figure><p>Most algorithm practice is a pile of random problems. You get reps, but you never build a mental map of the techniques. So I built AnotherCompiler, a place to learn algorithms and data structures through an ordered syllabus instead of a shuffle.</p><p>Every problem is grouped by pattern: two pointers, sliding window, prefix sums, hashing, binary search, dynamic programming, greedy, and more. Each section runs from the easy on-ramp to the harder variations, so the technique actually sticks. Difficulty climbs with it. I am still tweaking the exact order, but the recommended path is simple: solve all the easy problems, then all the medium, then all the hard.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*BSQaI9SGqS2c7PDRLnYd2g.gif" /><figcaption>AnotherCompiler</figcaption></figure><p>You write and run your solution right in the browser, and on your phone too, in Python, TypeScript, Kotlin, or Java.</p><p>It is still growing, and the ordering and problem set keep evolving.</p><p>Give it a try: <a href="https://anothercompiler.web.app/">https://anothercompiler.web.app</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=147f881eb243" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deuk/practice-algorithms-and-ds-the-structured-way-147f881eb243">Practice Algorithms and DS the Structured Way</a> was originally published in <a href="https://medium.com/deuk">AnotherAgent</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Continue Your Claude Code Sessions From Your Phone]]></title>
            <link>https://medium.com/deuk/continue-your-claude-code-sessions-from-your-phone-decefcacab1f?source=rss-ee0f8a90df77------2</link>
            <guid isPermaLink="false">https://medium.com/p/decefcacab1f</guid>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[open-source]]></category>
            <category><![CDATA[react-native]]></category>
            <category><![CDATA[claude-code]]></category>
            <dc:creator><![CDATA[Ken Ruiz Inoue]]></dc:creator>
            <pubDate>Thu, 02 Jul 2026 03:57:58 GMT</pubDate>
            <atom:updated>2026-07-02T03:57:58.049Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*eFFdwC4eMgPPXeUO8QlMpg.png" /></figure><p>I use Claude Code all day on my Mac. The problem: the moment I step away from the desk for a while, the conversation stays behind. The official Claude app was not flexible enough for this. It cannot see the sessions living in my Mac’s terminal, let alone continue them. So I decided to build my own client, one that shows every Claude Code conversation on the machine and re-syncs from the Mac’s transcript whenever the connection drops, so I never lose the thread.</p><p><a href="https://github.com/kenruizinoue/another_bridge_mobile">AnotherBridgeMobile</a> is a terminal-styled React Native client that connects to your Mac and lets you browse every Claude Code session on it, read any conversation, and keep it going. You type a message on your phone, the Mac runs claude --resume, and the reply streams back token by token. You can attach photos too, which is surprisingly useful for “here is a screenshot of the bug” moments.</p><p>Nothing runs on the phone. The app talks to <a href="https://github.com/kenruizinoue/another_bridge">AnotherBridge</a>, a self-hosted FastAPI server that exposes the local Claude Code CLI over HTTP with API-key auth. The transcript on the Mac is the single source of truth, which leads to the design decision I care most about: the app never re-sends a message. If the stream drops, the phone locks, or iOS kills the app mid-reply, it simply polls the transcript until the answer lands. Messages sent while a turn side, so the conversation keeps advancing even with the phone in your pocket.</p><p>Both repos are open source under MIT:</p><ul><li>App: <a href="https://github.com/kenruizinoue/another_bridge_mobile">github.com/kenruizinoue/another_bridge_mobile</a></li><li>Bridge: <a href="https://github.com/kenruizinoue/another_bridge">github.com/kenruizinoue/another_bridge</a></li></ul><p>Run the bridge on your Mac, point the app at it, and your Claude Code sessions are in your pocket. Read the security notes in the README before exposing anything beyond your local network.</p><p>There is more I want to add, Android support and push notifications are on the list, but the core loop already feels great: start a session at your desk, finish it from anywhere. Give it a spin, have fun, and let me know what you think!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=decefcacab1f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deuk/continue-your-claude-code-sessions-from-your-phone-decefcacab1f">Continue Your Claude Code Sessions From Your Phone</a> was originally published in <a href="https://medium.com/deuk">AnotherAgent</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Ship an AI Agent From Your Phone. No Code, No Deploy.]]></title>
            <link>https://medium.com/deuk/ship-an-ai-agent-from-your-phone-no-code-no-deploy-4db362f495da?source=rss-ee0f8a90df77------2</link>
            <guid isPermaLink="false">https://medium.com/p/4db362f495da</guid>
            <category><![CDATA[api]]></category>
            <category><![CDATA[artificial-intelligence]]></category>
            <category><![CDATA[ai-agent]]></category>
            <category><![CDATA[saas]]></category>
            <category><![CDATA[software-engineering]]></category>
            <dc:creator><![CDATA[Ken Ruiz Inoue]]></dc:creator>
            <pubDate>Wed, 15 Apr 2026 00:09:03 GMT</pubDate>
            <atom:updated>2026-04-15T00:11:33.829Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/512/1*OHnfbhFrE6ZX7-izqVLEUg.png" /><figcaption>AnotherAgent</figcaption></figure><p>I’ve built AI agents for enough projects to know the pattern: state graphs, vector embeddings, tool calling, prompt configs, streaming, conversation management. Every project, the same wiring from scratch. So I built <a href="https://anotheragent.dev/">AnotherAgent</a>, a multi-tenant API that handles all of it out of the box, plus multi-agent delegation and an MCP server. One API key, plug it into any app. It’s in public beta.</p><p><a href="https://anotheragent.dev/">AnotherAgent - Ship AI agents in hours, not weeks</a></p><p>And to prove how simple it is, I’m going to create a Shop Support Agent from my phone. This agent will know the store’s discount rules, return policy, and shipping info. Follow along. Your phone is enough.</p><p><strong>Step 1: Sign up</strong></p><p>First, sign up and grab your API key. You’ll only see it once, so copy it now (or you can rotate it if lost or leaked). New accounts start with 500 free credits.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/350/1*KGoJYZSln_pA364PrtSCAA.gif" /><figcaption>Hit Sign Up. Stay on the screen, it may take up to 30 seconds to set up your account.</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/350/1*nBR0Umhzo5ASlh8yZuqiaA.gif" /><figcaption>Copy Your API Key</figcaption></figure><p><strong>Step 2: Meet AnotherAgent</strong></p><p>You’ll land on the Agents tab. See that button at the bottom right? That’s AnotherAgent’s assistant, built on this platform to help you build agents. Tap it.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/350/1*HCqFZGO5IjxLdARkp70trg.gif" /><figcaption>Tap the AnotherAgent assistant button</figcaption></figure><p><strong>Step 3: Create the agent</strong></p><p>Tell it “I want to create an agent.” It will navigate you to the creation page.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/350/1*0UU5BDc_qwbmiWyFBeHcXA.gif" /><figcaption>The assistant navigates you to any page you need</figcaption></figure><p><strong>Step 4: Name it</strong></p><p>Name it “Shop Support Agent” and open the Prompt section. You’ll see a generic default prompt.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/350/1*hr54XLrzLcgAkQy0jmuSzA.gif" /><figcaption>The prompt preamble defines how the agent behaves</figcaption></figure><p><strong>Step 5: Tune the prompt</strong></p><p>See the <strong>ASK AGENT</strong> button inside the prompt section? Tap it and type something like<em> “Update this prompt for shop support that sells electronic goods online like phones and tablets.”</em> The agent will rewrite the prompt for you.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/348/1*NZdhwBYcJaq-k4bQGCzveA.gif" /><figcaption>Use ASK AGENT to let the assistant edit fields for you</figcaption></figure><p><strong>Step 6: Create</strong></p><p>Happy with the prompt? Hit Create AI Agent.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/350/1*ou6ITQe-av6O60EjeWJuZQ.gif" /><figcaption>Only the name field is mandatory to create an agent</figcaption></figure><p><strong>Step 7: Add knowledge</strong></p><p>Now give your agent some knowledge. Scroll to the memory section and tap Create. Paste your first rule:</p><p><em>“First-time customers get 15% off with code WELCOME15. Cannot be combined with other discounts.”</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/350/1*c6gdPQl0-t1X1tAF07qXWQ.gif" /><figcaption>You can add as many memories as you need</figcaption></figure><p><strong>Step 8: Add more knowledge</strong></p><p>Repeat with a couple more rules (feel free to add your rules):</p><p><em>“Returns accepted within 30 days with original tags. Sale items are final sale.”</em></p><p><em>“Free shipping on orders over $75. Under $75 pays a flat $5.99 shipping fee.”</em></p><p><strong>Step 9: Test it</strong></p><p>Time to test. Tap the AnotherAgent button, switch to Debug mode, select your Shop Support Agent from the dropdown, and ask it something from the knowledge you added.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/350/1*zmeYds5QO56-WzK9JaJG9A.gif" /><figcaption>Debug mode lets you test any agent and inspect trace events</figcaption></figure><p><strong>Step 10: Plug it into your app</strong></p><p>Now that your agent works, let’s plug it into your app. AnotherAgent is API-based, so it works with any platform: mobile, web, backend, a toaster, whatever speaks HTTP.</p><p>For a quick one-shot question, use /ask.</p><pre>curl -X POST https://api.anotheragent.dev/ask \<br>  -H &quot;Content-Type: application/json&quot; \<br>  -H &quot;X-API-Key: YOUR_API_KEY&quot; \<br>  -d &#39;{<br>    &quot;agentId&quot;: &quot;YOUR_AGENT_ID&quot;,<br>    &quot;message&quot;: &quot;Do you have any promo codes?&quot;<br>  }&#39;</pre><p>For a stateful conversation, first create one between a user and an agent using /conversations/start to get the conversationId. When you sign up, a non-deletable admin user is created for you. You can use it for testing.</p><pre>curl -X POST https://api.anotheragent.dev/conversations/start \<br>  -H &quot;Content-Type: application/json&quot; \<br>  -H &quot;X-API-Key: YOUR_API_KEY&quot; \<br>  -d &#39;{<br>    &quot;userId&quot;: &quot;YOUR_USER_ID&quot;,<br>    &quot;agentId&quot;: &quot;YOUR_AGENT_ID&quot;<br>  }&#39;</pre><p>Then send messages to /chat using the conversationId from the response. The AnotherAgent assistant and Debug mode both use this endpoint.</p><pre>curl -X POST https://api.anotheragent.dev/chat \<br>  -H &quot;Content-Type: application/json&quot; \<br>  -H &quot;X-API-Key: YOUR_API_KEY&quot; \<br>  -d &#39;{<br>    &quot;conversationId&quot;: &quot;YOUR_CONVERSATION_ID&quot;,<br>    &quot;message&quot;: &quot;What is your return policy?&quot;<br>  }&#39;</pre><p>For the full API reference, check out the <a href="https://api.anotheragent.dev/docs">API docs</a>.</p><p>That’s it. You just created, configured, and tested an AI agent from your phone. No repo, no code, no deploy pipeline.</p><p>This is just the surface. AnotherAgent supports tool calling with approval gates, multi-agent delegation, streaming, debug tracing, templates, and a full MCP server. I’ll cover these in future tutorials.</p><p>The platform is in public beta and I want your help to make it better. If you find a bug or want to request a feature, tell AnotherAgent directly and it will submit a ticket for me.</p><p>Try it out. Drop a comment with what you’re building!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4db362f495da" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deuk/ship-an-ai-agent-from-your-phone-no-code-no-deploy-4db362f495da">Ship an AI Agent From Your Phone. No Code, No Deploy.</a> was originally published in <a href="https://medium.com/deuk">AnotherAgent</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android Compose Task App Implementation Series: Customizable Text Field]]></title>
            <link>https://medium.com/deuk/android-compose-task-app-implementation-series-customizable-text-field-8f263facfe3c?source=rss-ee0f8a90df77------2</link>
            <guid isPermaLink="false">https://medium.com/p/8f263facfe3c</guid>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[android-compose]]></category>
            <dc:creator><![CDATA[Ken Ruiz Inoue]]></dc:creator>
            <pubDate>Wed, 20 Mar 2024 04:24:18 GMT</pubDate>
            <atom:updated>2025-11-03T20:38:04.884Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*2SarLJhZcrGmblUSgIQ5qQ.png" /></figure><h3>Production App</h3><p>Anothertask Version 1.0.0 is available <a href="https://play.google.com/store/apps/details?id=com.deuk.anothertask">here</a> if you are interested 😉</p><h3>Objective</h3><p>Recreate the Customizable Text Field from the <a href="https://themeforest.net/item/taskose-task-management-app-figma-template/40364051">Taskose</a> UI design kit I acquired, utilizing Android Jetpack Compose.</p><h3>Environment</h3><ul><li>Android Studio Iguana | 2023.2.1</li><li>Compose version: composeBom = &quot;2024.02.02&quot;</li><li>Pixel 6 API 30 Emulator</li><li>Project Minimum SDK: 27</li></ul><h3>Implementation</h3><p>CustomTextField.kt</p><pre>// Your package ...<br><br>import androidx.compose.foundation.layout.fillMaxWidth<br>import androidx.compose.foundation.shape.RoundedCornerShape<br>import androidx.compose.material3.OutlinedTextField<br>import androidx.compose.material3.OutlinedTextFieldDefaults<br>import androidx.compose.material3.Text<br>import androidx.compose.runtime.Composable<br>import androidx.compose.ui.Modifier<br>import androidx.compose.ui.graphics.Color<br>import androidx.compose.ui.platform.testTag<br>import androidx.compose.ui.text.TextStyle<br>import androidx.compose.ui.text.font.FontWeight<br>import androidx.compose.ui.unit.Dp<br>import androidx.compose.ui.unit.dp<br>import androidx.compose.ui.unit.sp<br><br>@Composable<br>fun CustomTextField(<br>    value: String = &quot;&quot;,<br>    placeholderText: String = &quot;&quot;,<br>    placeholderTextStyle: TextStyle = TextStyle(<br>        fontWeight = FontWeight.Normal,<br>        fontSize = 14.sp,<br>        color = Color(0xFF31394F).copy(alpha = 0.5f)<br>    ),<br>    focusedBorderColor : Color = Color(0xFF7980FF),<br>    unfocusedBorderColor : Color = Color(0xFF31394F).copy(alpha = 0.5f),<br>    roundedCornerRadius: Dp = 12.dp,<br>    testTag: String = &quot;&quot;,<br>    onValueChange: (String) -&gt; Unit,<br>) {<br>    OutlinedTextField(<br>        modifier = Modifier<br>            .fillMaxWidth()<br>            .testTag(testTag),<br>        value = value,<br>        onValueChange = {<br>            onValueChange(it)<br>        },<br>        singleLine = true,<br>        placeholder = { Text(text = placeholderText, style = placeholderTextStyle) },<br>        colors = OutlinedTextFieldDefaults.colors(<br>            focusedBorderColor = focusedBorderColor,<br>            unfocusedBorderColor = unfocusedBorderColor<br>        ),<br>        shape = RoundedCornerShape(roundedCornerRadius)<br>    )<br>}</pre><p>CustomTextFieldPreview.kt</p><pre>// Your package ...<br><br>import androidx.compose.foundation.layout.Box<br>import androidx.compose.foundation.layout.fillMaxSize<br>import androidx.compose.foundation.layout.padding<br>import androidx.compose.runtime.Composable<br>import androidx.compose.runtime.mutableStateOf<br>import androidx.compose.runtime.remember<br>import androidx.compose.ui.Alignment<br>import androidx.compose.ui.Modifier<br>import androidx.compose.ui.tooling.preview.Preview<br>import androidx.compose.ui.unit.dp<br><br>@Preview<br>@Composable<br>fun CustomTextFieldPreview() {<br>    val inputValue = remember { mutableStateOf(&quot;&quot;) }<br>    Box(<br>        modifier = Modifier.fillMaxSize().padding(24.dp),<br>        contentAlignment = Alignment.Center<br>    ) {<br>        CustomTextField(<br>            value = inputValue.value,<br>            placeholderText = &quot;Placeholder Text&quot;,<br>            onValueChange = {<br>                inputValue.value = it<br>            }<br>        )<br>    }<br>}</pre><h3>Preview Result</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*AwZbfonjvDuDs4J_wyYVFQ.gif" /></figure><h3>Explore More</h3><p>For developers eager to deepen their understanding or explore more about modern Android development, a wealth of resources and guides await.</p><ul><li><a href="https://medium.com/deuk/android-compose-tutorials-library-54111197c22d">Android Compose Tutorials Library</a></li><li><a href="https://medium.com/deuk/the-ultimate-android-development-career-guide-906ea32d5c8a">The Ultimate Android Development Career Guide</a></li></ul><p>If you’ve found this resource helpful, consider sharing your support through claps or a follow, and stay tuned for more insights into the evolving landscape of Android development. See you the next time!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8f263facfe3c" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deuk/android-compose-task-app-implementation-series-customizable-text-field-8f263facfe3c">Android Compose Task App Implementation Series: Customizable Text Field</a> was originally published in <a href="https://medium.com/deuk">AnotherAgent</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android Compose Task App Implementation Series: Customizable Button]]></title>
            <link>https://medium.com/deuk/android-compose-task-app-implementation-series-customizable-button-e5ab5ee3e2d6?source=rss-ee0f8a90df77------2</link>
            <guid isPermaLink="false">https://medium.com/p/e5ab5ee3e2d6</guid>
            <category><![CDATA[android-compose]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[android-app-development]]></category>
            <dc:creator><![CDATA[Ken Ruiz Inoue]]></dc:creator>
            <pubDate>Tue, 19 Mar 2024 00:15:18 GMT</pubDate>
            <atom:updated>2025-11-03T23:10:51.658Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*RC6m9HsJJldBXXJO87zh0Q.png" /></figure><h3>Production App</h3><p>Anothertask Version 1.0.0 is available <a href="https://play.google.com/store/apps/details?id=com.deuk.anothertask">here</a> if you are interested 😉</p><h3>Objective</h3><p>Recreate the Customizable Button from the <a href="https://themeforest.net/item/taskose-task-management-app-figma-template/40364051">Taskose</a> UI design kit I acquired, utilizing Android Jetpack Compose.</p><h3>Environment</h3><ul><li>Android Studio Iguana | 2023.2.1</li><li>Compose version: composeBom = &quot;2024.02.02&quot;</li><li>Pixel 6 API 30 Emulator</li><li>Project Minimum SDK: 27</li></ul><h3>Implementation</h3><h4>Step 1</h4><p>Download the needed icons <a href="https://drive.google.com/drive/folders/13lfXW_BpxJpi4PDgdtxJRGgSLSZZz-9W?usp=sharing">here</a>.</p><h4>Step 2</h4><p>Create the CustomButton.kt file with the following content.</p><pre>// Your package ...<br><br>import androidx.compose.foundation.layout.Box<br>import androidx.compose.foundation.layout.PaddingValues<br>import androidx.compose.foundation.layout.fillMaxSize<br>import androidx.compose.foundation.layout.fillMaxWidth<br>import androidx.compose.foundation.layout.height<br>import androidx.compose.foundation.shape.RoundedCornerShape<br>import androidx.compose.material3.Button<br>import androidx.compose.material3.ButtonDefaults<br>import androidx.compose.material3.Icon<br>import androidx.compose.material3.Text<br>import androidx.compose.runtime.Composable<br>import androidx.compose.ui.Alignment<br>import androidx.compose.ui.Modifier<br>import androidx.compose.ui.graphics.Color<br>import androidx.compose.ui.graphics.vector.ImageVector<br>import androidx.compose.ui.platform.testTag<br>import androidx.compose.ui.res.vectorResource<br>import androidx.compose.ui.text.TextStyle<br>import androidx.compose.ui.text.font.FontWeight<br>import androidx.compose.ui.unit.Dp<br>import androidx.compose.ui.unit.dp<br>import androidx.compose.ui.unit.sp<br><br>data class CustomButtonStyle(<br>    val buttonTextStyle: TextStyle = TextStyle(<br>        fontWeight = FontWeight.Bold,<br>        fontSize = 14.sp<br>    ),<br>    val buttonContainerColor: Color = Color(0xFF7980FF),<br>    val buttonContentColor: Color = Color.White,<br>    val buttonPaddingValues: PaddingValues = PaddingValues(16.dp),<br>    val roundedCornerRadius: Dp = 12.dp,<br>    val height: Dp = 48.dp,<br>    val iconDrawableId: Int? = null,<br>)<br><br>@Composable<br>fun CustomButton(<br>    modifier: Modifier = Modifier,<br>    enabled: Boolean = true,<br>    buttonText: String = &quot;Some Action&quot;,<br>    style: CustomButtonStyle = CustomButtonStyle(),<br>    testTag: String = &quot;&quot;,<br>    onClick: () -&gt; Unit = {}<br>) {<br>    Button(<br>        enabled = enabled,<br>        onClick = {<br>            onClick()<br>        },<br>        shape = RoundedCornerShape(style.roundedCornerRadius),<br>        modifier = modifier<br>            .height(style.height)<br>            .fillMaxWidth()<br>            .testTag(testTag),<br>        colors = ButtonDefaults.buttonColors(<br>            style.buttonContainerColor,<br>            style.buttonContentColor<br>        ),<br>        contentPadding = style.buttonPaddingValues<br>    ) {<br>        Box(<br>            modifier = Modifier.fillMaxSize()<br>        ) {<br>            if (style.iconDrawableId != null) {<br>                Icon(<br>                    modifier = Modifier.align(Alignment.CenterEnd),<br>                    imageVector = ImageVector.vectorResource(id = style.iconDrawableId),<br>                    tint = style.buttonContentColor,<br>                    contentDescription = null<br>                )<br>            }<br>            Text(<br>                modifier = Modifier.align(Alignment.Center),<br>                text = buttonText,<br>                style = style.buttonTextStyle<br>            )<br>        }<br>    }<br>}</pre><h4>Step 3</h4><p>Create the CustomButtonPreview.kt file with the following content.</p><pre>// Your package ...<br><br>import android.widget.Toast<br>import androidx.compose.foundation.layout.Arrangement<br>import androidx.compose.foundation.layout.Box<br>import androidx.compose.foundation.layout.Column<br>import androidx.compose.foundation.layout.fillMaxSize<br>import androidx.compose.foundation.layout.padding<br>import androidx.compose.runtime.Composable<br>import androidx.compose.runtime.mutableStateOf<br>import androidx.compose.runtime.remember<br>import androidx.compose.ui.Alignment<br>import androidx.compose.ui.Modifier<br>import androidx.compose.ui.graphics.Color<br>import androidx.compose.ui.platform.LocalContext<br>import androidx.compose.ui.tooling.preview.Preview<br>import androidx.compose.ui.unit.dp<br><br>@Preview<br>@Composable<br>fun CustomButtonPreview() {<br>    val context = LocalContext.current<br>    Box(<br>        modifier = Modifier<br>            .fillMaxSize()<br>            .padding(24.dp),<br>        contentAlignment = Alignment.Center<br>    ) {<br>        Column(<br>            verticalArrangement = Arrangement.spacedBy(16.dp)<br>        ) {<br>            CustomButton {<br>                Toast.makeText(context, &quot;Some Action&quot;, Toast.LENGTH_SHORT).show()<br>            }<br>            CustomButton(<br>                buttonText = &quot;Delete Task&quot;,<br>                style = CustomButtonStyle(<br>                    buttonContainerColor = Color(0xFFF05A5A),<br>                    iconDrawableId = R.drawable.ic_delete<br>                )<br>            ) {<br>                Toast.makeText(context, &quot;Delete Task&quot;, Toast.LENGTH_SHORT).show()<br>            }<br>            val isTracking = remember {<br>                mutableStateOf(false)<br>            }<br>            CustomButton(<br>                buttonText = if (isTracking.value) &quot;Stop Tracking&quot; else &quot;Start Tracking&quot;,<br>                style = CustomButtonStyle(<br>                    iconDrawableId = if (isTracking.value) R.drawable.ic_stop else R.drawable.ic_play<br>                )<br>            ) {<br>                isTracking.value = !isTracking.value<br>            }<br>        }<br>    }<br>}</pre><h3>Preview Result</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*VyidzlqHaBtXZ7cs9mmLKg.gif" /></figure><h3>Explore More</h3><p>For developers eager to deepen their understanding or explore more about modern Android development, a wealth of resources and guides await.</p><ul><li><a href="https://medium.com/deuk/android-compose-tutorials-library-54111197c22d">Android Compose Tutorials Library</a></li><li><a href="https://medium.com/deuk/the-ultimate-android-development-career-guide-906ea32d5c8a">The Ultimate Android Development Career Guide</a></li></ul><p>If you’ve found this resource helpful, consider sharing your support through claps or a follow, and stay tuned for more insights into the evolving landscape of Android development. See you the next time!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e5ab5ee3e2d6" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deuk/android-compose-task-app-implementation-series-customizable-button-e5ab5ee3e2d6">Android Compose Task App Implementation Series: Customizable Button</a> was originally published in <a href="https://medium.com/deuk">AnotherAgent</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android Compose Task App Implementation Series: Customizable Top Bar]]></title>
            <link>https://medium.com/deuk/android-compose-task-app-implementation-series-customizable-top-bar-e9c09a528c15?source=rss-ee0f8a90df77------2</link>
            <guid isPermaLink="false">https://medium.com/p/e9c09a528c15</guid>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[android-compose]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <dc:creator><![CDATA[Ken Ruiz Inoue]]></dc:creator>
            <pubDate>Mon, 18 Mar 2024 02:16:19 GMT</pubDate>
            <atom:updated>2025-11-03T23:12:24.419Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*0mWGo9xdmDR9FAgI_Dfa9Q.png" /></figure><h3>Production App</h3><p>Anothertask Version 1.0.0 is available <a href="https://play.google.com/store/apps/details?id=com.deuk.anothertask">here</a> if you are interested 😉</p><h3>Objective</h3><p>Recreate the Customizable Top Bar from the <a href="https://themeforest.net/item/taskose-task-management-app-figma-template/40364051">Taskose</a> UI design kit I acquired, utilizing Android Jetpack Compose.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/788/1*OKHSEEw0M-dNIp24cRPEDg.png" /></figure><h3>Related Content</h3><p><a href="https://medium.com/deuk/android-task-app-implementation-series-icon-button-with-customizable-menu-132be7e64dfc">Android Compose Task App Implementation Series: Icon Button with Customizable Menu</a></p><h3>Environment</h3><ul><li>Android Studio Iguana | 2023.2.1</li><li>Compose version: composeBom = &quot;2024.02.02&quot;</li><li>Pixel 6 API 30 Emulator</li><li>Project Minimum SDK: 27</li></ul><h3>Implementation</h3><h4>Step 1</h4><p>Download the needed resources <a href="https://drive.google.com/drive/folders/1DmehsNqleF9tkX3lORmn_ofcNndYjvZk?usp=share_link">here</a> (icons and IconButtonDropdownMenu.kt).</p><h4>Step 2</h4><p>Create the TopBar.kt file with the following content.</p><pre>// Your package ...<br><br>import androidx.compose.foundation.layout.Box<br>import androidx.compose.foundation.layout.Column<br>import androidx.compose.foundation.layout.fillMaxWidth<br>import androidx.compose.foundation.layout.height<br>import androidx.compose.foundation.layout.padding<br>import androidx.compose.material3.HorizontalDivider<br>import androidx.compose.material3.Text<br>import androidx.compose.runtime.Composable<br>import androidx.compose.ui.Alignment<br>import androidx.compose.ui.Modifier<br>import androidx.compose.ui.graphics.Color<br>import androidx.compose.ui.text.TextStyle<br>import androidx.compose.ui.text.font.FontWeight<br>import androidx.compose.ui.unit.Dp<br>import androidx.compose.ui.unit.dp<br>import androidx.compose.ui.unit.sp<br><br>@Composable<br>fun TopBar(<br>    title: String,<br>    height: Dp = 64.dp,<br>    topBarDividerColor: Color = Color(0xFF31394F).copy(alpha = 0.50f),<br>    titleTextStyle: TextStyle = TextStyle(<br>        fontWeight = FontWeight.Bold,<br>        fontSize = 18.sp,<br>        color = Color(0xFF31394F)<br>    ),<br>    leftIconComposable: @Composable (() -&gt; Unit)? = null,<br>    rightIconComposable: @Composable (() -&gt; Unit)? = null,<br>    topBarIconPadding: Dp = 12.dp<br>) {<br>    Column {<br>        Box(<br>            modifier = Modifier<br>                .fillMaxWidth()<br>                .height(height)<br>                .padding(horizontal = topBarIconPadding)<br>        ) {<br>            leftIconComposable?.let {<br>                Box(modifier = Modifier.align(Alignment.CenterStart)) {<br>                    it()<br>                }<br>            }<br>            Text(<br>                text = title,<br>                style = titleTextStyle,<br>                modifier = Modifier.align(Alignment.Center)<br>            )<br>            rightIconComposable?.let {<br>                Box(modifier = Modifier.align(Alignment.CenterEnd)) {<br>                    it()<br>                }<br>            }<br>        }<br>        HorizontalDivider(color = topBarDividerColor)<br>    }<br>}</pre><h4>Step 3</h4><p>Create the TopBarPreview.kt file with the following content.</p><pre>// Your package ...<br><br>import android.widget.Toast<br>import androidx.compose.material3.Icon<br>import androidx.compose.material3.IconButton<br>import androidx.compose.runtime.Composable<br>import androidx.compose.ui.Modifier<br>import androidx.compose.ui.graphics.vector.ImageVector<br>import androidx.compose.ui.platform.LocalContext<br>import androidx.compose.ui.platform.testTag<br>import androidx.compose.ui.res.vectorResource<br>import androidx.compose.ui.text.TextStyle<br>import androidx.compose.ui.tooling.preview.Preview<br>// import YOUR_PACKAGE_PATH.IconButtonDropdownMenu<br>// import YOUR_PACKAGE_PATH.MenuItem<br><br>@Composable<br>@Preview<br>fun ImprovedTopBarPreview() {<br>    val context = LocalContext.current<br>    TopBar(<br>        title = &quot;Title&quot;,<br>        titleTextStyle = TextStyle.Default,<br>        leftIconComposable = {<br>            IconButton(<br>                onClick = {<br>                    Toast.makeText(context, &quot;Back button clicked&quot;, Toast.LENGTH_SHORT).show()<br>                }<br>            ) {<br>                Icon(<br>                    imageVector = ImageVector.vectorResource(id = R.drawable.ic_back),<br>                    contentDescription = null<br>                )<br>            }<br>        },<br>        rightIconComposable = {<br>            IconButtonDropdownMenu(<br>                iconDrawableId = R.drawable.ic_more,<br>                menuItems = listOf(<br>                    MenuItem(R.drawable.ic_calendar, label = &quot;Calendar&quot;) {<br>                        Toast.makeText(context, &quot;Calendar clicked&quot;, Toast.LENGTH_SHORT).show()<br>                    },<br>                    MenuItem(R.drawable.ic_done, label = &quot;Check&quot;) {<br>                        Toast.makeText(context, &quot;Check clicked&quot;, Toast.LENGTH_SHORT).show()<br>                    },<br>                    MenuItem(label = &quot;Add Project&quot;) {<br>                        Toast.makeText(context, &quot;Add Project clicked&quot;, Toast.LENGTH_SHORT).show()<br>                    },<br>                )<br>            )<br>        }<br>    )<br>}</pre><h3>Preview Result</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1AoHVyc0vQHKDRy6oqR3cQ.gif" /></figure><h3>Explore More</h3><p>For developers eager to deepen their understanding or explore more about modern Android development, a wealth of resources and guides await.</p><ul><li><a href="https://medium.com/deuk/android-compose-tutorials-library-54111197c22d">Android Compose Tutorials Library</a></li><li><a href="https://medium.com/deuk/the-ultimate-android-development-career-guide-906ea32d5c8a">The Ultimate Android Development Career Guide</a></li></ul><p>If you’ve found this resource helpful, consider sharing your support through claps or a follow, and stay tuned for more insights into the evolving landscape of Android development. See you the next time!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e9c09a528c15" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deuk/android-compose-task-app-implementation-series-customizable-top-bar-e9c09a528c15">Android Compose Task App Implementation Series: Customizable Top Bar</a> was originally published in <a href="https://medium.com/deuk">AnotherAgent</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android Compose Task App Implementation Series: Bottom Bar with a FAB]]></title>
            <link>https://medium.com/deuk/android-task-app-implementation-series-bottom-bar-with-a-fab-8008962ee744?source=rss-ee0f8a90df77------2</link>
            <guid isPermaLink="false">https://medium.com/p/8008962ee744</guid>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[android-compose]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[android-app-development]]></category>
            <dc:creator><![CDATA[Ken Ruiz Inoue]]></dc:creator>
            <pubDate>Sat, 16 Mar 2024 18:46:52 GMT</pubDate>
            <atom:updated>2025-11-03T23:12:46.951Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Szfm3IqSekCz3dXuBqIx1w.png" /></figure><h3>Production App</h3><p>Anothertask Version 1.0.0 is available <a href="https://play.google.com/store/apps/details?id=com.deuk.anothertask">here</a> if you are interested 😉</p><h3>Objective</h3><p>Recreate the Bottom Bar with a Floating Action Button (FAB) from the <a href="https://themeforest.net/item/taskose-task-management-app-figma-template/40364051">Taskose</a> UI design kit I acquired, utilizing Android Jetpack Compose.</p><h3>Environment</h3><ul><li>Android Studio Iguana | 2023.2.1</li><li>Compose version: composeBom = &quot;2023.08.00&quot;</li><li>Pixel 6 API 30 Emulator</li><li>Project Minimum SDK: 27</li></ul><h3>Implementation</h3><h4>Step 1</h4><p>Download the needed icons <a href="https://drive.google.com/drive/folders/1rbITJfhtnOSkXc1Pfxy4cPprtQnKuB_C?usp=share_link">here</a>, and add them to res/drawable directory.</p><h4>Step 2</h4><p>Create BottomBarItem.kt file with the following content.</p><pre>// Your package ...<br><br>import androidx.compose.foundation.background<br>import androidx.compose.foundation.layout.Arrangement<br>import androidx.compose.foundation.layout.Column<br>import androidx.compose.foundation.layout.Spacer<br>import androidx.compose.foundation.layout.fillMaxSize<br>import androidx.compose.foundation.layout.height<br>import androidx.compose.foundation.layout.size<br>import androidx.compose.material3.Icon<br>import androidx.compose.material3.IconButton<br>import androidx.compose.material3.Text<br>import androidx.compose.runtime.Composable<br>import androidx.compose.ui.Alignment<br>import androidx.compose.ui.Modifier<br>import androidx.compose.ui.graphics.Color<br>import androidx.compose.ui.graphics.vector.ImageVector<br>import androidx.compose.ui.res.vectorResource<br>import androidx.compose.ui.tooling.preview.Preview<br>import androidx.compose.ui.unit.Dp<br>import androidx.compose.ui.unit.dp<br>// import YOUR_PACKAGE_PATH.R<br><br>data class BottomBarItemData(<br>    val drawableId: Int = 0,<br>    val selected: Boolean = false,<br>    val onClick: () -&gt; Unit = {}<br>)<br><br>@Composable<br>fun BottomBarItem(<br>    itemData: BottomBarItemData,<br>    selectedColor: Color = Color(0xFF7980FF),<br>    nonSelectedColor: Color = Color(0xFF464D61).copy(alpha = 0.7f),<br>    iconSize: Dp = 24.dp<br>) {<br>    IconButton(onClick = { itemData.onClick() }) {<br>        Icon(<br>            imageVector = ImageVector.vectorResource(id = itemData.drawableId),<br>            contentDescription = null,<br>            tint = if (itemData.selected) selectedColor else nonSelectedColor,<br>            modifier = Modifier.size(iconSize)<br>        )<br>    }<br>}<br><br>@Preview<br>@Composable<br>fun BottomBarItemPreview() {<br>    val mockOnClick = {}<br>    Column(<br>        verticalArrangement = Arrangement.Center,<br>        horizontalAlignment = Alignment.CenterHorizontally,<br>        modifier = Modifier<br>            .fillMaxSize()<br>            .background(Color(0xFF000000))<br>    ) {<br>        Text(&quot;Selected&quot;, color = Color.White)<br>        Spacer(modifier = Modifier.height(8.dp))<br>        BottomBarItem(<br>            itemData = BottomBarItemData(<br>                onClick = mockOnClick,<br>                drawableId = R.drawable.ic_dashboard,<br>                selected = true<br>            ),<br>        )<br>        Spacer(modifier = Modifier.height(16.dp))<br>        Text(&quot;Not Selected&quot;, color = Color.White)<br>        Spacer(modifier = Modifier.height(8.dp))<br>        BottomBarItem(<br>            itemData = BottomBarItemData(<br>                onClick = mockOnClick,<br>                drawableId = R.drawable.ic_profile,<br>                selected = false<br>            ),<br>        )<br>    }<br>}</pre><h4>Step 3</h4><p>Create BottomBar.kt file with the following content.</p><pre>// Your package ...<br><br>import android.widget.Toast<br>import androidx.compose.foundation.layout.Arrangement<br>import androidx.compose.foundation.layout.Box<br>import androidx.compose.foundation.layout.Row<br>import androidx.compose.foundation.layout.Spacer<br>import androidx.compose.foundation.layout.fillMaxSize<br>import androidx.compose.foundation.layout.fillMaxWidth<br>import androidx.compose.foundation.layout.height<br>import androidx.compose.foundation.layout.size<br>import androidx.compose.foundation.shape.CircleShape<br>import androidx.compose.foundation.shape.RoundedCornerShape<br>import androidx.compose.material3.Card<br>import androidx.compose.material3.CardDefaults<br>import androidx.compose.material3.FloatingActionButtonDefaults<br>import androidx.compose.material3.Icon<br>import androidx.compose.material3.LargeFloatingActionButton<br>import androidx.compose.runtime.Composable<br>import androidx.compose.runtime.mutableIntStateOf<br>import androidx.compose.runtime.remember<br>import androidx.compose.ui.Alignment<br>import androidx.compose.ui.Modifier<br>import androidx.compose.ui.graphics.Color<br>import androidx.compose.ui.graphics.vector.ImageVector<br>import androidx.compose.ui.platform.LocalContext<br>import androidx.compose.ui.res.vectorResource<br>import androidx.compose.ui.tooling.preview.Preview<br>import androidx.compose.ui.unit.Dp<br>import androidx.compose.ui.unit.dp<br>// import YOUR_PACKAGE_PATH.R<br>// import YOUR_PACKAGE_PATH.BottomBarItem<br>// import YOUR_PACKAGE_PATH.BottomBarItemData<br><br>@Composable<br>fun BottomBar(<br>    barHeight: Dp = 60.dp,<br>    fabColor: Color = Color(0xFF7980FF),<br>    fabSize: Dp = 64.dp,<br>    fabIconSize: Dp = 32.dp,<br>    cardTopCornerSize: Dp = 24.dp,<br>    cardElevation: Dp = 8.dp,<br>    fabDrawableId: Int = R.drawable.ic_add,<br>    buttons: List&lt;BottomBarItemData&gt; = listOf(<br>        BottomBarItemData(drawableId = R.drawable.ic_dashboard),<br>        BottomBarItemData(drawableId = R.drawable.ic_tasks),<br>        BottomBarItemData(drawableId = R.drawable.ic_chat),<br>        BottomBarItemData(drawableId = R.drawable.ic_profile)<br>    ),<br>    fabOnClick: () -&gt; Unit = {}<br>) {<br>    require(buttons.size == 4) { &quot;BottomBar must have exactly 4 buttons&quot; }<br>    Box(<br>        modifier = Modifier<br>            .fillMaxWidth()<br>            .height(barHeight + fabSize / 2)<br>    ) {<br>        Card(<br>            modifier = Modifier<br>                .fillMaxWidth()<br>                .height(barHeight)<br>                .align(Alignment.BottomCenter),<br>            colors = CardDefaults.cardColors(containerColor = Color.White),<br>            elevation = CardDefaults.cardElevation(defaultElevation = cardElevation),<br>            shape = RoundedCornerShape(<br>                topStart = cardTopCornerSize,<br>                topEnd = cardTopCornerSize,<br>                bottomEnd = 0.dp,<br>                bottomStart = 0.dp<br>            )<br>        ) {<br>            Row(<br>                modifier = Modifier.fillMaxSize(),<br>                verticalAlignment = Alignment.CenterVertically,<br>                horizontalArrangement = Arrangement.SpaceAround<br>            )<br>            {<br>                BottomBarItem(buttons[0])<br>                BottomBarItem(buttons[1])<br>                Spacer(modifier = Modifier.size(fabSize))<br>                BottomBarItem(buttons[2])<br>                BottomBarItem(buttons[3])<br>            }<br>        }<br>        LargeFloatingActionButton(<br>            modifier = Modifier<br>                .size(fabSize)<br>                .align(Alignment.TopCenter),<br>            onClick = { fabOnClick() },<br>            shape = CircleShape,<br>            containerColor = fabColor,<br>            elevation = FloatingActionButtonDefaults.bottomAppBarFabElevation(defaultElevation = 0.dp)<br>        ) {<br>            Icon(<br>                imageVector = ImageVector.vectorResource(id = fabDrawableId),<br>                contentDescription = null,<br>                tint = Color.White,<br>                modifier = Modifier.size(fabIconSize)<br>            )<br>        }<br>    }<br>}<br><br>@Preview<br>@Composable<br>fun BottomBarPreview() {<br>    val context = LocalContext.current<br>    val items = listOf(<br>        R.drawable.ic_dashboard,<br>        R.drawable.ic_tasks,<br>        R.drawable.ic_chat,<br>        R.drawable.ic_profile<br>    )<br><br>    // Use a single state to track the currently selected item&#39;s index<br>    val selectedIndex = remember { mutableIntStateOf(0) }<br><br>    // Dynamically generate the buttons based on the items list<br>    val buttons = items.mapIndexed { index, drawableId -&gt;<br>        BottomBarItemData(<br>            drawableId = drawableId,<br>            selected = index == selectedIndex.intValue,<br>            onClick = { selectedIndex.intValue = index } // Update the selected index on click<br>        )<br>    }<br><br>    Box(<br>        modifier = Modifier.fillMaxSize(),<br>        contentAlignment = Alignment.BottomCenter<br>    ) {<br>        BottomBar(buttons = buttons, fabOnClick = {<br>            Toast.makeText(context, &quot;FAB clicked&quot;, Toast.LENGTH_SHORT).show()<br>        })<br>    }<br>}</pre><h3>Preview Result</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5EGTSvKHWNEJIWdLST-zzQ.gif" /></figure><h3>Explore More</h3><p>For developers eager to deepen their understanding or explore more about modern Android development, a wealth of resources and guides await.</p><ul><li><a href="https://medium.com/deuk/android-compose-tutorials-library-54111197c22d">Android Compose Tutorials Library</a></li><li><a href="https://medium.com/deuk/the-ultimate-android-development-career-guide-906ea32d5c8a">The Ultimate Android Development Career Guide</a></li></ul><p>If you’ve found this resource helpful, consider sharing your support through claps or a follow, and stay tuned for more insights into the evolving landscape of Android development. See you the next time!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8008962ee744" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deuk/android-task-app-implementation-series-bottom-bar-with-a-fab-8008962ee744">Android Compose Task App Implementation Series: Bottom Bar with a FAB</a> was originally published in <a href="https://medium.com/deuk">AnotherAgent</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android Compose Task App Implementation Series: Icon Button with Customizable Menu]]></title>
            <link>https://medium.com/deuk/android-task-app-implementation-series-icon-button-with-customizable-menu-132be7e64dfc?source=rss-ee0f8a90df77------2</link>
            <guid isPermaLink="false">https://medium.com/p/132be7e64dfc</guid>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[android-compose]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[Ken Ruiz Inoue]]></dc:creator>
            <pubDate>Fri, 15 Mar 2024 23:52:19 GMT</pubDate>
            <atom:updated>2025-11-03T23:13:25.764Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*yAt1RoVu_RRmV-VRnV1Tbg.png" /></figure><h3>Production App</h3><p>Anothertask Version 1.0.0 is available <a href="https://play.google.com/store/apps/details?id=com.deuk.anothertask">here</a> if you are interested 😉</p><h3>Objective</h3><p>Recreate a similar Icon Button with a Menu from the <a href="https://themeforest.net/item/taskose-task-management-app-figma-template/40364051">Taskose</a> UI design kit I acquired, utilizing Android Jetpack Compose.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/628/1*ykZ8nDZ-JgxC-v6JazrWOw.png" /></figure><h3>Environment</h3><ul><li>Android Studio Iguana | 2023.2.1</li><li>Compose version: composeBom = &quot;2023.08.00&quot;</li><li>Pixel 6 API 30 Emulator</li><li>Project Minimum SDK: 27</li></ul><h3>Implementation</h3><p>Download the needed icons <a href="https://drive.google.com/drive/folders/1S0gQaXsEJJ4WNB17mEiSsDpJt5sKVym4?usp=share_link">here</a>, and add them to res/drawable directory.</p><pre>// Your package...<br><br>import android.widget.Toast<br>import androidx.compose.foundation.layout.Box<br>import androidx.compose.foundation.layout.Column<br>import androidx.compose.foundation.layout.PaddingValues<br>import androidx.compose.foundation.layout.fillMaxSize<br>import androidx.compose.foundation.layout.fillMaxWidth<br>import androidx.compose.foundation.layout.height<br>import androidx.compose.material3.DropdownMenu<br>import androidx.compose.material3.DropdownMenuItem<br>import androidx.compose.material3.Icon<br>import androidx.compose.material3.IconButton<br>import androidx.compose.material3.Text<br>import androidx.compose.runtime.Composable<br>import androidx.compose.runtime.mutableStateOf<br>import androidx.compose.runtime.remember<br>import androidx.compose.ui.Alignment<br>import androidx.compose.ui.Modifier<br>import androidx.compose.ui.graphics.Color<br>import androidx.compose.ui.graphics.vector.ImageVector<br>import androidx.compose.ui.platform.LocalContext<br>import androidx.compose.ui.platform.testTag<br>import androidx.compose.ui.res.vectorResource<br>import androidx.compose.ui.text.TextStyle<br>import androidx.compose.ui.text.font.FontWeight<br>import androidx.compose.ui.tooling.preview.Preview<br>import androidx.compose.ui.unit.Dp<br>import androidx.compose.ui.unit.dp<br>import androidx.compose.ui.unit.sp<br>// import YOUR_PACKAGE_PATH.R<br><br>data class MenuItem(<br>    val drawableId: Int? = null,<br>    val label: String,<br>    val distinguishedColor: Color? = null,<br>    val testTag: String = &quot;&quot;,<br>    val onClick: () -&gt; Unit<br>)<br><br>data class IconButtonDropdownMenuStyle(<br>    val menuItemColor: Color = Color(0xFF31394F).copy(alpha = 0.8f),<br>    val menuItemTextStyle: TextStyle = TextStyle(<br>        fontWeight = FontWeight.Normal,<br>        fontSize = 14.sp,<br>        color = menuItemColor<br>    ),<br>    val menuItemHeight: Dp = 40.dp,<br>    val menuItemHorizontalPadding: Dp = 16.dp,<br>)<br><br>@Composable<br>fun IconButtonDropdownMenu(<br>    modifier: Modifier = Modifier,<br>    style: IconButtonDropdownMenuStyle = IconButtonDropdownMenuStyle(),<br>    menuItems: List&lt;MenuItem&gt;,<br>    testTag: String = &quot;&quot;,<br>    iconDrawableId: Int = R.drawable.ic_more,<br>) {<br>    val expanded = remember { mutableStateOf(false) }<br>    Column(modifier = modifier) {<br>        IconButton(<br>            onClick = {<br>                expanded.value = true<br>            },<br>            modifier = Modifier.testTag(testTag)<br>        ) {<br>            Icon(<br>                imageVector = ImageVector.vectorResource(id = iconDrawableId),<br>                contentDescription = null<br>            )<br>        }<br>        DropdownMenu(<br>            expanded = expanded.value,<br>            onDismissRequest = { expanded.value = false }<br>        ) {<br>            menuItems.forEach { menuItem -&gt;<br>                DropdownMenuItem(<br>                    text = {<br>                        val textStyle = if (menuItem.distinguishedColor != null) {<br>                            style.menuItemTextStyle.copy(color = menuItem.distinguishedColor)<br>                        } else {<br>                            style.menuItemTextStyle<br>                        }<br>                        Text(<br>                            menuItem.label,<br>                            style = textStyle,<br>                            modifier = Modifier.testTag(menuItem.testTag)<br>                        )<br>                    },<br>                    onClick = {<br>                        expanded.value = false<br>                        menuItem.onClick()<br>                    },<br>                    // Use the &#39;let&#39; function to execute the lambda only if<br>                    // &#39;drawableId&#39; is not null.<br>                    // This ensures that the &#39;leadingIcon&#39; will only be added to<br>                    // the DropdownMenuItem if an icon is provided.<br>                    // Otherwise, &#39;leadingIcon&#39; is null,<br>                    // and no space is reserved for it in the layout.<br>                    leadingIcon = menuItem.drawableId?.let { id -&gt;<br>                        val iconTint = menuItem.distinguishedColor ?: style.menuItemColor<br>                        {<br>                            Icon(<br>                                imageVector = ImageVector.vectorResource(id = id),<br>                                contentDescription = null,<br>                                tint = iconTint<br>                            )<br>                        }<br>                    },<br>                    modifier = Modifier<br>                        .fillMaxWidth()<br>                        .height(style.menuItemHeight),<br>                    contentPadding = PaddingValues(horizontal = style.menuItemHorizontalPadding)<br>                )<br>            }<br>        }<br>    }<br>}<br><br>@Preview<br>@Composable<br>fun IconButtonDropdownMenuPreview() {<br>    val context = LocalContext.current<br>    Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {<br>        IconButtonDropdownMenu(<br>            menuItems = listOf(<br>                MenuItem(<br>                    R.drawable.ic_calendar,<br>                    label = &quot;Calendar&quot;,<br>                    distinguishedColor = Color.Red<br>                ) {<br>                    Toast.makeText(context, &quot;Calendar&quot;, Toast.LENGTH_SHORT).show()<br>                },<br>                MenuItem(R.drawable.ic_done, label = &quot;Check&quot;) {<br>                    Toast.makeText(context, &quot;Check&quot;, Toast.LENGTH_SHORT).show()<br>                },<br>                MenuItem(<br>                    label = &quot;Add Project&quot;,<br>                    distinguishedColor = Color.Blue<br>                ) {<br>                    Toast.makeText(context, &quot;Add Project&quot;, Toast.LENGTH_SHORT).show()<br>                },<br>            )<br>        )<br>    }<br>}</pre><h3>Preview Result</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*WWqjhNX3wuLM67fcbdZEig.gif" /></figure><h3>Explore More</h3><p>For developers eager to deepen their understanding or explore more about modern Android development, a wealth of resources and guides await.</p><ul><li><a href="https://medium.com/deuk/android-compose-tutorials-library-54111197c22d">Android Compose Tutorials Library</a></li><li><a href="https://medium.com/deuk/the-ultimate-android-development-career-guide-906ea32d5c8a">The Ultimate Android Development Career Guide</a></li></ul><p>If you’ve found this resource helpful, consider sharing your support through claps or a follow, and stay tuned for more insights into the evolving landscape of Android development. See you the next time!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=132be7e64dfc" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deuk/android-task-app-implementation-series-icon-button-with-customizable-menu-132be7e64dfc">Android Compose Task App Implementation Series: Icon Button with Customizable Menu</a> was originally published in <a href="https://medium.com/deuk">AnotherAgent</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android Compose Task App Implementation Series: Custom Dropdown Menu]]></title>
            <link>https://medium.com/deuk/android-task-app-implementation-series-custom-dropdown-menu-13ee0e1e0571?source=rss-ee0f8a90df77------2</link>
            <guid isPermaLink="false">https://medium.com/p/13ee0e1e0571</guid>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[android-compose]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <dc:creator><![CDATA[Ken Ruiz Inoue]]></dc:creator>
            <pubDate>Thu, 14 Mar 2024 20:30:34 GMT</pubDate>
            <atom:updated>2025-11-03T23:14:08.954Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*kcxt3uyPihv4Vj4JG0LQCQ.png" /></figure><h3>Production App</h3><p>Anothertask Version 1.0.0 is available <a href="https://play.google.com/store/apps/details?id=com.deuk.anothertask">here</a> if you are interested 😉</p><h3>Objective</h3><p>Recreate the Custom Dropdown Menu UI component from the <a href="https://themeforest.net/item/taskose-task-management-app-figma-template/40364051">Taskose</a> UI design kit I acquired, utilizing Android Jetpack Compose.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/814/1*Ruu8Pp2_5tBlvCDaD5q-vw.png" /></figure><h3>Environment</h3><ul><li>Android Studio Iguana | 2023.2.1</li><li>Compose version: composeBom = &quot;2023.08.00&quot;</li><li>Pixel 6 API 30 Emulator</li><li>Project Minimum SDK: 27</li></ul><h3>Implementation</h3><h4>Step 1</h4><p>Download the needed icons <a href="https://drive.google.com/drive/folders/1axItDkEeuhJ0HS4j2MwVqKIpG2txUsTA?usp=share_link">here</a>, and add them to res/drawable directory.</p><h4>Step 2</h4><p>Create CustomDropdownMenu.kt.</p><pre>// Your package ...<br><br>import androidx.compose.foundation.BorderStroke<br>import androidx.compose.foundation.layout.*<br>import androidx.compose.foundation.shape.RoundedCornerShape<br>import androidx.compose.material3.*<br>import androidx.compose.runtime.Composable<br>import androidx.compose.runtime.mutableStateOf<br>import androidx.compose.runtime.remember<br>import androidx.compose.ui.Alignment<br>import androidx.compose.ui.Modifier<br>import androidx.compose.ui.graphics.Color<br>import androidx.compose.ui.graphics.vector.ImageVector<br>import androidx.compose.ui.res.vectorResource<br>import androidx.compose.ui.text.TextStyle<br>import androidx.compose.ui.text.font.FontWeight<br>import androidx.compose.ui.unit.Dp<br>import androidx.compose.ui.unit.dp<br>import androidx.compose.ui.unit.sp<br><br>// Styles configuration for dropdown menu<br>data class CustomDropdownMenuStyles(<br>    val height: Dp = 40.dp,<br>    val mainColor: Color = Color(0xFF31394F),<br>    val strokeColor: Color = mainColor.copy(alpha = 0.4f),<br>    val buttonContainerColor: Color = Color(0xFFF8FAFC),<br>    val buttonCornerRadius: Dp = 12.dp,<br>    val buttonVerticalPadding: Dp = 12.dp,<br>    val buttonBorderStroke: BorderStroke = BorderStroke(1.dp, strokeColor),<br>    val buttonHorizontalPadding: Dp = 16.dp,<br>    val buttonTextStyle: TextStyle = TextStyle(<br>        fontWeight = FontWeight.Medium,<br>        fontSize = 14.sp,<br>        color = mainColor.copy(alpha = 0.8f)<br>    ),<br>    val menuItemTextStyle: TextStyle = TextStyle(<br>        fontWeight = FontWeight.Normal,<br>        fontSize = 14.sp,<br>        color = mainColor.copy(alpha = 0.8f)<br>    ),<br>    val menuItemHeight: Dp = 40.dp,<br>    val menuItemHorizontalPadding: Dp = 16.dp,<br>    val iconColor: Color = mainColor.copy(alpha = 0.8f),<br>    val leftIconDrawableId: Int? = null,<br>    val expandedLeftIconDrawable: Int? = null,<br>    val rightIconDrawableId: Int? = null,<br>    val expandedRightIconDrawable: Int? = null<br>)<br><br>// Chooses the correct icon based on the dropdown&#39;s state<br>fun CustomDropdownMenuStyles.getIconDrawable(expanded: Boolean, isLeftIcon: Boolean): Int? {<br>    return if (isLeftIcon) {<br>        if (expanded) expandedLeftIconDrawable ?: leftIconDrawableId else leftIconDrawableId<br>    } else {<br>        if (expanded) expandedRightIconDrawable ?: rightIconDrawableId else rightIconDrawableId<br>    }<br>}<br><br>// Renders a custom dropdown menu<br>@Composable<br>fun CustomDropdownMenu(<br>    modifier: Modifier = Modifier,<br>    buttonText: String = &quot;Select an item&quot;,<br>    menuItems: List&lt;Pair&lt;String, () -&gt; Unit&gt;&gt;,<br>    styles: CustomDropdownMenuStyles = CustomDropdownMenuStyles()<br>) {<br>    val expanded = remember { mutableStateOf(false) }<br><br>    Column(modifier = modifier) {<br>        // Dropdown button that controls menu visibility<br>        OutlinedDropdownButton(<br>            buttonText = buttonText,<br>            leftIconDrawableId = styles.getIconDrawable(expanded.value, isLeftIcon = true),<br>            rightIconDrawableId = styles.getIconDrawable(expanded.value, isLeftIcon = false),<br>            styles = styles<br>        ) {<br>            expanded.value = true<br>        }<br>        // Conditional display of menu items<br>        DropdownMenu(<br>            expanded = expanded.value,<br>            onDismissRequest = { expanded.value = false }<br>        ) {<br>            menuItems.forEach { (label, onClick) -&gt;<br>                DropdownMenuItem(<br>                    text = { Text(label, style = styles.menuItemTextStyle) },<br>                    onClick = {<br>                        expanded.value = false<br>                        onClick()<br>                    },<br>                    modifier = Modifier<br>                        .fillMaxWidth()<br>                        .height(styles.menuItemHeight),<br>                    contentPadding = PaddingValues(horizontal = styles.menuItemHorizontalPadding)<br>                )<br>            }<br>        }<br>    }<br>}<br><br>// Dropdown button component<br>@Composable<br>fun OutlinedDropdownButton(<br>    buttonText: String,<br>    leftIconDrawableId: Int?,<br>    rightIconDrawableId: Int?,<br>    styles: CustomDropdownMenuStyles,<br>    onClick: () -&gt; Unit<br>) {<br>    OutlinedButton(<br>        onClick = onClick,<br>        modifier = Modifier<br>            .height(styles.height)<br>            .wrapContentWidth(),<br>        border = styles.buttonBorderStroke,<br>        shape = RoundedCornerShape(styles.buttonCornerRadius),<br>        colors = ButtonDefaults.outlinedButtonColors(<br>            containerColor = styles.buttonContainerColor<br>        ),<br>        contentPadding = PaddingValues(<br>            horizontal = styles.buttonHorizontalPadding,<br>            vertical = styles.buttonVerticalPadding<br>        )<br>    ) {<br>        Row(verticalAlignment = Alignment.CenterVertically) {<br>            // Optional left icon<br>            leftIconDrawableId?.let {<br>                Icon(<br>                    imageVector = ImageVector.vectorResource(id = it),<br>                    contentDescription = null,<br>                    tint = styles.iconColor<br>                )<br>                Spacer(modifier = Modifier.width(4.dp))<br>            }<br>            // Button text<br>            Text(buttonText, style = styles.buttonTextStyle)<br>            // Optional right icon<br>            rightIconDrawableId?.let {<br>                Spacer(modifier = Modifier.width(4.dp))<br>                Icon(<br>                    imageVector = ImageVector.vectorResource(id = it),<br>                    contentDescription = null,<br>                    tint = styles.iconColor<br>                )<br>            }<br>        }<br>    }<br>}</pre><h4>Step 3</h4><p>Create CustomDropdownMenuPreview.kt.</p><pre>// Your package ...<br><br>import android.widget.Toast<br>import androidx.compose.foundation.background<br>import androidx.compose.foundation.layout.Box<br>import androidx.compose.foundation.layout.Column<br>import androidx.compose.foundation.layout.Spacer<br>import androidx.compose.foundation.layout.fillMaxSize<br>import androidx.compose.foundation.layout.height<br>import androidx.compose.runtime.Composable<br>import androidx.compose.ui.Alignment<br>import androidx.compose.ui.Modifier<br>import androidx.compose.ui.graphics.Color<br>import androidx.compose.ui.platform.LocalContext<br>import androidx.compose.ui.tooling.preview.Preview<br>import androidx.compose.ui.unit.dp<br>// import YOUR_PACKAGE_PATH.R<br><br>@Preview<br>@Composable<br>fun CustomDropdownMenuPreview() {<br>    val context = LocalContext.current<br>    Box(<br>        modifier = Modifier<br>            .fillMaxSize()<br>            .background(Color(0xFFB4BACC)),<br>        contentAlignment = Alignment.Center<br>    ) {<br>        Column {<br>            CustomDropdownMenu(<br>                menuItems = listOf(<br>                    &quot;Item 1&quot; to {<br>                        Toast<br>                            .makeText(context, &quot;Item 1 clicked&quot;, Toast.LENGTH_SHORT)<br>                            .show()<br>                    },<br>                    &quot;Item 2&quot; to {<br>                        Toast<br>                            .makeText(context, &quot;Item 2 clicked&quot;, Toast.LENGTH_SHORT)<br>                            .show()<br>                    },<br>                    &quot;Item 3&quot; to {<br>                        Toast<br>                            .makeText(context, &quot;Item 3 clicked&quot;, Toast.LENGTH_SHORT)<br>                            .show()<br>                    }<br>                ),<br>                styles = CustomDropdownMenuStyles(<br>                    rightIconDrawableId = R.drawable.ic_expand_more,<br>                    expandedRightIconDrawable = R.drawable.ic_expand_less<br>                ),<br>            )<br>            Spacer(modifier = Modifier.height(16.dp))<br>            CustomDropdownMenu(<br>                buttonText = &quot;Select an option&quot;,<br>                menuItems = listOf(<br>                    &quot;Option 1&quot; to {<br>                        Toast<br>                            .makeText(context, &quot;Option 1 clicked&quot;, Toast.LENGTH_SHORT)<br>                            .show()<br>                    },<br>                    &quot;Option 2&quot; to {<br>                        Toast<br>                            .makeText(context, &quot;Option 2 clicked&quot;, Toast.LENGTH_SHORT)<br>                            .show()<br>                    },<br>                    &quot;Option 3&quot; to {<br>                        Toast<br>                            .makeText(context, &quot;Option 3 clicked&quot;, Toast.LENGTH_SHORT)<br>                            .show()<br>                    }<br>                ),<br>                styles = CustomDropdownMenuStyles(<br>                    leftIconDrawableId = R.drawable.ic_more<br>                )<br>            )<br>        }<br>    }<br>}</pre><h3>Preview Result</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ok7ighoUoRrkpbDiT7tXWg.gif" /></figure><h3>Explore More</h3><p>For developers eager to deepen their understanding or explore more about modern Android development, a wealth of resources and guides await.</p><ul><li><a href="https://medium.com/deuk/android-compose-tutorials-library-54111197c22d">Android Compose Tutorials Library</a></li><li><a href="https://medium.com/deuk/the-ultimate-android-development-career-guide-906ea32d5c8a">The Ultimate Android Development Career Guide</a></li></ul><p>If you’ve found this resource helpful, consider sharing your support through claps or a follow, and stay tuned for more insights into the evolving landscape of Android development. See you the next time!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=13ee0e1e0571" width="1" height="1" alt=""><hr><p><a href="https://medium.com/deuk/android-task-app-implementation-series-custom-dropdown-menu-13ee0e1e0571">Android Compose Task App Implementation Series: Custom Dropdown Menu</a> was originally published in <a href="https://medium.com/deuk">AnotherAgent</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>