<?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: Antonio Huerta Reyes</title>
    <description>The latest articles on DEV Community by Antonio Huerta Reyes (@tonyakitori).</description>
    <link>https://dev.to/tonyakitori</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F755038%2Fa21c8b24-a591-4ac3-9efc-131a02d524ad.jpeg</url>
      <title>DEV Community: Antonio Huerta Reyes</title>
      <link>https://dev.to/tonyakitori</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tonyakitori"/>
    <language>en</language>
    <item>
      <title>Introducing TourCompose: The Easiest Way to Add Interactive Tours to Your Android Compose App (Without Losing Your Sanity)</title>
      <dc:creator>Antonio Huerta Reyes</dc:creator>
      <pubDate>Sun, 03 Aug 2025 01:03:15 +0000</pubDate>
      <link>https://dev.to/tonyakitori/introducing-tourcompose-the-easiest-way-to-add-interactive-tours-to-your-android-compose-app-3k2j</link>
      <guid>https://dev.to/tonyakitori/introducing-tourcompose-the-easiest-way-to-add-interactive-tours-to-your-android-compose-app-3k2j</guid>
      <description>&lt;p&gt;I get it – you're busy, you want to see results NOW, and you probably have 17 other tabs open. So here's what you came for:&lt;/p&gt;

&lt;p&gt;🎯 &lt;strong&gt;Live Demo:&lt;/strong&gt; &lt;a href="https://github.com/AntonioHReyes/TourCompose" rel="noopener noreferrer"&gt;Try it on GitHub&lt;/a&gt;&lt;br&gt;&lt;br&gt;
📦 &lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/AntonioHReyes/TourCompose" rel="noopener noreferrer"&gt;github.com/AntonioHReyes/TourCompose&lt;/a&gt;&lt;br&gt;&lt;br&gt;
⚡ &lt;strong&gt;Quick Start:&lt;/strong&gt; Just add &lt;code&gt;implementation("com.github.AntonioHReyes:TourCompose:1.0.1")&lt;/code&gt; to your gradle  &lt;/p&gt;

&lt;p&gt;Got 2 minutes? Copy-paste the complete working example below and you'll have a working tour in your app.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Want the full story and all the juicy details? Keep reading – I promise it's worth it (and slightly entertaining). Otherwise, you know what to do.&lt;/em&gt; 👇&lt;/p&gt;


&lt;h2&gt;
  
  
  🤷‍♂️ &lt;strong&gt;The Full Story (For Those Who Like Context)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Let's be honest – adding onboarding tours to your Android app has traditionally been about as fun as debugging a null pointer exception at 3 AM. You've probably been there: wrestling with complex libraries, writing endless configuration files, and somehow ending up with tour bubbles that look like they time-traveled from 2010.&lt;/p&gt;

&lt;p&gt;Well, grab your coffee ☕ and prepare to be pleasantly surprised. Meet &lt;strong&gt;TourCompose&lt;/strong&gt; – the library that finally makes creating beautiful app tours as easy as it should have been all along.&lt;/p&gt;
&lt;h2&gt;
  
  
  🤔 The Problem We All Know Too Well
&lt;/h2&gt;

&lt;p&gt;Picture this: Your PM walks over (or pings you on Slack if you're lucky enough to work remotely) and says, "Hey, can we add some onboarding to help users understand the app? Should be super quick, right?"&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Internal screaming intensifies&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You know what comes next:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hours of documentation reading 📚&lt;/li&gt;
&lt;li&gt;Complex XML configurations that make you question your life choices&lt;/li&gt;
&lt;li&gt;Tour bubbles that somehow always appear in the wrong place&lt;/li&gt;
&lt;li&gt;Styling that looks like it was designed by a caffeinated developer at 2 AM (spoiler: it probably was)&lt;/li&gt;
&lt;li&gt;That one edge case where the tour breaks completely, but only on Samsung devices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sound familiar? Yeah, we've all been there.&lt;/p&gt;
&lt;h2&gt;
  
  
  🚀 Enter TourCompose: Your New Best Friend
&lt;/h2&gt;

&lt;p&gt;TourCompose was born from the simple idea that creating app tours shouldn't require a computer science degree. It's designed by developers, for developers who just want things to work without having to sacrifice their weekends to the onboarding gods.&lt;/p&gt;
&lt;h3&gt;
  
  
  What Makes It Special?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero complex setup&lt;/strong&gt; – Because life's too short for 50-line configuration files&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Works with Material Theme out of the box&lt;/strong&gt; – Your designer will actually thank you&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flexible but not overwhelming&lt;/strong&gt; – Customize when you need to, ignore when you don't&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actually tested in real apps&lt;/strong&gt; – Not just in "hello world" examples&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  📦 Setup So Easy, Your Intern Could Do It
&lt;/h2&gt;

&lt;p&gt;First, add this to your app's &lt;code&gt;build.gradle.kts&lt;/code&gt; (yes, that's really all you need):&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;dependencies&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;implementation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.github.AntonioHReyes:TourCompose:1.0.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;Then add JitPack to your &lt;code&gt;settings.gradle.kts&lt;/code&gt; (because apparently we all agreed that having 47 different repositories is totally normal):&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;dependencyResolutionManagement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;repositories&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;google&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;mavenCentral&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;maven&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"https://jitpack.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;// The hero we need&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;That's it. No, seriously. No additional permissions, no manifest entries, no sacrificial offerings to the Android gods.&lt;/p&gt;

&lt;h2&gt;
  
  
  💡 The "Holy Grail" Example: Copy, Paste, Ship
&lt;/h2&gt;

&lt;p&gt;Remember how I mentioned this should be easy? Here's a complete, working example that you can literally copy and paste into a new project and it'll work. I'm not kidding – I actually tested this one:&lt;/p&gt;

&lt;h3&gt;
  
  
  MainActivity.kt
&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;import&lt;/span&gt; &lt;span class="nn"&gt;android.os.Bundle&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.activity.ComponentActivity&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.activity.compose.setContent&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.foundation.layout.Arrangement&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.foundation.layout.Column&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.foundation.layout.Spacer&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.foundation.layout.fillMaxSize&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.foundation.layout.fillMaxWidth&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.foundation.layout.padding&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.material3.Button&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.material3.ButtonDefaults&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.material3.Card&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.material3.MaterialTheme&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.material3.Text&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.runtime.collectAsState&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.runtime.getValue&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.runtime.remember&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.ui.Alignment&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.ui.Modifier&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.compose.ui.unit.dp&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.tonyakitori.apps.tourcompose.TourCompose&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.tonyakitori.apps.tourcompose.controller.TourComposeController&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.tonyakitori.apps.tourcompose.controller.TourComposeStep&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.tonyakitori.apps.tourcompose.controller.TourComposeWrapper&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.tonyakitori.apps.tourcompose.settings.bubbleContent.bubbleContentBasicSettings&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.UUID&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MainActivity&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ComponentActivity&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="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bundle&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="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;setContent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;MaterialTheme&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;tourController&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;remember&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;SimpleTourController&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

                &lt;span class="nc"&gt;TourComposeWrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tourController&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;tourController&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;currentStep&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;tourController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;currentStep&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collectAsState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initial&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;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;
                            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillMaxSize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                            &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="n"&gt;verticalArrangement&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Arrangement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spacedBy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
                        &lt;span class="n"&gt;horizontalAlignment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Alignment&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;CenterHorizontally&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

                        &lt;span class="nc"&gt;Text&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="s"&gt;"Welcome to my awesome app!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MaterialTheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;typography&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;headlineMedium&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tourStepIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"welcome-tour"&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="p"&gt;)&lt;/span&gt;

                        &lt;span class="nc"&gt;Text&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="s"&gt;"This app does amazing things that will change your life, cure your anxiety, and probably make you a better person."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MaterialTheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;typography&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bodyLarge&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                            &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="p"&gt;)&lt;/span&gt;

                        &lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="n"&gt;onClick&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="c1"&gt;// This button obviously does something super important&lt;/span&gt;
                                &lt;span class="nf"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Button clicked! 🎉"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                            &lt;span class="p"&gt;},&lt;/span&gt;
                            &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tourStepIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"welcome-tour"&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;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Do Something Amazing"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                        &lt;span class="p"&gt;}&lt;/span&gt;

                        &lt;span class="nc"&gt;Card&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;
                                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fillMaxWidth&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                                &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tourStepIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"welcome-tour"&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="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                            &lt;span class="nc"&gt;Column&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;padding&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dp&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                                &lt;span class="nc"&gt;Text&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="s"&gt;"Pro Feature™"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MaterialTheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;typography&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;titleMedium&lt;/span&gt;
                                &lt;span class="p"&gt;)&lt;/span&gt;
                                &lt;span class="nc"&gt;Text&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="s"&gt;"This card contains features so advanced that we needed a tour just to explain them."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                    &lt;span class="n"&gt;style&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MaterialTheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;typography&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;bodyMedium&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;Spacer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;weight&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1f&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

                        &lt;span class="nc"&gt;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                            &lt;span class="n"&gt;onClick&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;tourController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startTour&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"welcome-tour"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                            &lt;span class="n"&gt;colors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ButtonDefaults&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;buttonColors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                                &lt;span class="n"&gt;containerColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MaterialTheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;colorScheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;secondary&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;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"🎯 Start the Tour"&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="c1"&gt;// Here's where the magic happens ✨&lt;/span&gt;
                    &lt;span class="nc"&gt;TourCompose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;componentRectArea&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentStep&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;componentRect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;bubbleContentSettings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentStep&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;bubbleContentSettings&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="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// This controller is so simple, it's almost embarrassing how easy this is&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SimpleTourController&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TourComposeController&lt;/span&gt;&lt;span class="p"&gt;()&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;addTour&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;flowId&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"welcome-tour"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;steps&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;TourComposeStep&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;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;bubbleContentSettings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bubbleContentBasicSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"👋 Hey There!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"This is your app's main title. It's probably the first thing users see, so we made it big and friendly. Good choice!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;primaryButtonText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Makes Sense"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;onPrimaryClick&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;nextStep&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                        &lt;span class="n"&gt;onDismiss&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;stopTour&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="nc"&gt;TourComposeStep&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;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;bubbleContentSettings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bubbleContentBasicSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"🚀 The Action Button"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"This button is where the magic happens. Click it to do something amazing (results may vary, but we're optimistic)."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;primaryButtonText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Got It!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;secondaryButtonText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Wait, Go Back"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;onPrimaryClick&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;nextStep&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                        &lt;span class="n"&gt;onSecondaryClick&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;previousStep&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                        &lt;span class="n"&gt;onDismiss&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;stopTour&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="nc"&gt;TourComposeStep&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;span class="nc"&gt;UUID&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;randomUUID&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                    &lt;span class="n"&gt;bubbleContentSettings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bubbleContentBasicSettings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"💎 Premium Content"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"This card showcases your premium features. It's fancy, it's important, and now your users know exactly where to find it!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;primaryButtonText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Awesome, I'm Done!"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;secondaryButtonText&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Show Me Again"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="n"&gt;onPrimaryClick&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;stopTour&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                        &lt;span class="n"&gt;onSecondaryClick&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;previousStep&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
                        &lt;span class="n"&gt;onDismiss&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;stopTour&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="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;
  
  
  🎉 Run It and Watch the Magic
&lt;/h2&gt;

&lt;p&gt;Seriously, that's it. Copy that code, create a new Compose project, paste it in, and hit run. You'll get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Beautiful, themed tour bubbles that actually look good ✨&lt;/li&gt;
&lt;li&gt;Navigation that just works (forward, backward, dismiss) 🔄&lt;/li&gt;
&lt;li&gt;Automatic component highlighting that finds your UI elements like a GPS for tours 📍&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🎨 But Wait, There's More! (Customization Without the Headache)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  The "I Just Want Different Colors" Solution and the "I Want It to Match My Theme" Solution
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Your designer will think you're a wizard 🧙‍♂️&lt;/span&gt;
&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;contentBubbleColors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defaultBubbleContentColors&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;titleTextColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MaterialTheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;colorScheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;error&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The "I Need This Tour to Look Like It Came From the Future" Solution
&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;TourCompose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;componentRectArea&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentStep&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;componentRect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bubbleContentSettings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentStep&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;bubbleContentSettings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;tourComposeProperties&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TourComposeProperties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getDefaultInstance&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;spotlightColors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DefaultSpotlightColors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;overlayBackgroundColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Black&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;copy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.9f&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;overlayBorderColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Cyan&lt;/span&gt;
        &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="n"&gt;dialogBubbleColors&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DefaultDialogBubbleColors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;backgroundColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Black&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;borderColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Cyan&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;
  
  
  🤓 For the Overachievers: Advanced Features
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Smart Component Targeting
&lt;/h3&gt;

&lt;p&gt;Just slap &lt;code&gt;.tourStepIndex("your-tour-id", stepNumber)&lt;/code&gt; on any Composable and boom – it's part of your tour:&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;// Works on literally anything&lt;/span&gt;
&lt;span class="nc"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tourStepIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"intro"&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;Button&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;onClick&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tourStepIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"intro"&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;span class="nc"&gt;LazyColumn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tourStepIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"intro"&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="p"&gt;{}&lt;/span&gt;
&lt;span class="nc"&gt;YourCustomComposable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;modifier&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Modifier&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tourStepIndex&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"intro"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Multiple Tours (Because Why Not?)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Welcome tour for new users&lt;/span&gt;
&lt;span class="n"&gt;tourController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startTour&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"welcome-tour"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// Feature tour for when you add something cool&lt;/span&gt;
&lt;span class="n"&gt;tourController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startTour&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"new-feature-tour"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;// "Please rate us" tour for when you're feeling brave&lt;/span&gt;
&lt;span class="n"&gt;tourController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startTour&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"desperate-for-ratings-tour"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Custom Bubble Content (For the Creative Souls)
&lt;/h3&gt;

&lt;p&gt;Want to add progress bars, ratings, custom buttons, or a dancing cat GIF? TourCompose supports custom bubble content that can be as wild as your imagination (and your designer's caffeine level).&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Getting Started (The TL;DR Version)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add the dependency&lt;/strong&gt; (2 lines in your gradle files)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Copy the example above&lt;/strong&gt; (literally copy-paste, we won't judge)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run it and smile&lt;/strong&gt; (because it actually works)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customize to your heart's content&lt;/strong&gt; (or don't, the defaults are pretty nice)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ship it&lt;/strong&gt; (and maybe buy yourself a coffee to celebrate)&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  🔗 Links for the Impatient
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repository:&lt;/strong&gt; &lt;a href="https://github.com/AntonioHReyes/TourCompose" rel="noopener noreferrer"&gt;github.com/AntonioHReyes/TourCompose&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;License:&lt;/strong&gt; MIT (because we're not monsters)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Documentation:&lt;/strong&gt; In the README (and it's actually helpful)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🚀 Final Thoughts
&lt;/h2&gt;

&lt;p&gt;Creating good onboarding shouldn't be harder than building the actual app features. TourCompose exists because we got tired of fighting with tour libraries instead of building cool stuff.&lt;/p&gt;

&lt;p&gt;Whether you're a solo indie developer building the next big thing, or part of a team shipping features to millions of users, TourCompose has your back. It's simple enough for quick prototypes and powerful enough for production apps.&lt;/p&gt;

&lt;p&gt;So go ahead, add those tours your PM has been asking for. Make your app more user-friendly. Help your users discover features they didn't know existed. And do it all without losing your sanity or your weekend.&lt;/p&gt;

&lt;p&gt;Your users (and your mental health) will thank you. 🙌&lt;/p&gt;

&lt;p&gt;Ready to make onboarding tours that don't suck? Give TourCompose a spin – we promise it's more fun than debugging memory leaks! &lt;/p&gt;




&lt;p&gt;&lt;em&gt;P.S. – If you end up using TourCompose and it saves you time, star the repo. It makes us feel good about our life choices and motivates us to keep making cool stuff. Plus, your GitHub activity graph will look more impressive. Win-win! ⭐&lt;/em&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>jetpack</category>
      <category>kotlin</category>
      <category>tour</category>
    </item>
    <item>
      <title>CitRep - Android + Appwrite - Hackathon</title>
      <dc:creator>Antonio Huerta Reyes</dc:creator>
      <pubDate>Tue, 10 May 2022 15:50:25 +0000</pubDate>
      <link>https://dev.to/tonyakitori/citrep-android-appwrite-hackathon-4f21</link>
      <guid>https://dev.to/tonyakitori/citrep-android-appwrite-hackathon-4f21</guid>
      <description>&lt;h3&gt;
  
  
  Overview of My Submission
&lt;/h3&gt;

&lt;p&gt;CitRep is the prototype of an application to send complaints to a municipality, it is designed to speed up the complaint processes in the cities and towns of Mexico.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This app is designed with a clean architecture pattern and uses kotlin as its programming language.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The app is a prototype of a bigger idea and works as a preamble for the creation of the final version, before this I must admit that I had not heard of appwrite, however, with this hackathon I have learned a lot from this platform and I am delighted with the speed of development that I provide.&lt;/p&gt;

&lt;p&gt;This application use the next appwrite features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create account&lt;/li&gt;
&lt;li&gt;Create session with user and password&lt;/li&gt;
&lt;li&gt;Delete Session&lt;/li&gt;
&lt;li&gt;Get Account&lt;/li&gt;
&lt;li&gt;Email verification&lt;/li&gt;
&lt;li&gt;Get avatar with the name&lt;/li&gt;
&lt;li&gt;Database list documents&lt;/li&gt;
&lt;li&gt;RealTime&lt;/li&gt;
&lt;li&gt;Create and save a new document&lt;/li&gt;
&lt;li&gt;Upload and download image from appwrite bucket&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;p&gt;Mobile Moguls&lt;/p&gt;

&lt;h3&gt;
  
  
  Link to Code
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/AntonioHReyes"&gt;
        AntonioHReyes
      &lt;/a&gt; / &lt;a href="https://github.com/AntonioHReyes/CitRep-appwrite"&gt;
        CitRep-appwrite
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
CitRep-appwrite&lt;/h1&gt;
&lt;p&gt;CitRep is the prototype of an application to send complaints to a municipality,
it is designed to speed up the complaint processes in the cities and towns of Mexico&lt;/p&gt;
&lt;p&gt;The application is designed by clean arquitecture pattern.&lt;/p&gt;
&lt;h2&gt;
TechStack&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/appwrite/appwrite"&gt;Appwrite&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kotlinlang.org/" rel="nofollow"&gt;Kotlin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.android.com/studio" rel="nofollow"&gt;Android Studio&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Setup&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Install appwrite in your local docker&lt;/li&gt;
&lt;li&gt;Replace projectId and endpoint url in build.gradle&lt;/li&gt;
&lt;li&gt;Generate a post and notifications collections and replace ids in build.gradle&lt;/li&gt;
&lt;li&gt;Create a bucket and replace id in build.gradle&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Appwrite features implemented&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Create account&lt;/li&gt;
&lt;li&gt;Create session with user and password&lt;/li&gt;
&lt;li&gt;Delete Session&lt;/li&gt;
&lt;li&gt;Get Account&lt;/li&gt;
&lt;li&gt;Email verification&lt;/li&gt;
&lt;li&gt;Get avatar with the name&lt;/li&gt;
&lt;li&gt;Database list documents&lt;/li&gt;
&lt;li&gt;RealTime&lt;/li&gt;
&lt;li&gt;Create and save a new document&lt;/li&gt;
&lt;li&gt;Upload and download image from appwrite bucket&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Screen gifs&lt;/h2&gt;
&lt;p&gt;Signup&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://user-images.githubusercontent.com/51680520/167661633-76eb30fb-b71a-4f50-a419-f5e1630a8cfd.gif"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SwH9rtOK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://user-images.githubusercontent.com/51680520/167661633-76eb30fb-b71a-4f50-a419-f5e1630a8cfd.gif" alt="sign_up"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Login&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://user-images.githubusercontent.com/51680520/167660865-7020250f-e9d1-42d0-a93e-929eb1b7f5e8.gif"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--uTeifTLI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://user-images.githubusercontent.com/51680520/167660865-7020250f-e9d1-42d0-a93e-929eb1b7f5e8.gif" alt="login"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Post simple&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://user-images.githubusercontent.com/51680520/167661761-01dc6665-7913-4794-85ae-8062987d2831.gif"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--36qDbfmL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://user-images.githubusercontent.com/51680520/167661761-01dc6665-7913-4794-85ae-8062987d2831.gif" alt="post_simple"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Post with images&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://user-images.githubusercontent.com/51680520/167661798-55e4ea1d-3873-4831-bbe2-f6a39fc1885b.gif"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Ft1DK6qR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://user-images.githubusercontent.com/51680520/167661798-55e4ea1d-3873-4831-bbe2-f6a39fc1885b.gif" alt="post_imagen"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Navigation&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://user-images.githubusercontent.com/51680520/167661874-d4c86732-477c-43e8-b92d-e552abab3ebf.gif"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vqOOazID--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://user-images.githubusercontent.com/51680520/167661874-d4c86732-477c-43e8-b92d-e552abab3ebf.gif" alt="navegacion"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Email verification flow&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://user-images.githubusercontent.com/51680520/167661962-c53fb340-ca68-4fb6-b0a1-7f575de6d6b1.gif"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--CQSQqFdn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://user-images.githubusercontent.com/51680520/167661962-c53fb340-ca68-4fb6-b0a1-7f575de6d6b1.gif" alt="verification"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;



&lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/AntonioHReyes/CitRep-appwrite"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


&lt;h3&gt;
  
  
  Additional Resources / Info
&lt;/h3&gt;

&lt;p&gt;Here are some flows implemented within the application&lt;/p&gt;

&lt;p&gt;Signup&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RmMjy8xH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/00dkfcbrqnezfs2mkxbz.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RmMjy8xH--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/00dkfcbrqnezfs2mkxbz.gif" alt="Signup" width="284" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Login&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QidFMF6W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kjxax8f0zzu480ksaogn.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QidFMF6W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kjxax8f0zzu480ksaogn.gif" alt="Login" width="284" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Navigation&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jli7fjkF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u14rimusb8yzoq642ruf.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jli7fjkF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/u14rimusb8yzoq642ruf.gif" alt="Navigation" width="284" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Publish a simple post&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--i-mqOtx_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5w95n5pijsp5ci0h4mdb.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--i-mqOtx_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5w95n5pijsp5ci0h4mdb.gif" alt="simple post" width="284" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Publish a post with images&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--oEE70Nro--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1d9mf0wuq553ouodpaj0.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--oEE70Nro--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1d9mf0wuq553ouodpaj0.gif" alt="images post" width="284" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Email verification flow&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GIqmcWg1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0dsvlty52tur1hyjrsnu.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GIqmcWg1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0dsvlty52tur1hyjrsnu.gif" alt="Email verification flow" width="284" height="615"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>appwritehack</category>
      <category>android</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>SetInterval en Android con Kotlin</title>
      <dc:creator>Antonio Huerta Reyes</dc:creator>
      <pubDate>Sat, 19 Mar 2022 20:26:23 +0000</pubDate>
      <link>https://dev.to/tonyakitori/setinterval-en-android-con-kotlin-19fa</link>
      <guid>https://dev.to/tonyakitori/setinterval-en-android-con-kotlin-19fa</guid>
      <description>&lt;p&gt;Durante mis inicios como desarrollador en más de una ocasión me encontré con el problema de tener que realizar alguna operación repetitiva cada «N» cantidad de tiempo, por aquellos días mis mejores soluciones dentro del mundo Android rondaban el crear una instancia de la clase Timer y una TimerTask así cuando esta terminara, reiniciaba una vez más el proceso.&lt;/p&gt;

&lt;p&gt;Sin embargo como se puede suponer esta no es la mejor manera de realizarlo, dentro de Android gracias al profiler podía detectar que al estar haciendo este proceso con una función, el mismo se estaba convirtiendo en uno recursivo y por ende el consumo de memoria aunque pequeño crecía.&lt;/p&gt;

&lt;p&gt;Pasado algún tiempo y leyendo la documentación de Kotlin en busca de mejorar mis soluciones y aprender más sobre el lenguaje me encontré con una herramienta que desde ese entonces me ha ayudado a mejorar mi programación cuando de intervalos se trata.&lt;/p&gt;

&lt;p&gt;Dentro de Kotlin existe una &lt;a href="https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.concurrent/"&gt;biblioteca&lt;/a&gt; que proporciona herramientas útiles para la programación concurrente.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;La programación concurrente es la ejecución simultánea de múltiples tareas interactivamente. Estas tareas pueden ser un conjunto de procesos o hilos de ejecución creados por un único programa.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Por ahora nos centraremos en una función que sera fixedRateTimer, en pocas palabras esta función crea un temporizador que ejecuta la acción especificada periódicamente, comenzando después del initialDelay especificado (expresado en milisegundos) y con el intervalo de milisegundos de período entre el inicio de la tarea anterior y el inicio de la siguiente.&lt;/p&gt;

&lt;p&gt;A continuación les comparto un ligero ejemplo donde imprimiremos un mensaje de Hola mundo cada 5 segundos&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4rd1ddEU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sev3604u6q2ytvqypppo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4rd1ddEU--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sev3604u6q2ytvqypppo.png" alt="Example" width="880" height="672"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;El resultado seria el siguiente:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--fj-ru_S2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xjl5x28qebyi6iijjml6.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--fj-ru_S2--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xjl5x28qebyi6iijjml6.gif" alt="Example gif" width="657" height="166"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Así mismo esta función retorna una instancia de Timer con lo cual podemos obtener una referencia y cancelar el proceso una vez que nuestro trabajo repetitivo haya finalizado. Quedando una nueva versión del código en donde queremos finalizar el trabajo tras 5 repeticiones de la siguiente manera.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--kWIkBW3z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4z87tqb52ncofba1hr0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--kWIkBW3z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/g4z87tqb52ncofba1hr0.png" alt="Example" width="880" height="1089"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esta misma función puede aceptar aparte de un delay al inicio también una fecha exacta para ejecutarse, te recomiendo leer más en la documentación oficial para conocer todas las posibilidades que existen.&lt;/p&gt;

&lt;p&gt;Espero te haya gustado este articulo, nos seguimos viendo en el futuro.&lt;/p&gt;

</description>
      <category>kotlin</category>
      <category>android</category>
      <category>setinterval</category>
      <category>feature</category>
    </item>
    <item>
      <title>SetTimeOut en Android</title>
      <dc:creator>Antonio Huerta Reyes</dc:creator>
      <pubDate>Sat, 04 Dec 2021 20:54:19 +0000</pubDate>
      <link>https://dev.to/tonyakitori/settimeout-en-android-1nhn</link>
      <guid>https://dev.to/tonyakitori/settimeout-en-android-1nhn</guid>
      <description>&lt;p&gt;En muchas ocasiones como desarrolladores Android nos hemos encontrado con la necesidad de realizar un proceso pasado cierta cantidad de tiempo o de realizar una cuenta regresiva que permita realizar una acción, en muchos de estos casos solemos hacer uso de los Handler o posiblemente de la funcionalidad delay en las coroutines de kotlin.&lt;/p&gt;

&lt;p&gt;En esta ocasión traigo una alternativa que me ha funcionado exitosamente para muchas de mis implementaciones y que puede ser de utilidad también para muchos desarrolladores Android.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun setTimeOut(handleFunction: () -&amp;gt; Unit, delay: Long): CountDownTimer {

    val timer = object: CountDownTimer(delay, 1000){
        override fun onFinish() {
            CoroutineScope(Dispatchers.Main).launch {
                handleFunction()
                cancel()
            }
        }

        override fun onTick(p0: Long) {}
    }

    timer.start()

    return timer
}

fun clearTimeOut(timer: CountDownTimer){
    try {
        timer.cancel()
    }catch (e: Exception){
        Log.e("Error", e.toString())
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esta alternativa tiene como base el uso de la clase CountDownTimer de Android que en la propia definición de la documentación de Android es:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Schedule a countdown until a time in the future, with regular notifications on intervals along the way. Example of showing a 30 second countdown in a text field&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dicha clase nos permite programar una cuenta regresiva hasta un momento en el futuro con notificaciones regulares de los intervalos, y como se puede observar esta alternativa hace uso de funciones al estilo de JavaScript.&lt;/p&gt;

&lt;p&gt;Nuestra función setTimeOut recibe como parámetros la función que se desea ejecutar tras determinado tiempo, este tiempo viene como el segundo parámetro el cual define en cuantos milisegundos deseas ejecutar la acción.&lt;/p&gt;

&lt;p&gt;El segundo parámetro de la instancia de CountDownTimer es el intervalo que estará ejecutando el método onTick, en mi ejemplo lo he definido a un segundo, esta función a su ves regresa la instancia de CountDownTimer creada para su manipulación desde donde se llame la función o para hacer uso de la segunda función en el ejemplo.&lt;/p&gt;

&lt;p&gt;La función clearTimeOut permite detener la ejecución de nuestro CountDownTimer antes de que este finalice su tarea.&lt;/p&gt;

&lt;p&gt;Un ejemplo practico para estas dos funciones puede ser un buscador con conexión a una RestApi en donde deseamos llamar al endpoint que nos regresa los datos filtrados tras un determinado tiempo que se detecte que el usuario dejo de escribir.&lt;/p&gt;

&lt;p&gt;En dicho caso haríamos el siguiente ejemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    private lateinit var timeOutRef: CountDownTimer

    private fun setUpSearchEditText(){
        binding.searchInput.doOnTextChanged { text, start, before, count -&amp;gt;
            if(this::timeOutRef.isInitialized){
                clearTimeOut(this.timeOutRef)
            }

            timeOutRef = setTimeOut({ Log.d("TimeOut","Enviar al endpoint") }, 1000)
        }
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Espero que este pequeño blog y funciones sean de ayuda para otros programadores como lo fue en mi caso, de la misma manera les dejo el enlace al gist por si desean copiarlo y adaptarlo a sus necesidades.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://gist.github.com/AntonioHReyes/73a3184362dc8d9a65157070695d87cb"&gt;https://gist.github.com/AntonioHReyes/73a3184362dc8d9a65157070695d87cb&lt;/a&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>developer</category>
      <category>how</category>
    </item>
  </channel>
</rss>
