<?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: flutter</title>
    <description>The latest articles tagged 'flutter' on DEV Community.</description>
    <link>https://dev.to/t/flutter</link>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tag/flutter"/>
    <language>en</language>
    <item>
      <title>Building a Car Rental Booking Site: Availability Is the Hard Part</title>
      <dc:creator>Ehsan Abedi</dc:creator>
      <pubDate>Wed, 28 Oct 2026 17:47:16 +0000</pubDate>
      <link>https://dev.to/ehsanabedi_dev/building-a-car-rental-booking-site-availability-is-the-hard-part-1fng</link>
      <guid>https://dev.to/ehsanabedi_dev/building-a-car-rental-booking-site-availability-is-the-hard-part-1fng</guid>
      <description>&lt;p&gt;A car rental website looks simple from the outside: pick dates, pick a car, pay. Under the hood, almost every problem comes back to one question: &lt;strong&gt;is this car actually free for these dates?&lt;/strong&gt; Get that wrong and you get double bookings, angry customers at the airport and a team that stops trusting the website.&lt;/p&gt;

&lt;p&gt;I'm a freelance WordPress and Flutter developer in Famagusta, North Cyprus, where a lot of small rental companies still take bookings over WhatsApp. Here is how I approach a booking site for them, from a developer's point of view.&lt;/p&gt;

&lt;h2&gt;
  
  
  Model the fleet, not just "products"
&lt;/h2&gt;

&lt;p&gt;The tempting shortcut is to treat each car as a WooCommerce product with stock. It breaks quickly, because rental stock is about &lt;strong&gt;time&lt;/strong&gt;, not quantity. I usually model it like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Car group&lt;/strong&gt; (e.g. "Economy automatic"): what the customer books, with photos, seats, luggage and transmission.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vehicle&lt;/strong&gt;: the physical car with its plate, linked to a group, which can be blocked for service.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Booking&lt;/strong&gt;: vehicle, pick-up datetime, return datetime, locations, extras, status.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Customers book a group and the system assigns a free vehicle. That way one car in the workshop doesn't make the whole group "sold out" on the website.&lt;/p&gt;

&lt;h2&gt;
  
  
  The overlap query
&lt;/h2&gt;

&lt;p&gt;Two bookings clash when one starts before the other ends and ends after the other starts. In SQL that is the classic interval check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;wp_rental_bookings&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;vehicle_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;d&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'confirmed'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'pending'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;pickup_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;   &lt;span class="c1"&gt;-- requested return&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;return_at&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;-- requested pick-up&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Small details that matter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store datetimes in UTC and convert for display. Rental days cross midnight, and daylight saving changes will bite you otherwise.&lt;/li&gt;
&lt;li&gt;Add a &lt;strong&gt;buffer&lt;/strong&gt; after each return (cleaning, fuel, delivery time) before the car is free again.&lt;/li&gt;
&lt;li&gt;Count &lt;code&gt;pending&lt;/code&gt; bookings too, so two people paying at the same time can't both get the last car.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prevent the race condition
&lt;/h2&gt;

&lt;p&gt;Checking availability and inserting the booking are two steps. If two requests run between them, both pass the check. Wrap the check and the insert in a transaction and lock the vehicle row (&lt;code&gt;SELECT ... FOR UPDATE&lt;/code&gt; on InnoDB), or hold a short reservation that expires if payment isn't completed in, say, 15 minutes. It's boring code, and it's the code that saves the business.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prices are rules, not numbers
&lt;/h2&gt;

&lt;p&gt;Rental pricing is rarely "price × days". There are seasonal rates, discounts for longer rentals, a minimum number of days, one-way fees and extras like a child seat or full insurance. I keep these as small, testable rules and calculate the total on the server, never only in JavaScript. The customer sees the full breakdown before confirming, which also reduces the "why is it more at the desk?" conversations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Airport delivery and the booking form
&lt;/h2&gt;

&lt;p&gt;Many visitors here land at Ercan Airport or arrive at the port in Kyrenia. Delivery locations, a delivery fee and a flight number field belong in the booking form itself. A booking that arrives with all the details means one message for the staff instead of five.&lt;/p&gt;

&lt;h2&gt;
  
  
  Speed and languages
&lt;/h2&gt;

&lt;p&gt;Most people book on a phone, often on roaming data, so the search results page has to be light: real image sizes, WebP, lazy-loading below the fold, and no heavy slider. On a four-language WordPress site I improved homepage PageSpeed from 51 to 75 on mobile and 81 to 97 on desktop without touching the design, mostly through this kind of asset discipline. For languages I use WPML with real URLs per language and &lt;code&gt;hreflang&lt;/code&gt;, so the German page can rank for German searches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where a Flutter app fits
&lt;/h2&gt;

&lt;p&gt;The website is for customers. The part rental companies love is a small &lt;strong&gt;staff app&lt;/strong&gt;: at pick-up and return the employee takes photos of the car, records fuel and mileage, and the customer signs on the screen. Built in Flutter, it runs on Android and iPhone and talks to the same WordPress database through the REST API, so bookings and check-ins stay in one place.&lt;/p&gt;

&lt;p&gt;I wrote a client-facing version of this, for rental company owners rather than developers, here: &lt;a href="https://ehsanabedi.com/car-rental-website-north-cyprus/" rel="noopener noreferrer"&gt;Car Rental Website Design in North Cyprus&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;How do you handle availability in booking systems: database locks, short reservations, or something else?&lt;/p&gt;

</description>
      <category>wordpress</category>
      <category>webdev</category>
      <category>php</category>
      <category>flutter</category>
    </item>
    <item>
      <title>Özel Yazılım Geliştirme (Web &amp; Mobil Kurumsal Çözümler): Production Architecture &amp; Best Practices 2026 (28.10.2026)</title>
      <dc:creator>Nuh Mehmet Demirkol</dc:creator>
      <pubDate>Wed, 28 Oct 2026 15:00:08 +0000</pubDate>
      <link>https://dev.to/nmdemirkol/ozel-yazilim-gelistirme-web-mobil-kurumsal-cozumler-production-architecture-best-practices-343i</link>
      <guid>https://dev.to/nmdemirkol/ozel-yazilim-gelistirme-web-mobil-kurumsal-cozumler-production-architecture-best-practices-343i</guid>
      <description>&lt;h1&gt;
  
  
  Özel Yazılım Geliştirme (Web &amp;amp; Mobil Kurumsal Çözümler)
&lt;/h1&gt;

&lt;p&gt;Mobil uygulama ekosisteminde ölçeklenebilir ve sürdürülebilir bir sistem inşa etmek için doğru mimari ve mühendislik disiplini şarttır.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Mimari Prensipler ve Doğru Teknoloji
&lt;/h2&gt;

&lt;p&gt;Clean Architecture katmanları ile iş mantığını ve UI katmanını birbirinden ayırmak bakım maliyetlerini %60 oranında azaltır. Flutter ile tek kod tabanından iOS ve Android için 120 FPS akıcılık elde edilir.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Garanti ve Kod Mülkiyeti
&lt;/h2&gt;

&lt;p&gt;Her kurumsal projede %100 açık kaynak kod, Git deposu ve en az 1 yıllık teknik bakım garantisi sağlanmalıdır.&lt;/p&gt;




&lt;h3&gt;
  
  
  🚀 İletişim &amp;amp; Proje Teklifi
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;📌 &lt;strong&gt;İlgili Özel Sayfa:&lt;/strong&gt; &lt;a href="https://nmdemirkol.com/cozumler/hazir-paket-yerine-ozel-yazilim" rel="noopener noreferrer"&gt;Özel Yazılım Geliştirme (Web &amp;amp; Mobil Kurumsal Çözümler)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🧮 &lt;strong&gt;Canlı Maliyet Hesaplama:&lt;/strong&gt; &lt;a href="https://nmdemirkol.com/mobil-uygulama-maliyet-hesaplama" rel="noopener noreferrer"&gt;Mobil Uygulama Maliyet Hesaplama&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;👨‍💻 Bilgisayar Mühendisi: Nuh Mehmet Demirkol&lt;/li&gt;
&lt;li&gt;📞 WhatsApp: &lt;a href="https://wa.me/905078236811" rel="noopener noreferrer"&gt;+90 507 823 68 11&lt;/a&gt; | Ankara, Türkiye&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>mobile</category>
      <category>architecture</category>
      <category>ios</category>
    </item>
    <item>
      <title>Building DevFeed — Part 4: Shipping v1.0.1 with GitHub Actions and a Changelog</title>
      <dc:creator>Yadnesh Teli</dc:creator>
      <pubDate>Wed, 28 Oct 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/yadneshteli/building-devfeed-part-4-shipping-v101-with-github-actions-and-a-changelog-1kno</link>
      <guid>https://dev.to/yadneshteli/building-devfeed-part-4-shipping-v101-with-github-actions-and-a-changelog-1kno</guid>
      <description>&lt;p&gt;&lt;em&gt;Series 2 of 2 — Part 4 of 4&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Building DevFeed — Part 4: Shipping v1.0.1 with GitHub Actions and a Changelog
&lt;/h1&gt;

&lt;p&gt;&lt;em&gt;Series: Building DevFeed — A Proper Flutter MVVM App from Day One&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;DevFeed started as a practice project. At some point during development it stopped feeling like practice and started feeling like a real app. That shift happened when I set up the GitHub Actions release workflow and wrote the first changelog entry.&lt;/p&gt;

&lt;p&gt;Treating a learning project like a real product — versioning it, writing release notes, automating the build — changes how you think about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why bother with releases for a practice project
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The low-stakes answer&lt;/strong&gt;: Setting up a release workflow on a practice project is good practice for doing it on a real project. You make mistakes when there are no consequences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The portfolio answer&lt;/strong&gt;: A versioned changelog shows you think about the full software development lifecycle, not just writing features. It is a small signal that reads clearly to anyone reviewing your work.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The GitHub Actions workflow
&lt;/h2&gt;

&lt;p&gt;I set up two workflows: one for changelog generation on push to main, and one for the release build when a version tag is pushed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Release&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;v*'&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;subosito/flutter-action@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;flutter-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3.x'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flutter pub get&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;flutter build apk --release&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/upload-artifact@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;release-apk&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build/app/outputs/flutter-apk/app-release.apk&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Keeping action versions current
&lt;/h2&gt;

&lt;p&gt;My first version used older action versions — &lt;code&gt;actions/checkout@v2&lt;/code&gt; and &lt;code&gt;actions/upload-artifact@v3&lt;/code&gt;. GitHub started showing deprecation warnings. I updated all of them to &lt;code&gt;v4&lt;/code&gt; and added an environment variable for the Flutter version to make future updates a one-line change.&lt;/p&gt;

&lt;p&gt;This is a small maintenance detail but it matters: deprecated action versions will eventually break your builds. Keeping CI/CD dependencies current is the same discipline as keeping &lt;code&gt;pubspec.yaml&lt;/code&gt; current.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the v1.0.1 changelog
&lt;/h2&gt;

&lt;p&gt;The changelog covers the features from the initial commits: in-app WebView for article reading, sharing actions, the INTERNET permission fix, and the &lt;code&gt;ConsumerStatefulWidget&lt;/code&gt; refactor.&lt;/p&gt;

&lt;p&gt;Writing a changelog for a practice project taught me the value of conventional commits. When every commit starts with &lt;code&gt;feat:&lt;/code&gt;, &lt;code&gt;fix:&lt;/code&gt;, &lt;code&gt;refactor:&lt;/code&gt;, or &lt;code&gt;chore:&lt;/code&gt;, generating a changelog is easy. DevFeed's commit history followed conventional commits from the beginning — every commit is changelog-ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  What done looks like for a learning project
&lt;/h2&gt;

&lt;p&gt;DevFeed does not have user accounts, saved reading lists, or offline support. But it has a working feature set, a clean architecture, a release workflow, and versioned changelogs. It is done in the sense that matters for a learning project — polished enough to show, documented enough to explain, and structured enough to extend.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up Series 2
&lt;/h2&gt;

&lt;p&gt;Over four posts, DevFeed went from a full MVVM scaffold on the first commit to a shipped app at v1.0.1. No big refactors, because the architecture was right from the start. Tagging a new version automatically builds a release APK.&lt;/p&gt;

&lt;p&gt;The contrast with the shopping app series is the whole point. Series 1 shows you what learning the hard way looks like. Series 2 shows you what applying those lessons looks like. You need both. You need to build the messy version before the clean version makes sense.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thanks for following along.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>github</category>
      <category>devops</category>
      <category>cicd</category>
    </item>
    <item>
      <title>I shipped my app to 178 countries in 10 languages. One person outside Korea installed it.</title>
      <dc:creator>BT_Sun</dc:creator>
      <pubDate>Wed, 28 Oct 2026 11:29:51 +0000</pubDate>
      <link>https://dev.to/_21fd064819ff1707b8060/i-shipped-my-app-to-178-countries-in-10-languages-one-person-outside-korea-installed-it-270m</link>
      <guid>https://dev.to/_21fd064819ff1707b8060/i-shipped-my-app-to-178-countries-in-10-languages-one-person-outside-korea-installed-it-270m</guid>
      <description>&lt;p&gt;I'm a solo developer from South Korea. I started coding about seven months ago. Writing in English is still difficult for me, so I use a translator and AI to help.&lt;/p&gt;

&lt;p&gt;My app, Party Picker, has 21 party games. Friends use one phone to pick who buys coffee and things like that.&lt;/p&gt;

&lt;p&gt;In my last post, I wrote that nobody outside Korea had installed it. Since then, one person has.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I tried outside Korea
&lt;/h2&gt;

&lt;p&gt;The app is available in 178 countries and regions on Google Play. It supports 10 languages, and the store page is in 10 languages too. I used AI for the translations.&lt;/p&gt;

&lt;p&gt;I posted on Reddit and made English YouTube Shorts. I also translated the store pages and ran a Google Play promotion through promotional content in 177 countries.&lt;/p&gt;

&lt;p&gt;None of these made a noticeable difference so far.&lt;/p&gt;

&lt;h2&gt;
  
  
  The numbers in Play Console
&lt;/h2&gt;

&lt;p&gt;I checked Google Play Console on October 28. For the last 28 days, September 30 to October 27, it showed:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;About 2,080 store impressions&lt;/li&gt;
&lt;li&gt;8 device acquisitions&lt;/li&gt;
&lt;li&gt;7 first opens&lt;/li&gt;
&lt;li&gt;7 monthly active devices&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The store listing report for the last 28 days showed 55 visitors. There were 11 install button clicks, or 20%.&lt;/p&gt;

&lt;p&gt;The promotion ran from October 19 to 25 in 177 countries. Seven people saw it. Clicks and conversions still show "no data".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb7bdtdtq4jz3qva45cwu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fb7bdtdtq4jz3qva45cwu.png" alt="The funnel from 178 countries to 8 installs" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These numbers measure different things. But when I look at them, it feels like I got about 7 or 8 installs in the end. The app is available in many countries, but very few people are finding it.&lt;/p&gt;

&lt;h2&gt;
  
  
  One user in the Dominican Republic
&lt;/h2&gt;

&lt;p&gt;The country report showed 6 installed users as of October 26: 5 in South Korea and 1 in the Dominican Republic.&lt;/p&gt;

&lt;p&gt;I don't know who that person is. I don't know how they found the app either.&lt;/p&gt;

&lt;p&gt;There was also 1 device acquisition from Ethiopia on October 12. But that metric counts both new and returning devices. I can't say it was a second person outside Korea, so I'm not adding it to the user count.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fap2qq3lmoafr6h2si04x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fap2qq3lmoafr6h2si04x.png" alt="178 countries, and where the installs actually are" width="800" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Do I need to do more on social media?
&lt;/h2&gt;

&lt;p&gt;I keep wondering if Instagram, Threads, or TikTok needs to be the main way I promote this app to people outside Korea.&lt;/p&gt;

&lt;p&gt;This app isn't my only work. I write this blog too, and I'm making three other games. One of them is getting ready for a Steam listing. So I have a lot to think about before adding social media to that work.&lt;/p&gt;

&lt;p&gt;The Play promotion didn't bring in many people either. I don't know whether spending more time on social media would help, or how much time I should give it.&lt;/p&gt;

&lt;p&gt;I don't have an answer yet.&lt;/p&gt;

&lt;h2&gt;
  
  
  I'll keep trying outside Korea too
&lt;/h2&gt;

&lt;p&gt;I don't plan to wait until the app does well in Korea before working on other countries. I want to keep trying both.&lt;/p&gt;

&lt;p&gt;AI has made the language barrier lower for me. The translations aren't perfect, but I can do more than I could on my own.&lt;/p&gt;

&lt;p&gt;Comments on dev.to have helped too. A native Chinese speaker gave me suggestions, and I changed 32 Traditional Chinese strings. I also added a tip about right-to-left languages to my notes.&lt;/p&gt;

&lt;p&gt;I can use that help to improve the app. That's part of why I want to keep trying, even with these small numbers.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkpldlvxifzju5ogd0bqv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkpldlvxifzju5ogd0bqv.png" alt="What I tried, and what I'll keep doing" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you released a small app globally, did social media actually bring you users? If you did it alone, what worked for you?&lt;/p&gt;

&lt;p&gt;You can find Party Picker on &lt;a href="https://play.google.com/store/apps/details?id=kr.binarytech.party_picker&amp;amp;utm_source=devto&amp;amp;utm_campaign=blog_202610" rel="noopener noreferrer"&gt;Google Play&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>startup</category>
      <category>marketing</category>
      <category>flutter</category>
    </item>
    <item>
      <title>Real-Time IoT Apps: What a Flutter Hydration Case Study Reveals About Development Partners</title>
      <dc:creator>Manny Frank</dc:creator>
      <pubDate>Wed, 28 Oct 2026 10:58:19 +0000</pubDate>
      <link>https://dev.to/mannyfrank_07/real-time-iot-apps-what-a-flutter-hydration-case-study-reveals-about-development-partners-5cjn</link>
      <guid>https://dev.to/mannyfrank_07/real-time-iot-apps-what-a-flutter-hydration-case-study-reveals-about-development-partners-5cjn</guid>
      <description>&lt;p&gt;Choosing an IoT development partner involves more than checking whether a team can connect devices. The application also needs to make incoming data understandable.&lt;/p&gt;

&lt;p&gt;GeekyAnts’ Curovia case study offers a practical example: a Flutter hydration-monitoring app for football teams.&lt;/p&gt;

&lt;p&gt;According to the case study, the application includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;IoT integration for real-time data capture and analysis.&lt;/li&gt;
&lt;li&gt;Team-level and individual-player hydration views.&lt;/li&gt;
&lt;li&gt;Graphs, gauges, filters, and CSV export.&lt;/li&gt;
&lt;li&gt;Screens implemented from Figma designs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The main engineering challenge was visualization. The team reported that available Flutter components did not provide the customization needed for the project, so developers built bespoke widgets and modified existing pub packages.&lt;/p&gt;

&lt;p&gt;That is a project-specific limitation, rather than evidence that Flutter lacks capable visualization tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Companies to evaluate for connected-product development&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;An unranked shortlist could include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GeekyAnts:&lt;/strong&gt; The Curovia example provides evidence of Flutter implementation, device-data integration, and custom visualization.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IBM Consulting:&lt;/strong&gt; Its published IoT offering addresses connected-asset strategies and integration across IoT, AI, and asset management.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TCS:&lt;/strong&gt; Its IoT engineering services cover designing and deploying connected solutions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are different engagement scopes, so company size or brand recognition alone will not establish suitability.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Questions worth asking before choosing a team&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Can the proposed team demonstrate how its interface handles stale readings, disconnected devices, and missing data? Can users distinguish an individual reading from an aggregate? What happens when a visualization package needs modification?&lt;/p&gt;

&lt;p&gt;Those are evaluation questions, not capabilities confirmed by the Curovia transcript.&lt;/p&gt;

&lt;p&gt;The case study describes development as ongoing and does not specify the device protocol, refresh latency, or measured health outcomes.&lt;/p&gt;

&lt;p&gt;The video explains the implementation and visualization work:&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=FBWaoS4QXsw&amp;amp;utm_source=dis2026" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=FBWaoS4QXsw&amp;amp;utm_source=dis2026&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For developers building connected applications, where has the greater challenge been: device integration, data handling, or visualization?&lt;/p&gt;

</description>
      <category>forum</category>
      <category>mobile</category>
      <category>flutter</category>
      <category>iot</category>
    </item>
    <item>
      <title>Why your Flutter JSON model crashes at runtime (and how to generate one that doesn't)</title>
      <dc:creator>Chanpenh Yorn</dc:creator>
      <pubDate>Wed, 28 Oct 2026 09:37:50 +0000</pubDate>
      <link>https://dev.to/chanpenh/why-your-flutter-json-model-crashes-at-runtime-and-how-to-generate-one-that-doesnt-hf6</link>
      <guid>https://dev.to/chanpenh/why-your-flutter-json-model-crashes-at-runtime-and-how-to-generate-one-that-doesnt-hf6</guid>
      <description>&lt;p&gt;You write a model, the first API response parses fine, and a week later a crash report says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type 'int' is not a subtype of type 'double' in type cast
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing in your code changed. The data did. Here are the three type errors I see most often with &lt;code&gt;jsonDecode&lt;/code&gt;, why they happen, and how to write (or generate) models that survive real responses.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. &lt;code&gt;int&lt;/code&gt; vs &lt;code&gt;double&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Dart's &lt;code&gt;jsonDecode&lt;/code&gt; reads the number type from the text. &lt;code&gt;1.0&lt;/code&gt; is a &lt;code&gt;double&lt;/code&gt;, &lt;code&gt;1&lt;/code&gt; is an &lt;code&gt;int&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'dart:convert'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonDecode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'{"price": 1.0}'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
  &lt;span class="n"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'price'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;runtimeType&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// double&lt;/span&gt;

  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonDecode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'{"price": 1}'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'price'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// type 'int' is not a subtype of type 'double' in type cast&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Many backends drop the &lt;code&gt;.0&lt;/code&gt; for whole numbers, so a price of &lt;code&gt;3.0&lt;/code&gt; arrives as &lt;code&gt;3&lt;/code&gt;. The safe cast for any field that is "a number with decimals" is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'price'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toDouble&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. &lt;code&gt;List&amp;lt;dynamic&amp;gt;&lt;/code&gt; vs &lt;code&gt;List&amp;lt;String&amp;gt;&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;jsonDecode&lt;/code&gt; has no idea what your list should contain, so every JSON array becomes &lt;code&gt;List&amp;lt;dynamic&amp;gt;&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;jsonDecode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'{"tags": ["a", "b"]}'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'tags'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// type 'List&amp;lt;dynamic&amp;gt;' is not a subtype of type 'List&amp;lt;String&amp;gt;' in type cast&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cast each item instead of the list:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'tags'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lists of objects work the same way, with &lt;code&gt;fromJson&lt;/code&gt; on each item:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'items'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;))&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. The key that is only sometimes there
&lt;/h3&gt;

&lt;p&gt;You build the model from one sample response. In that sample every item has a &lt;code&gt;note&lt;/code&gt;. In production some items don't, and &lt;code&gt;json['note'] as String&lt;/code&gt; throws because the value is &lt;code&gt;null&lt;/code&gt;. Any key that can be missing or &lt;code&gt;null&lt;/code&gt; needs a nullable type, &lt;code&gt;String?&lt;/code&gt;, and a non-&lt;code&gt;required&lt;/code&gt; constructor parameter.&lt;/p&gt;

&lt;p&gt;The fix is to look at more than one sample. If a key is missing from any item, or &lt;code&gt;null&lt;/code&gt; in any item, it's nullable.&lt;/p&gt;

&lt;h3&gt;
  
  
  A model that handles all three
&lt;/h3&gt;

&lt;p&gt;Given this sample, where the second item has a &lt;code&gt;note&lt;/code&gt; and the first doesn't:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"tags"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"new"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"items"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"A1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sku"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"B2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"qty"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"note"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gift"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;a model that survives real responses looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Order&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;double&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;factory&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;price:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'price'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toDouble&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nl"&gt;tags:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'tags'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="nl"&gt;items:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'items'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;))&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toJson&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;'id'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;'price'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;price&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;'tags'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;'items'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;items&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toJson&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toList&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="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Item&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;note&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="kd"&gt;required&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;note&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="kd"&gt;factory&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromJson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Item&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nl"&gt;sku:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'sku'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;qty:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'qty'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nl"&gt;note:&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'note'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kt"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;dynamic&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;toJson&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s"&gt;'sku'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sku&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;'qty'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;qty&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;'note'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;note&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Decoding &lt;code&gt;{"id": 8, "price": 3, "tags": [], "items": [{"sku": "C3", "qty": 1}]}&lt;/code&gt; with it gives &lt;code&gt;price&lt;/code&gt; = &lt;code&gt;3.0&lt;/code&gt; and &lt;code&gt;note&lt;/code&gt; = &lt;code&gt;null&lt;/code&gt;, with no exceptions.&lt;/p&gt;

&lt;h3&gt;
  
  
  Generating it
&lt;/h3&gt;

&lt;p&gt;I wrote that model by hand far too many times, so I built a free &lt;a href="https://tool.yornchanpenh.com/tools/json-to-dart/" rel="noopener noreferrer"&gt;JSON to Dart generator&lt;/a&gt; (disclosure: it's my project). The model above is its actual output for the sample. It:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;runs entirely in the browser, so API responses are never uploaded,&lt;/li&gt;
&lt;li&gt;reads number types from the raw text, so &lt;code&gt;1.0&lt;/code&gt; stays a &lt;code&gt;double&lt;/code&gt; (JavaScript's &lt;code&gt;JSON.parse&lt;/code&gt; would turn it into &lt;code&gt;1&lt;/code&gt;),&lt;/li&gt;
&lt;li&gt;merges the keys of every item in an array and makes keys that are missing or &lt;code&gt;null&lt;/code&gt; in some items nullable,&lt;/li&gt;
&lt;li&gt;outputs plain Dart (no packages), json_serializable or freezed,&lt;/li&gt;
&lt;li&gt;can repair a map printed by &lt;code&gt;print()&lt;/code&gt; / &lt;code&gt;debugPrint&lt;/code&gt; (like &lt;code&gt;{id: 1, name: Ada}&lt;/code&gt;) into JSON first.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It does not guess &lt;code&gt;DateTime&lt;/code&gt; or enums; dates stay &lt;code&gt;String&lt;/code&gt;, so convert those by hand.&lt;/p&gt;

&lt;p&gt;If you're debugging one of these errors right now, the guide &lt;a href="https://tool.yornchanpenh.com/guides/flutter-json-type-errors/" rel="noopener noreferrer"&gt;Flutter JSON Errors: "is not a subtype" Explained&lt;/a&gt; covers more of them, including &lt;code&gt;'_Map&amp;lt;String, dynamic&amp;gt;' is not a subtype of type ...&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>json</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Özel Yazılım Yaptırmak İstiyorum (Şirketlere Özel Otomasyon): Production Architecture &amp; Best Practices 2026</title>
      <dc:creator>Nuh Mehmet Demirkol</dc:creator>
      <pubDate>Wed, 28 Oct 2026 08:00:07 +0000</pubDate>
      <link>https://dev.to/nmdemirkol/ozel-yazilim-yaptirmak-istiyorum-sirketlere-ozel-otomasyon-production-architecture-best-2h24</link>
      <guid>https://dev.to/nmdemirkol/ozel-yazilim-yaptirmak-istiyorum-sirketlere-ozel-otomasyon-production-architecture-best-2h24</guid>
      <description>&lt;h1&gt;
  
  
  Özel Yazılım Yaptırmak İstiyorum (Şirketlere Özel Otomasyon)
&lt;/h1&gt;

&lt;p&gt;Mobil uygulama ekosisteminde ölçeklenebilir ve sürdürülebilir bir sistem inşa etmek için doğru mimari ve mühendislik disiplini şarttır.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Mimari Prensipler ve Doğru Teknoloji
&lt;/h2&gt;

&lt;p&gt;Clean Architecture katmanları ile iş mantığını ve UI katmanını birbirinden ayırmak bakım maliyetlerini %60 oranında azaltır. Flutter ile tek kod tabanından iOS ve Android için 120 FPS akıcılık elde edilir.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Garanti ve Kod Mülkiyeti
&lt;/h2&gt;

&lt;p&gt;Her kurumsal projede %100 açık kaynak kod, Git deposu ve en az 1 yıllık teknik bakım garantisi sağlanmalıdır.&lt;/p&gt;




&lt;h3&gt;
  
  
  🚀 İletişim &amp;amp; Proje Teklifi
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;📌 &lt;strong&gt;İlgili Özel Sayfa:&lt;/strong&gt; &lt;a href="https://nmdemirkol.com/cozumler/kurumsal-paket-mobil-yazilim" rel="noopener noreferrer"&gt;Özel Yazılım Yaptırmak İstiyorum (Şirketlere Özel Otomasyon)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🧮 &lt;strong&gt;Canlı Maliyet Hesaplama:&lt;/strong&gt; &lt;a href="https://nmdemirkol.com/mobil-uygulama-maliyet-hesaplama" rel="noopener noreferrer"&gt;Mobil Uygulama Maliyet Hesaplama&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;👨‍💻 Bilgisayar Mühendisi: Nuh Mehmet Demirkol&lt;/li&gt;
&lt;li&gt;📞 WhatsApp: &lt;a href="https://wa.me/905078236811" rel="noopener noreferrer"&gt;+90 507 823 68 11&lt;/a&gt; | Ankara, Türkiye&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>mobile</category>
      <category>architecture</category>
      <category>ios</category>
    </item>
    <item>
      <title>Shipping a Marketplace App to iOS and Android From One Codebase: An Engineering Checklist</title>
      <dc:creator>Journeyhorizon</dc:creator>
      <pubDate>Wed, 28 Oct 2026 07:06:37 +0000</pubDate>
      <link>https://dev.to/journeyhorizon/shipping-a-marketplace-app-to-ios-and-android-from-one-codebase-an-engineering-checklist-4do7</link>
      <guid>https://dev.to/journeyhorizon/shipping-a-marketplace-app-to-ios-and-android-from-one-codebase-an-engineering-checklist-4do7</guid>
      <description>&lt;p&gt;Most "native vs cross-platform" threads turn into framework fights. For marketplace apps, the framework is the least interesting part of the decision. The questions that actually move your timeline and budget are these: how many platforms you must hit on day one, how far your UI drifts from stock components, which integrations you can't live without, and how users will find the app at all.&lt;/p&gt;

&lt;p&gt;This post turns those questions into a checklist you can run before &lt;code&gt;flutter create&lt;/code&gt; or &lt;code&gt;npx create-expo-app&lt;/code&gt;. It's based on a longer guide on &lt;a href="https://www.journeyh.io/blog/multi-platform-mobile-application-development" rel="noopener noreferrer"&gt;multi-platform mobile app development for marketplaces&lt;/a&gt; from the team at &lt;a href="https://www.journeyh.io/" rel="noopener noreferrer"&gt;Journeyhorizon&lt;/a&gt;, a marketplace development agency.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why "both platforms on day one" is the default for marketplaces
&lt;/h2&gt;

&lt;p&gt;A marketplace has two sides, and you rarely control which phone either side carries. Android holds roughly 70% of global market share, yet iOS matches or leads in many developed markets and among high-value users. If your sellers mostly carry Android and your buyers mostly carry iPhones (or the other way round), shipping one platform first means half of your market walks into an empty room.&lt;/p&gt;

&lt;p&gt;That's why the "launch on one, learn, then port" plan rarely survives contact with a real marketplace roadmap. The economics point the same way: building two separate native apps can cost 30–40% more than a single cross-platform build, and a shared codebase lets one team ship to both stores instead of coordinating two teams or two sequential projects. Time-to-market matters too, because a competitor that lands on both stores first gets to the users first.&lt;/p&gt;

&lt;p&gt;Before going further, write down the answers to this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ ] Which side of the marketplace is on which platform?
[ ] Can we launch with only one side served? (usually: no)
[ ] Does a competitor already cover both stores?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the answers push you toward both platforms, the next question is how much code you can realistically share.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reusability vs customisation: the real trade-off
&lt;/h2&gt;

&lt;p&gt;Frameworks advertise big numbers. Flutter and React Native typically claim 70–90% code sharing. Kotlin Multiplatform takes a different route: it shares business logic but keeps a native UI on each side. Ionic is the comfortable option for teams coming from the web.&lt;/p&gt;

&lt;p&gt;The catch is that the sharing percentage is not really a property of the framework. It's a property of your product. Every time you ask for a screen that feels distinctly iOS on an iPhone and distinctly Android on a Pixel, or for heavy custom animation, the shared portion shrinks.&lt;/p&gt;

&lt;p&gt;A rough way to place your app:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Product type&lt;/th&gt;
&lt;th&gt;Customisation need&lt;/th&gt;
&lt;th&gt;What to expect&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Booking, local services, resale, SaaS-style tools&lt;/td&gt;
&lt;td&gt;Low to moderate&lt;/td&gt;
&lt;td&gt;High code sharing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fashion, design and other highly visual catalogues&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;td&gt;Lower sharing; plan custom work&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AR product previews or deep device features&lt;/td&gt;
&lt;td&gt;Very high&lt;/td&gt;
&lt;td&gt;Plan platform-specific modules&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The architecture that ages well is a foundation that ships fast and accepts extensions: custom plugins and platform-specific features layered on top without forcing a full rewrite. Sharetribe is one example of that pattern on the marketplace side, giving you a proven core you extend rather than replace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Audit your integrations before you pick a framework
&lt;/h2&gt;

&lt;p&gt;Marketplace apps are integration-heavy. A typical list looks like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;payments and payouts&lt;/li&gt;
&lt;li&gt;shipping labels&lt;/li&gt;
&lt;li&gt;push notifications&lt;/li&gt;
&lt;li&gt;analytics&lt;/li&gt;
&lt;li&gt;fraud detection&lt;/li&gt;
&lt;li&gt;CRM sync&lt;/li&gt;
&lt;li&gt;industry-specific services for your vertical&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ecosystem depth varies by framework. React Native has the largest library ecosystem. Flutter's is growing but smaller. Kotlin Multiplatform's plugin architecture is still maturing. None of this disqualifies a framework, but it changes your estimate.&lt;/p&gt;

&lt;p&gt;A simple exercise: make a table with each required integration, then mark it "official SDK", "maintained community package" or "we build it". Anything in the third column is scope, and scope belongs in the plan, not in a surprise sprint three weeks before launch.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;integration        | flutter   | react-native | decision
-------------------|-----------|--------------|---------
payments           | ?         | ?            |
push               | ?         | ?            |
fraud detection    | ?         | ?            |
&amp;lt;niche vertical&amp;gt;   | ?         | ?            |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fill the question marks with what you actually verify, not what you remember from an old blog post. If you're building on Sharetribe, much of this foundation is already in place and custom integrations can be commissioned on top. A standalone app means more hands-on integration work, or a partner who has done it before.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discoverability is part of the architecture
&lt;/h2&gt;

&lt;p&gt;This is the part engineers tend to skip. A well-built app nobody finds is still a failed launch. App store optimisation, user acquisition, SEO, content and brand authority matter as much as frame rates.&lt;/p&gt;

&lt;p&gt;Growth strategy should influence technical choices from the start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SEO-driven acquisition&lt;/strong&gt; means you need crawlable web surfaces: structured data and category pages that can rank, not only a mobile binary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Word-of-mouth growth&lt;/strong&gt; shifts weight toward in-app referral mechanics, deep links and share flows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Treat these as requirements in sprint one, not marketing tasks for month six. Organic growth that is planned early is what makes scaling sustainable instead of expensive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where AI and custom features fit
&lt;/h2&gt;

&lt;p&gt;Shipping a decent cross-platform app is now the baseline. Differentiation is moving to AI-driven personalisation, recommendations, fraud detection, intelligent search, automation and custom workflows.&lt;/p&gt;

&lt;p&gt;Cross-platform frameworks handle calls to AI APIs, such as a recommendation engine or an image recognition service, without much friction. Things get harder when you want custom AI experiences or models trained on your own product data; those usually need specialised development or platform-specific optimisation. Budget for that separately from "build the app".&lt;/p&gt;

&lt;h2&gt;
  
  
  A decision matrix you can paste into your ADR
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IF  first mobile product AND need iOS + Android
    -&amp;gt; proven framework (Flutter or React Native)
IF  already running on Sharetribe
    -&amp;gt; start from mobile templates built for multi-platform
IF  platform-specific UX OR advanced integrations required
    -&amp;gt; budget custom work on top of the framework
IF  growth/discoverability is critical
    -&amp;gt; partner that covers both app development and digital growth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The goal isn't the trendiest stack. It's one that launches quickly, reaches both platforms and scales without a rewrite.&lt;/p&gt;

&lt;h2&gt;
  
  
  Timeline sanity check
&lt;/h2&gt;

&lt;p&gt;Multi-platform development is typically 20–40% faster than separate native builds. Ballpark ranges from the source guide:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;simple marketplace MVP with a solid team: &lt;strong&gt;8–12 weeks&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;full-featured platform with custom integrations: &lt;strong&gt;16–24 weeks&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If an estimate lands far below these, ask what was left out of scope.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick FAQ for the team channel
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Is multi-platform right for every marketplace?&lt;/strong&gt; Most benefit, especially early on. Listings, messaging, ratings and payments work well cross-platform. Highly visual products, or those needing advanced native features, need custom development on top.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multi-platform vs cross-platform: same thing?&lt;/strong&gt; They're often used interchangeably. Strictly, multi-platform spans iOS, Android, web and desktop, while cross-platform usually means iOS plus Android. Modern frameworks can handle both.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Can we plug in third-party services?&lt;/strong&gt; Payment gateways, analytics and push notifications are standard. Niche integrations may need custom development.&lt;/p&gt;

&lt;h2&gt;
  
  
  Takeaway
&lt;/h2&gt;

&lt;p&gt;Run the checklist before the framework debate: platforms per marketplace side, customisation level, integration audit, growth channel and AI ambitions. Once those are written down, the framework usually picks itself.&lt;/p&gt;

&lt;p&gt;If you'd rather work with a team that has shipped this before, Journeyhorizon offers &lt;a href="https://www.journeyh.io/services/flutter-mobile-app-development" rel="noopener noreferrer"&gt;Flutter mobile app development for marketplaces&lt;/a&gt; that pairs the build with a growth plan.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>reactnative</category>
      <category>mobile</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Flutter Push Notifications Not Received in Background or When App Is Killed: Fixes</title>
      <dc:creator>Notify sphere</dc:creator>
      <pubDate>Wed, 28 Oct 2026 06:37:22 +0000</pubDate>
      <link>https://dev.to/notifysphere/flutter-push-notifications-not-received-in-background-or-when-app-is-killed-fixes-j3a</link>
      <guid>https://dev.to/notifysphere/flutter-push-notifications-not-received-in-background-or-when-app-is-killed-fixes-j3a</guid>
      <description>&lt;p&gt;Your Flutter app gets push notifications while it's open, but nothing shows up once it's in the background or closed. This is one of the most common Firebase Cloud Messaging problems, and it almost always comes down to a handful of causes. Here they are, from most to least likely.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  The background handler is a &lt;strong&gt;top-level function&lt;/strong&gt; with &lt;code&gt;@pragma('vm:entry-point')&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  &lt;code&gt;FirebaseMessaging.onBackgroundMessage()&lt;/code&gt; is registered &lt;strong&gt;before&lt;/strong&gt; &lt;code&gt;runApp()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;  The message has a &lt;strong&gt;notification&lt;/strong&gt; block, or is data-only with &lt;strong&gt;high priority&lt;/strong&gt; (Android) and &lt;code&gt;content-available&lt;/code&gt; (iOS).&lt;/li&gt;
&lt;li&gt;  Notification permission is granted (Android 13+ and iOS both ask).&lt;/li&gt;
&lt;li&gt;  On iOS: &lt;strong&gt;Push Notifications&lt;/strong&gt; and &lt;strong&gt;Background Modes → Remote notifications&lt;/strong&gt; are enabled, and the APNs key is uploaded to Firebase.&lt;/li&gt;
&lt;li&gt;  You're testing on a real device in a &lt;strong&gt;release or profile build&lt;/strong&gt;, and the app wasn't force-stopped.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  1. Set up the background handler correctly
&lt;/h2&gt;

&lt;p&gt;When your app isn't in the foreground, &lt;code&gt;firebase_messaging&lt;/code&gt; runs your handler in a separate Dart isolate. That only works if Flutter can find the function by itself, so it has to be a top-level function (not inside a class) and marked as an entry point. Otherwise tree shaking in release builds can remove it, and messages silently do nothing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:firebase_core/firebase_core.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:firebase_messaging/firebase_messaging.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/material.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// Must be a top-level function, not a class method or closure&lt;/span&gt;
&lt;span class="nd"&gt;@pragma&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'vm:entry-point'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;firebaseMessagingBackgroundHandler&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;RemoteMessage&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Runs in its own isolate, so initialise Firebase again here&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Firebase&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="n"&gt;debugPrint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Background message: &lt;/span&gt;&lt;span class="si"&gt;${message.messageId}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;WidgetsFlutterBinding&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ensureInitialized&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;Firebase&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;initializeApp&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Register before runApp()&lt;/span&gt;
  &lt;span class="n"&gt;FirebaseMessaging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;onBackgroundMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;firebaseMessagingBackgroundHandler&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="n"&gt;runApp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the handler runs in its own isolate, it can't touch your widgets, providers or in-memory state. If you use other Firebase services inside it, call &lt;code&gt;Firebase.initializeApp()&lt;/code&gt; there too.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Send the right kind of message
&lt;/h2&gt;

&lt;p&gt;FCM has two kinds of messages, and they behave very differently in the background:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Notification messages&lt;/strong&gt; (with a &lt;code&gt;notification&lt;/code&gt; block) are displayed by the operating system itself when your app is in the background or closed. Your Dart code doesn't have to run for the user to see them. This is what you want for anything user-facing.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Data-only messages&lt;/strong&gt; (just a &lt;code&gt;data&lt;/code&gt; block) display nothing on their own. They wake your background handler, and only if the platform agrees to deliver them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a notification the user must see, send something like this through the FCM HTTP v1 API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DEVICE_FCM_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"notification"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Your order is out for delivery"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"body"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Arriving in about 20 minutes"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"orderId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4821"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"android"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"notification"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"channel_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high_importance"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"apns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"apns-priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"10"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"payload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"aps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sound"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"default"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you really need a silent data-only message, mark it high priority on Android and as a background push on iOS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DEVICE_FCM_TOKEN"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sync"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"orderId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4821"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"android"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"apns"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"apns-push-type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"apns-priority"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"5"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"payload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"aps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"content-available"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can build and check both payloads with our free &lt;a href="https://notifysphere.net/tools/fcm-payload-builder" rel="noopener noreferrer"&gt;FCM payload builder&lt;/a&gt;, which also gives you the cURL command.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Ask for notification permission
&lt;/h2&gt;

&lt;p&gt;Since Android 13, apps need the &lt;code&gt;POST_NOTIFICATIONS&lt;/code&gt; runtime permission, and iOS has always needed one. Without it, messages are delivered to the device but never shown. Ask once, at a sensible moment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;settings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;FirebaseMessaging&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;instance&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;requestPermission&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;alert:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;badge:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;sound:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;debugPrint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;'Permission: &lt;/span&gt;&lt;span class="si"&gt;${settings.authorizationStatus}&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the status comes back as &lt;code&gt;denied&lt;/code&gt;, the system won't show the dialog again, so point the user to the app's notification settings instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Android: battery savers and force stop
&lt;/h2&gt;

&lt;p&gt;If notifications arrive on a Pixel but not on some Xiaomi, Oppo, Vivo, Realme, OnePlus or Samsung phones, the phone's battery optimisation is likely stopping your app in the background. Things to check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Force stop&lt;/strong&gt; from Settings blocks every FCM message until the user opens the app again. A normal swipe from recents shouldn't, but some brands treat it like a force stop.&lt;/li&gt;
&lt;li&gt;  Use &lt;strong&gt;high priority&lt;/strong&gt; for time-sensitive messages. Normal priority can be delayed while the phone is in Doze mode.&lt;/li&gt;
&lt;li&gt;  Create a &lt;strong&gt;notification channel with high importance&lt;/strong&gt; and send its &lt;code&gt;channel_id&lt;/code&gt;, or the notification may arrive silently without a heads-up banner.&lt;/li&gt;
&lt;li&gt;  For testing, set the app's battery usage to &lt;strong&gt;Unrestricted&lt;/strong&gt; and turn on Autostart where the phone has it. You can't do this for your users, but it confirms the cause.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  5. iOS: capabilities, APNs and how the app was closed
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  In Xcode, under Signing &amp;amp; Capabilities, add &lt;strong&gt;Push Notifications&lt;/strong&gt; and &lt;strong&gt;Background Modes&lt;/strong&gt; with &lt;em&gt;Remote notifications&lt;/em&gt; ticked.&lt;/li&gt;
&lt;li&gt;  Upload your APNs authentication key in the Firebase console (Project settings → Cloud Messaging). Without it, nothing reaches iOS at all. The &lt;a href="https://notifysphere.net/blog/firebase-ios-push-notifications" rel="noopener noreferrer"&gt;Firebase iOS push guide&lt;/a&gt; walks through it.&lt;/li&gt;
&lt;li&gt;  Background data messages are throttled by iOS and aren't delivered at all if the user swiped the app away. Notification messages are still shown in that case.&lt;/li&gt;
&lt;li&gt;  If you see &lt;code&gt;apns-token-not-set&lt;/code&gt;, the device never got an APNs token. See &lt;a href="https://notifysphere.net/blog/no-apns-token-specified-before-fetching-fcm-token" rel="noopener noreferrer"&gt;how to fix the APNs token error&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  6. Test the right way
&lt;/h2&gt;

&lt;p&gt;Debug builds behave differently from what your users run. When you stop the app from your IDE, the process is killed in a way that real users never do, and background messages may not arrive. Test background and terminated states on a real device with &lt;code&gt;flutter run --release&lt;/code&gt; (or a profile build), close the app the way a user would, then send a message.&lt;/p&gt;

&lt;p&gt;To handle taps, use &lt;code&gt;FirebaseMessaging.onMessageOpenedApp&lt;/code&gt; for a tap while the app is in the background, and &lt;code&gt;FirebaseMessaging.instance.getInitialMessage()&lt;/code&gt; for a tap that launched the app from closed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Want this handled for you?
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://notifysphere.net/blog/flutter-push-notifications" rel="noopener noreferrer"&gt;notifySphere Flutter SDK&lt;/a&gt; handles token registration and wires your background handler in at startup, and it tracks delivery and opens for every notification, so you can see whether a message reached the phone or was dropped on the way. It's free up to 1,000 monthly active users. Compare it with other options on our &lt;a href="https://notifysphere.net/vs/onesignal-alternative" rel="noopener noreferrer"&gt;OneSignal alternatives&lt;/a&gt; page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently asked questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why do Flutter notifications work in the foreground but not in the background?
&lt;/h3&gt;

&lt;p&gt;Usually one of four things: the background handler isn't a top-level function marked &lt;a class="mentioned-user" href="https://dev.to/pragma"&gt;@pragma&lt;/a&gt;('vm:entry-point'), it's registered after runApp(), the message is data-only without high priority, or the phone's battery saver is stopping your app. Fix them in that order.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does onBackgroundMessage not get called on iOS?
&lt;/h3&gt;

&lt;p&gt;For a data-only message, iOS needs content-available: 1 in the aps payload and the Background Modes capability with Remote notifications enabled. Even then iOS throttles background messages and won't deliver them if the user swiped the app away. For anything the user must see, send a notification payload instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do push notifications arrive if the user force-stops or swipes away the app?
&lt;/h3&gt;

&lt;p&gt;On Android, force stop from Settings blocks all FCM messages until the user opens the app again; a normal swipe from recents does not, except on some phone brands with aggressive battery savers. On iOS, notification messages are still shown after a swipe, but background data messages are not delivered.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I update the UI from onBackgroundMessage?
&lt;/h3&gt;

&lt;p&gt;No. The background handler runs in a separate isolate with no access to your widgets or app state. Save what you need (for example with shared_preferences) and read it when the app opens, or use onMessageOpenedApp and getInitialMessage to react to taps.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why are notifications not showing when the app is open?
&lt;/h3&gt;

&lt;p&gt;That's expected. In the foreground, Android doesn't display FCM notifications on its own, so you show them yourself with flutter_local_notifications. On iOS, call setForegroundNotificationPresentationOptions(alert: true, badge: true, sound: true).&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published on the &lt;a href="https://notifysphere.net/blog/flutter-push-notifications-not-received-in-background" rel="noopener noreferrer"&gt;notifySphere blog&lt;/a&gt;. notifySphere is push notifications, WhatsApp Business API, deep linking, and in-app support in one SDK — &lt;a href="https://notifysphere.net/pricing" rel="noopener noreferrer"&gt;free up to 1,000 users&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>android</category>
      <category>ios</category>
      <category>flutter</category>
    </item>
    <item>
      <title>App Development Guide: Steps, Tools, and Success Tips</title>
      <dc:creator>Rohit Voice</dc:creator>
      <pubDate>Wed, 28 Oct 2026 05:07:06 +0000</pubDate>
      <link>https://dev.to/rohit_jp/app-development-guide-steps-tools-and-success-tips-1a2h</link>
      <guid>https://dev.to/rohit_jp/app-development-guide-steps-tools-and-success-tips-1a2h</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Application Development is the process of creating applications for mobile phones, desktops, or the web. This process incorporates design, development, testing, and deployment, all aimed at providing functional and engaging experiences for users.&lt;br&gt;
In the present day and age, where the first thing people do in the morning is check their phones for notifications, apps become essential for driving business forward.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Helps shorten time to market&lt;/li&gt;
&lt;li&gt;Drives user engagement&lt;/li&gt;
&lt;li&gt;Provides additional revenue sources&lt;/li&gt;
&lt;li&gt;Improves company reputation&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  App Development Overview
&lt;/h3&gt;

&lt;p&gt;Quick look at what is entailed in the process of app development, from conception to ongoing management after the launch.&lt;/p&gt;

&lt;h3&gt;
  
  
  App Development Phases
&lt;/h3&gt;

&lt;p&gt;Phases common to every project that is successful.&lt;/p&gt;

&lt;h3&gt;
  
  
  Proof of Value for App Development
&lt;/h3&gt;

&lt;p&gt;Numbers that demonstrate the importance of good app development.&lt;br&gt;
&lt;strong&gt;Illustration:&lt;/strong&gt; Retail company develops a mobile app through which consumers can shop, get rewards, and special offers tailored just for them.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;App Category&lt;/th&gt;
&lt;th&gt;Typical Platforms&lt;/th&gt;
&lt;th&gt;Development Complexity&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Native Mobile&lt;/td&gt;
&lt;td&gt;iOS, Android&lt;/td&gt;
&lt;td&gt;High&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hybrid Mobile&lt;/td&gt;
&lt;td&gt;Both (using frameworks)&lt;/td&gt;
&lt;td&gt;Medium&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Progressive Web App&lt;/td&gt;
&lt;td&gt;Any browser&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Important Summary Points&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;App Development creates practical and user-friendly software applications.&lt;/li&gt;
&lt;li&gt;It drives growth, engagement, and competitiveness.&lt;/li&gt;
&lt;li&gt;Important phases in the process include planning, designing, coding, testing, and launching.&lt;/li&gt;
&lt;li&gt;The choice of platform affects budget, efficiency, and sustainability.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What Does App Development Consist of?
&lt;/h2&gt;

&lt;p&gt;App development is the creation of an application from a concept through designing, programming, testing, and release. This will help to achieve all the necessary tasks and make sure that the application is working well for users.&lt;br&gt;
Getting familiar with the main elements of the development process, the team can properly distribute their resources.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Requirement analysis and collection&lt;/li&gt;
&lt;li&gt;UI/UX design and prototyping&lt;/li&gt;
&lt;li&gt;Development on chosen &lt;a href="https://jbcodeapp.com/technology/" rel="noopener noreferrer"&gt;technology stack&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Testing, debugging and QA&lt;/li&gt;
&lt;li&gt;Release to the app store and support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Requirements &amp;amp; Design&lt;/strong&gt;: Understanding the needs of the end-users and designing wireframes – the basis of the project.&lt;br&gt;
&lt;strong&gt;Development &amp;amp; Testing&lt;/strong&gt;: Programming the application in order to be sure that it performs well and is safe for the users.&lt;br&gt;
&lt;strong&gt;Deployment &amp;amp; Maintenance&lt;/strong&gt;: Putting an app online and updating it constantly.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Development Approach&lt;/th&gt;
&lt;th&gt;Best For&lt;/th&gt;
&lt;th&gt;Pros&lt;/th&gt;
&lt;th&gt;Cons&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Native Development&lt;/td&gt;
&lt;td&gt;High performance&lt;/td&gt;
&lt;td&gt;Best UX, full device access&lt;/td&gt;
&lt;td&gt;Requires separate codebases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cross‑Platform (React Native/Flutter)&lt;/td&gt;
&lt;td&gt;Faster time‑to‑market&lt;/td&gt;
&lt;td&gt;Single codebase, cost‑effective&lt;/td&gt;
&lt;td&gt;Slight performance trade‑off&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Progressive Web Apps&lt;/td&gt;
&lt;td&gt;Light‑weight access&lt;/td&gt;
&lt;td&gt;No install required&lt;/td&gt;
&lt;td&gt;Limited device features&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key Concepts for Summary&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The development process of an application consists of designing, programming, testing, and launching.&lt;/li&gt;
&lt;li&gt;Appropriate requirements and design prevent any extra work.&lt;/li&gt;
&lt;li&gt;Multisystem solutions might accelerate the process without affecting its result.&lt;/li&gt;
&lt;li&gt;Constant support will ensure security and satisfaction of customers.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Importance of App Development in Today’s World
&lt;/h2&gt;

&lt;p&gt;In the era of high expectations for digital services, app development plays a vital role in ensuring business success. Companies that invest in their app development notice improved interactions with users and higher revenues.&lt;br&gt;
This does not only help to increase sales but contributes to building brand trust and creating customer loyalty.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;More engaged and loyal users&lt;/li&gt;
&lt;li&gt;Higher conversion rates and increased revenue&lt;/li&gt;
&lt;li&gt;Improved visibility and credibility of the brand&lt;/li&gt;
&lt;li&gt;Insights gained from data analytics to improve products&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;UX&lt;/strong&gt;: The good design of the app ensures intuitive navigation, quick loading, and responsiveness of the interfaces.&lt;br&gt;
&lt;strong&gt;Business benefits&lt;/strong&gt;: &lt;a href="https://jbcodeapp.com/what-we-do/mobile-app-development/" rel="noopener noreferrer"&gt;Mobile applications&lt;/a&gt; provide new sales opportunities, optimization of processes and ongoing revenue generation.&lt;br&gt;
&lt;strong&gt;Technical benefits&lt;/strong&gt;: Thanks to modern development tools, the process can be iterative, scalable and innovative.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Metric&lt;/th&gt;
&lt;th&gt;Typical Impact&lt;/th&gt;
&lt;th&gt;How Apps Contribute&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;User Engagement&lt;/td&gt;
&lt;td&gt;+30% daily active users&lt;/td&gt;
&lt;td&gt;Push notifications &amp;amp; personalized content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Conversion Rate&lt;/td&gt;
&lt;td&gt;+15% sales lift&lt;/td&gt;
&lt;td&gt;Seamless checkout &amp;amp; recommendations&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Customer Retention&lt;/td&gt;
&lt;td&gt;+20% repeat purchases&lt;/td&gt;
&lt;td&gt;Loyalty programs &amp;amp; exclusive offers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Summary Key Points&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The apps are absolutely necessary to satisfy current consumer demands.&lt;/li&gt;
&lt;li&gt;They affect income, engagement, and reputation of brands.&lt;/li&gt;
&lt;li&gt;It is vital to invest in UX and business strategies to maximize ROI.&lt;/li&gt;
&lt;li&gt;The data gathered through apps enables constant improvements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How App Development works The process of app development can consist of many stages to turn your app from conception to launch. By following a good process you lower the risk and increase your chance of success.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Discovery &amp;amp; Requirements&lt;/li&gt;
&lt;li&gt;Design &amp;amp; Prototyping&lt;/li&gt;
&lt;li&gt;Development &amp;amp; Integration&lt;/li&gt;
&lt;li&gt;Testing &amp;amp; QA&lt;/li&gt;
&lt;li&gt;Deployment &amp;amp; Post Launch&lt;/li&gt;
&lt;li&gt;Requirements &amp;amp; Design Conducting interviews with stakeholders along with market research.&lt;/li&gt;
&lt;li&gt;Development &amp;amp; Testing Coding, API Integrations and Testing.&lt;/li&gt;
&lt;li&gt;Deployment &amp;amp; Maintenance Deploying your app to the app stores and 
maintenance based on feedback from users.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Phase&lt;/th&gt;
&lt;th&gt;Typical Duration&lt;/th&gt;
&lt;th&gt;Key Activities&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Discovery&lt;/td&gt;
&lt;td&gt;1‑2 weeks&lt;/td&gt;
&lt;td&gt;Interviews, market analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Design&lt;/td&gt;
&lt;td&gt;1‑2 weeks&lt;/td&gt;
&lt;td&gt;Wireframes, UI mockups&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Development&lt;/td&gt;
&lt;td&gt;4‑8 weeks&lt;/td&gt;
&lt;td&gt;Coding, integration&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testing&lt;/td&gt;
&lt;td&gt;1‑2 weeks&lt;/td&gt;
&lt;td&gt;Unit, UI, beta testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Deployment&lt;/td&gt;
&lt;td&gt;1 week&lt;/td&gt;
&lt;td&gt;Store submission, launch&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Key points to take away:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Every project will be a well-defined, structured process.&lt;/li&gt;
&lt;li&gt;Agile methodologies allow you to quickly make changes and constantly improve.&lt;/li&gt;
&lt;li&gt;Testing your product early on saves you from having to spend more money on fixing issues later on.&lt;/li&gt;
&lt;li&gt;Ongoing support after launch makes sure your product is successful in the long term.&lt;/li&gt;
&lt;li&gt;A flowchart showing each stage of the life-cycle when building an App. &lt;/li&gt;
&lt;li&gt;From discovery to design, development, testing, deployment. Each with icons to highlight what stage it is.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to successfully implement your app
&lt;/h2&gt;

&lt;p&gt;A well-planned strategy and choosing the right tools will help your team follow an effective roadmap and ensure you avoid any issues.&lt;br&gt;
Splitting your app into smaller steps means even complicated apps will be easy to implement.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Define your goals and how you’ll build your MVP&lt;/li&gt;
&lt;li&gt;Choose which platform and technology you want to use&lt;/li&gt;
&lt;li&gt;Set up your development environment and version control&lt;/li&gt;
&lt;li&gt;Build out your key features
-Test your app thoroughly and get feedback from your users.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Define your goals and plan your MVP&lt;/strong&gt;: Think of the problem your user has and define what the minimum features required are to solve it.&lt;br&gt;
&lt;strong&gt;Choose your technology and platform&lt;/strong&gt;: Decide whether to go with a Native solution or Cross-Platform solution based on performance, budget and time.&lt;br&gt;
&lt;strong&gt;Code your app and iterate&lt;/strong&gt;: Clean code can be written, the necessary APIs integrated and incremental releases can be made based on testing.&lt;br&gt;
For example a travel agency may create a trip planning app using &lt;a href="https://jbcodeapp.com/services/mobile/flutter-app-development/" rel="noopener noreferrer"&gt;Flutter&lt;/a&gt; which is a cross-platform solution meaning they can use the same code across both iOS and Android.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool Category&lt;/th&gt;
&lt;th&gt;Popular Options&lt;/th&gt;
&lt;th&gt;Key Features&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;IDEs&lt;/td&gt;
&lt;td&gt;Android Studio, Xcode, Visual Studio Code&lt;/td&gt;
&lt;td&gt;Code editing, debugging, UI design&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Version Control&lt;/td&gt;
&lt;td&gt;Git, GitHub&lt;/td&gt;
&lt;td&gt;Collaborative coding, history tracking&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI/CD&lt;/td&gt;
&lt;td&gt;Jenkins, GitHub Actions&lt;/td&gt;
&lt;td&gt;Automated testing &amp;amp; deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testing&lt;/td&gt;
&lt;td&gt;Espresso, XCTest, Detox&lt;/td&gt;
&lt;td&gt;Automated UI testing across devices&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key Takeaways&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with a clear MVP to test if your idea will work in the market.&lt;/li&gt;
&lt;li&gt;Choosing the correct tech stack can help speed up your product while ensuring it is fast and doesn’t cost too much.&lt;/li&gt;
&lt;li&gt;Iteratively developing your product ensures it works for your users.&lt;/li&gt;
&lt;li&gt;Strong testing ensures your product is reliable.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Benefits of Modern App Development
&lt;/h2&gt;

&lt;p&gt;When modern app development is used, you are able to build high quality products using advanced frameworks, cloud and agile processes. This also gives many benefits which can improve your overall business.&lt;br&gt;
Businesses that take advantage of this will often experience quicker growth.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Improved user engagement&lt;/li&gt;
&lt;li&gt;Scalable solutions&lt;/li&gt;
&lt;li&gt;Cost efficient&lt;/li&gt;
&lt;li&gt;Faster development times&lt;/li&gt;
&lt;li&gt;Improved User Engagement
Users will engage more with your app thanks to a personalised interface, in-app notifications and smooth interactions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scalable&lt;/strong&gt;: Apps will scale thanks to cloud based back ends and micro-services.&lt;br&gt;
&lt;strong&gt;Cost Efficient&lt;/strong&gt;: Code can be reused meaning lower development costs.&lt;br&gt;
&lt;strong&gt;Faster development&lt;/strong&gt;: Using CI/CD processes means developments can happen quicker.&lt;br&gt;
For example, a news publisher has developed an app that sends out breaking news alerts. This has increased reader numbers.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Benefit&lt;/th&gt;
&lt;th&gt;Real‑World Impact&lt;/th&gt;
&lt;th&gt;Implementation Tip&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Higher Engagement&lt;/td&gt;
&lt;td&gt;+35% daily active users&lt;/td&gt;
&lt;td&gt;Use behavior analytics to personalize content&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scalable Architecture&lt;/td&gt;
&lt;td&gt;Supports 1M+ users&lt;/td&gt;
&lt;td&gt;Adopt cloud services like AWS or Firebase&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reduced Costs&lt;/td&gt;
&lt;td&gt;20% lower total development budget&lt;/td&gt;
&lt;td&gt;Leverage cross‑platform frameworks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Quick Releases&lt;/td&gt;
&lt;td&gt;Weekly updates without downtime&lt;/td&gt;
&lt;td&gt;Implement CI/CD pipelines&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key points&lt;/strong&gt;&lt;br&gt;
Modern tools create a better and more engaging experience for users&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Architectures are scalable&lt;/li&gt;
&lt;li&gt;Saves costs (reusable code)&lt;/li&gt;
&lt;li&gt;Fast delivery to keep your app competitive&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Challenges &amp;amp; Ways to Overcome
&lt;/h2&gt;

&lt;p&gt;Despite having advanced tools at your disposal, developing an application can present several challenges including security vulnerabilities, performance issues and regular updates. If you identify potential challenges within your project early you can create solutions to help overcome them.&lt;br&gt;
By being proactive with your approach you reduce your risk and will have a smooth journey to launch.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Security Vulnerabilities&lt;/li&gt;
&lt;li&gt;Performance Issues&lt;/li&gt;
&lt;li&gt;Keeping up with constant OS and frameworks updates&lt;/li&gt;
&lt;li&gt;Managing Development Costs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Security Vulnerabilities&lt;/strong&gt;: Use end-to-end encryption, regular security checks and ensure you follow data privacy rules.&lt;br&gt;
&lt;strong&gt;Performance Issues&lt;/strong&gt;: Optimise your code, use efficient APIs and test thoroughly across devices.&lt;br&gt;
&lt;strong&gt;Keeping up with Updates&lt;/strong&gt;: Use modular design and continuous integration to keep up with new OS versions and SDK releases.&lt;br&gt;
For example a banking application will use end-to-end encryption and perform regular security checks every quarter to keep users financial data secure and build trust.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Challenge&lt;/th&gt;
&lt;th&gt;Common Pitfall&lt;/th&gt;
&lt;th&gt;Effective Solution&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Security&lt;/td&gt;
&lt;td&gt;Inadequate data encryption&lt;/td&gt;
&lt;td&gt;Use industry‑standard encryption &amp;amp; regular audits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;Inefficient code causing slow load times&lt;/td&gt;
&lt;td&gt;Profile and optimize critical paths, leverage native modules&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Updates&lt;/td&gt;
&lt;td&gt;Lagging behind new OS features&lt;/td&gt;
&lt;td&gt;Adopt modular codebase and automated testing&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Main points to remember&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Proactive security protection helps protect user data and keep your brand safe.&lt;/li&gt;
&lt;li&gt;Optimise performance to provide user's with a smooth experience.
Keep up to date with updates to keep your app current.&lt;/li&gt;
&lt;li&gt;Ensure you're using optimal resources to help keep costs at bay.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Tools, Technologies &amp;amp; Best Practice&lt;/strong&gt;&lt;br&gt;
Using the correct tools as well as following best practice will ensure an efficient and quality application. This includes using integrated development environments, CI/CD pipelines etc.&lt;br&gt;
Keeping up to date with new technologies will ensure your team creates &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;innovative &amp;amp; reliable applications.&lt;/li&gt;
&lt;li&gt;Integrated Development Environments&lt;/li&gt;
&lt;li&gt;Code editors, debuggers &amp;amp; UI Designers.&lt;/li&gt;
&lt;li&gt;Version Control &amp;amp; CI/CD
Using Git &amp;amp; GitHub allows you to work on code collaboratively. CI/CD will automatically test &amp;amp; build your application.&lt;/li&gt;
&lt;li&gt;Testing Frameworks&lt;/li&gt;
&lt;li&gt;Automated UI testing across various devices can be done using tools such as Espresso, XCTest &amp;amp; Detox.&lt;/li&gt;
&lt;li&gt;Cloud Services &amp;amp; APIs
Cloud services allow you to scale your application and APIs can be used to add 3rd party features such as payment &amp;amp; analytics.
For example a SaaS company may use Docker containers &amp;amp; GitHub Actions to automate building, testing &amp;amp; deploying updates, this drastically reduces manual work &amp;amp; errors.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Recommended Tools&lt;/th&gt;
&lt;th&gt;Why They Matter&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;IDEs&lt;/td&gt;
&lt;td&gt;Android Studio, Xcode, VS Code&lt;/td&gt;
&lt;td&gt;Unified coding, debugging, UI design&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Version Control&lt;/td&gt;
&lt;td&gt;Git, GitHub&lt;/td&gt;
&lt;td&gt;Track changes, enable teamwork&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI/CD&lt;/td&gt;
&lt;td&gt;Jenkins, GitHub Actions&lt;/td&gt;
&lt;td&gt;Automated testing &amp;amp; deployment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testing&lt;/td&gt;
&lt;td&gt;Espresso, XCTest, Detox&lt;/td&gt;
&lt;td&gt;Reliable cross‑device testing&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cloud&lt;/td&gt;
&lt;td&gt;AWS, Firebase, Azure&lt;/td&gt;
&lt;td&gt;Scalable hosting &amp;amp; services&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Key Summary Points&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Modern IDEs streamline the development workflow.&lt;/li&gt;
&lt;li&gt;Version control and CI/CD ensure reliability and speed.&lt;/li&gt;
&lt;li&gt;Comprehensive testing tools guarantee quality across devices.&lt;/li&gt;
&lt;li&gt;Cloud services provide the scalability needed for growing user bases.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Developing an app is an exciting field full of creativity and technical skill. Understanding what goes into developing an app, using the correct tools and overcoming any challenges will help you create an engaging app that increases revenues.&lt;br&gt;
Creating an app can be an iterative process, but by following best practice, keeping up with new trends and letting data guide you, you can &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;create an app that will engage your users.&lt;/li&gt;
&lt;li&gt;Set clear goals and have a strong MVP strategy.&lt;/li&gt;
&lt;li&gt;Use modern framework to speed up development and reduce costs.&lt;/li&gt;
&lt;li&gt;Focus on security, performance and feedback from users.&lt;/li&gt;
&lt;li&gt;Use automation and cloud to scale.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;Define MVP &amp;amp; goals&lt;/td&gt;
&lt;td&gt;Validates market fit early&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;Choose tech stack&lt;/td&gt;
&lt;td&gt;Balances speed, cost, performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Implement CI/CD&lt;/td&gt;
&lt;td&gt;Reduces errors, speeds releases&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Gather user feedback&lt;/td&gt;
&lt;td&gt;Drives continuous improvement&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Scale with cloud services&lt;/td&gt;
&lt;td&gt;Supports growth without downtime&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>flutter</category>
      <category>programming</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Flutter Deep Linking: App Links Guide</title>
      <dc:creator>Muhammad Uzair</dc:creator>
      <pubDate>Wed, 28 Oct 2026 03:01:35 +0000</pubDate>
      <link>https://dev.to/uzairxdev/flutter-deep-linking-app-links-guide-1889</link>
      <guid>https://dev.to/uzairxdev/flutter-deep-linking-app-links-guide-1889</guid>
      <description>&lt;p&gt;A link to a product page should open the product page, not a browser tab asking the user to install an app they already have. That gap is where deep linking earns its keep: a shared link, a marketing email, a QR code, or a push notification lands the user on exactly the right screen, logged in, with the back stack intact.&lt;/p&gt;

&lt;p&gt;Flutter makes the in-app half easy. The part that trips developers up is verification: proving to Android and iOS that your app is allowed to handle your domain. This guide covers the whole chain — Android App Links, iOS Universal Links, custom schemes as a fallback, and the Dart code that turns an incoming URL into a route — with every file you need.&lt;/p&gt;

&lt;h2&gt;
  
  
  The three kinds of links
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;How it opens&lt;/th&gt;
&lt;th&gt;When to use it&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Custom scheme (myapp://products/42)&lt;/td&gt;
&lt;td&gt;Always shows a chooser if several apps claim it&lt;/td&gt;
&lt;td&gt;Internal links, development, fallback&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android App Link (&lt;a href="https://example.com/products/42" rel="noopener noreferrer"&gt;https://example.com/products/42&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Opens your app directly, no chooser&lt;/td&gt;
&lt;td&gt;Production links on Android&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iOS Universal Link (&lt;a href="https://example.com/products/42" rel="noopener noreferrer"&gt;https://example.com/products/42&lt;/a&gt;)&lt;/td&gt;
&lt;td&gt;Opens your app directly, no chooser&lt;/td&gt;
&lt;td&gt;Production links on iOS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The rule of thumb: ship verified HTTPS links for anything a user can tap, and keep a custom scheme only as a fallback for environments where you do not control a domain. Verified links also survive the case where the app is not installed — they simply open the website — while a custom scheme shows an error.&lt;/p&gt;

&lt;h2&gt;
  
  
  Android: App Links
&lt;/h2&gt;

&lt;p&gt;Android verification has two halves: the app declares the domains it wants, and the domain publishes a file confirming the app.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Declare the intent filter
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;android/app/src/main/AndroidManifest.xml&lt;/code&gt;, add an intent filter to the main activity. The &lt;code&gt;android:autoVerify="true"&lt;/code&gt; attribute is the key detail: it tells Android to check the domain at install time, and on Android 12 and later a link is only treated as verified when this check passes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;intent-filter&lt;/span&gt; &lt;span class="na"&gt;android:autoVerify=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;action&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.action.VIEW"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;category&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.category.DEFAULT"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;category&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.category.BROWSABLE"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;data&lt;/span&gt; &lt;span class="na"&gt;android:scheme=&lt;/span&gt;&lt;span class="s"&gt;"https"&lt;/span&gt; &lt;span class="na"&gt;android:host=&lt;/span&gt;&lt;span class="s"&gt;"example.com"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/intent-filter&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use one intent filter per host, and keep the path open: verification applies to the whole host, and you decide which paths the app handles in Dart.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Publish assetlinks.json
&lt;/h3&gt;

&lt;p&gt;Serve this file at &lt;code&gt;https://example.com/.well-known/assetlinks.json&lt;/code&gt; over HTTPS, with content type &lt;code&gt;application/json&lt;/code&gt;, no redirects, and no authentication gate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"relation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"delegate_permission/common.handle_all_urls"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"namespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"android_app"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"package_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.example.shop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"sha256_cert_fingerprints"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fingerprint must match the key that signed the build. List both fingerprints if you test debug and release builds against the same domain — a debug build signed with the debug key will not verify against a file that only lists the release fingerprint. Get a fingerprint with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;keytool &lt;span class="nt"&gt;-list&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;-keystore&lt;/span&gt; android/app/release.keystore &lt;span class="nt"&gt;-alias&lt;/span&gt; upload
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the debug key, the keystore is &lt;code&gt;~/.android/debug.keystore&lt;/code&gt; with alias &lt;code&gt;androiddebugkey&lt;/code&gt; and password &lt;code&gt;android&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Confirm verification
&lt;/h3&gt;

&lt;p&gt;Android re-checks verification at install time, so after changing the file, uninstall and reinstall the app rather than just relaunching it. You can also check the system state with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adb shell pm get-app-links com.example.shop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the domain shows as unverified, check the file path first, then the fingerprint, then the content type — those three cover nearly every failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  iOS: Universal Links
&lt;/h2&gt;

&lt;p&gt;iOS verification mirrors Android: a capability in the app, and a file on the domain.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Add the Associated Domains capability
&lt;/h3&gt;

&lt;p&gt;In Xcode, open the Runner target, go to Signing and Capabilities, add Associated Domains, and add one entry per domain:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;applinks:example.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do not add a scheme here, and do not add a path: the domain entry covers the whole host, and path filtering happens in the association file.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Publish apple-app-site-association
&lt;/h3&gt;

&lt;p&gt;Serve this file at &lt;code&gt;https://example.com/.well-known/apple-app-site-association&lt;/code&gt; over HTTPS, with content type &lt;code&gt;application/json&lt;/code&gt;, and no &lt;code&gt;.json&lt;/code&gt; extension on the URL:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"applinks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"details"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"appIDs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"TEAMID.com.example.shop"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"components"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"#"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"no_universal_links"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"comment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Matches a page that should stay in the browser"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/products/*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/orders/*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The app ID is your Team ID followed by the bundle ID, and you can find the Team ID in your Apple Developer account under Membership. The components list is evaluated in order, so put exclusions before the patterns they override. A path that matches no component falls back to the browser, which is exactly what you want for pages like login or legal content.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Confirm on device
&lt;/h3&gt;

&lt;p&gt;iOS fetches the association file when the app is installed, and it caches aggressively. As on Android, uninstall and reinstall after changing the file. Test on a real device or simulator with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xcrun simctl openurl booted https://example.com/products/42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the link opens Safari instead of the app, check the file URL first (the missing-extension detail catches people), then the app ID, then whether the device actually reinstalled the app after the file changed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Custom schemes as a fallback
&lt;/h2&gt;

&lt;p&gt;A custom scheme needs no domain and no verification file, which makes it useful for development and for links that never leave your own app. Declare it in the manifest alongside the HTTPS filter, and in &lt;code&gt;ios/Runner/Info.plist&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleURLTypes&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleURLSchemes&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;myshop&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Treat custom schemes as untrusted input: any app on the device can claim the same scheme, so never put tokens or sensitive data in a custom-scheme link, and never let one trigger a privileged action without a logged-in session check.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling links in Dart
&lt;/h2&gt;

&lt;p&gt;The &lt;a href="https://pub.dev/packages/app_links" rel="noopener noreferrer"&gt;app_links&lt;/a&gt; package gives you both halves of the problem: the link that launched the app, and the stream of links that arrive while it runs. The stream alone is not enough — it only fires when the app is already alive, so a cold start would silently drop the link.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:app_links/app_links.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:flutter/material.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="s"&gt;'package:go_router/go_router.dart'&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;_router&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;GoRouter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;routes:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="n"&gt;GoRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s"&gt;'/'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;HomeScreen&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="n"&gt;GoRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s"&gt;'/products/:id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;ProductScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;GoRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;path:&lt;/span&gt; &lt;span class="s"&gt;'/orders/:id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;builder:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;OrderScreen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;id:&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pathParameters&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s"&gt;'id'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;initDeepLinks&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="kd"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;appLinks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AppLinks&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// Cold start: the link that launched the app, if any.&lt;/span&gt;
  &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;initialLink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;appLinks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInitialLink&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initialLink&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;_handleLink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;initialLink&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="c1"&gt;// Warm state: links that arrive while the app is running.&lt;/span&gt;
  &lt;span class="n"&gt;appLinks&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;uriLinkStream&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_handleLink&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;_handleLink&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Uri&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// Route from the path only, so the destination survives a cold start.&lt;/span&gt;
  &lt;span class="n"&gt;_router&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;go&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two details matter here. First, route from &lt;code&gt;uri.path&lt;/code&gt;, not from extra state or a cached object, because a cold-started app has no previous session to read from. Second, guard routes that need login the same way you guard normal navigation — for example with the redirect pattern covered in &lt;a href="https://www.uzairxdev.online/blog/flutter-go-router-navigation-guide" rel="noopener noreferrer"&gt;Flutter go_router: The Navigation Guide&lt;/a&gt; — so a shared link to a private screen lands on login first and then continues to the destination.&lt;/p&gt;

&lt;p&gt;Links from push notifications follow the same path: when a notification tap carries a URL, feed it through the same handler instead of building a second routing system. The notification setup itself is covered in &lt;a href="https://www.uzairxdev.online/blog/flutter-firebase-push-notifications-guide" rel="noopener noreferrer"&gt;Flutter Firebase Push Notifications Guide&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing checklist
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Android, cold start: kill the app, then run &lt;code&gt;adb shell am start -a android.intent.action.VIEW -d "https://example.com/products/42"&lt;/code&gt; and confirm the product screen opens, not the browser.&lt;/li&gt;
&lt;li&gt;Android, warm state: with the app open on another screen, run the same command and confirm it navigates in place.&lt;/li&gt;
&lt;li&gt;iOS: paste the link into Notes and tap it, or use &lt;code&gt;xcrun simctl openurl&lt;/code&gt;, and confirm the app opens without a Safari intermediate page.&lt;/li&gt;
&lt;li&gt;App not installed: tap the link on a clean device and confirm the website opens, since the HTTPS link degrades gracefully.&lt;/li&gt;
&lt;li&gt;After any change to either verification file: uninstall and reinstall the app before retesting, because both platforms cache verification at install time.&lt;/li&gt;
&lt;li&gt;Edge cases: a link to an excluded path should open the browser, a link with an unknown product ID should show a not-found screen inside the app rather than crashing, and a link tapped while logged out should route through login first.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Symptom&lt;/th&gt;
&lt;th&gt;Most likely cause&lt;/th&gt;
&lt;th&gt;Fix&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Android opens the browser&lt;/td&gt;
&lt;td&gt;Verification failed&lt;/td&gt;
&lt;td&gt;Check the assetlinks.json path, HTTPS, content type, and SHA-256 fingerprint, then reinstall&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android shows an app chooser&lt;/td&gt;
&lt;td&gt;autoVerify missing or failed&lt;/td&gt;
&lt;td&gt;Add android:autoVerify="true" and confirm verification with adb&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iOS opens Safari&lt;/td&gt;
&lt;td&gt;Association file problem&lt;/td&gt;
&lt;td&gt;Check the well-known URL without a .json extension, the app ID, and reinstall&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iOS opens the app for excluded paths&lt;/td&gt;
&lt;td&gt;Component order&lt;/td&gt;
&lt;td&gt;Put exclusions before the wildcard patterns they override&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Link works warm but not cold&lt;/td&gt;
&lt;td&gt;Only the stream is handled&lt;/td&gt;
&lt;td&gt;Add getInitialLink handling at startup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Link opens the wrong screen&lt;/td&gt;
&lt;td&gt;Routing from extra state&lt;/td&gt;
&lt;td&gt;Route from uri.path so cold starts resolve the same way&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Pre-launch checklist
&lt;/h2&gt;

&lt;p&gt;Before you ship deep links, confirm the whole chain once: intent filter with autoVerify, assetlinks.json live over HTTPS with both fingerprints, Associated Domains capability, apple-app-site-association live without a file extension, cold-start and warm handling in Dart, login guards on private routes, and a graceful website fallback when the app is absent. Deep links are infrastructure, not a feature — when they work, nobody notices them, and that silence is the point.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.uzairxdev.online/contact" rel="noopener noreferrer"&gt;Get in touch&lt;/a&gt; and let's talk about your project.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://www.uzairxdev.online/blog/flutter-deep-linking-app-links-universal-links" rel="noopener noreferrer"&gt;uzairxdev.online&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>deeplinking</category>
      <category>applinks</category>
      <category>universallinks</category>
    </item>
    <item>
      <title>How to Build Interactive AI Mascots for React, Flutter &amp; SaaS Apps with Rive (2027 Guide)</title>
      <dc:creator>Praneeth Kawya Thathsara</dc:creator>
      <pubDate>Wed, 28 Oct 2026 01:22:57 +0000</pubDate>
      <link>https://dev.to/uianimation/how-to-build-interactive-ai-mascots-for-react-flutter-saas-apps-with-rive-2027-guide-39j3</link>
      <guid>https://dev.to/uianimation/how-to-build-interactive-ai-mascots-for-react-flutter-saas-apps-with-rive-2027-guide-39j3</guid>
      <description>&lt;p&gt;&lt;em&gt;From static chatbot icons to real-time AI companions: a practical guide to Rive state machines, data binding, talking avatars, and developer-ready character animation.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Building an AI-powered application in 2027?&lt;/p&gt;

&lt;p&gt;You might already have an LLM, a chat interface, voice recognition, text-to-speech, and a working frontend.&lt;/p&gt;

&lt;p&gt;But what happens visually when your AI is listening, thinking, or speaking?&lt;/p&gt;

&lt;p&gt;For many applications, the answer is still a loading spinner, a static avatar, or three animated dots.&lt;/p&gt;

&lt;p&gt;What if your AI assistant could actually react to your application's state?&lt;/p&gt;

&lt;p&gt;Imagine a character that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Waves when a user opens the app.&lt;/li&gt;
&lt;li&gt;Listens when the microphone activates.&lt;/li&gt;
&lt;li&gt;Thinks while your AI processes a response.&lt;/li&gt;
&lt;li&gt;Speaks using animated mouth shapes.&lt;/li&gt;
&lt;li&gt;Changes facial expressions based on context.&lt;/li&gt;
&lt;li&gt;Celebrates when a user completes a task.&lt;/li&gt;
&lt;li&gt;Returns naturally to idle when the interaction ends.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can build this kind of experience using &lt;strong&gt;Rive&lt;/strong&gt;, its state machines, and runtime data binding.&lt;/p&gt;

&lt;p&gt;In this guide, I'll explain how interactive Rive mascots work, how developers can structure their application logic, and what to consider before integrating one into a production app.&lt;/p&gt;

&lt;p&gt;I'm &lt;strong&gt;Praneeth Kawya Thathsara&lt;/strong&gt;, a Rive animator and interactive motion designer specializing in custom character systems for AI applications, SaaS products, React, Flutter, and React Native.&lt;/p&gt;

&lt;p&gt;I help developers turn character designs into reusable interactive animation systems.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Need a custom mascot for your app?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Visit &lt;a href="https://riveanimator.com" rel="noopener noreferrer"&gt;RiveAnimator.com&lt;/a&gt; or &lt;a href="https://wa.me/94717000999?text=Hi%20Praneeth%21%20How%20much%20would%20a%20custom%20Rive%20mascot%20cost%20for%20my%20app%3F%20MEDIUM20" rel="noopener noreferrer"&gt;send me a quick WhatsApp message&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I'm currently offering &lt;strong&gt;20% off eligible new custom Rive animation projects&lt;/strong&gt; with code &lt;code&gt;MEDIUM20&lt;/code&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Why Interactive Characters Are Different From Traditional Animations
&lt;/h2&gt;

&lt;p&gt;Traditional animations usually follow a predefined timeline.&lt;/p&gt;

&lt;p&gt;For example, a GIF might show a character waving continuously.&lt;/p&gt;

&lt;p&gt;That works for decorative motion.&lt;/p&gt;

&lt;p&gt;But an AI assistant needs to react to application events.&lt;/p&gt;

&lt;p&gt;Consider this interaction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User opens app
    ↓
Mascot welcomes user
    ↓
User activates microphone
    ↓
Mascot starts listening
    ↓
Speech recognition finishes
    ↓
AI begins processing
    ↓
Mascot starts thinking
    ↓
AI response becomes available
    ↓
Mascot starts speaking
    ↓
Audio playback finishes
    ↓
Mascot returns to idle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These are not simply animations played in sequence.&lt;/p&gt;

&lt;p&gt;They're &lt;strong&gt;application-driven character behaviors&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The application determines what should happen, and the animation system responds.&lt;/p&gt;

&lt;p&gt;This separation is important because it allows the same character to work with different AI models, APIs, and frontend frameworks.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Why Rive Is Useful for Interactive App Mascots
&lt;/h2&gt;

&lt;p&gt;Rive is an interactive animation platform with a visual editor, state machines, data binding, and runtimes for web and mobile applications.&lt;/p&gt;

&lt;p&gt;Unlike a traditional exported video, a Rive character can respond to application-controlled properties.&lt;/p&gt;

&lt;p&gt;For example, a character might expose:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Property&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isListening&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Boolean&lt;/td&gt;
&lt;td&gt;Activate listening behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isThinking&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Boolean&lt;/td&gt;
&lt;td&gt;Activate thinking behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isSpeaking&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Boolean&lt;/td&gt;
&lt;td&gt;Activate speaking behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;emotion&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;Select facial expression&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;mouthShape&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;Select speech mouth shape&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lookX&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;Control horizontal gaze&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lookY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Number&lt;/td&gt;
&lt;td&gt;Control vertical gaze&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;celebrate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Trigger&lt;/td&gt;
&lt;td&gt;Play a one-shot celebration&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are example properties, not universal Rive inputs.&lt;/p&gt;

&lt;p&gt;The actual contract depends on how the character is designed.&lt;/p&gt;

&lt;h3&gt;
  
  
  State machines vs. data binding
&lt;/h3&gt;

&lt;p&gt;This distinction matters in 2027.&lt;/p&gt;

&lt;p&gt;Rive supports state machine logic, while modern runtimes increasingly emphasize &lt;strong&gt;ViewModels and data binding&lt;/strong&gt; for connecting application data to animations.&lt;/p&gt;

&lt;p&gt;For new data-driven characters, I recommend planning the ViewModel contract early.&lt;/p&gt;

&lt;p&gt;For existing projects, direct state machine inputs may still be appropriate, depending on the runtime and version.&lt;/p&gt;

&lt;p&gt;The important principle remains the same:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Your application owns the product state. Rive owns the character's visual behavior.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Designing a Reliable AI Assistant State System
&lt;/h2&gt;

&lt;p&gt;One of the biggest challenges in interactive mascot development isn't drawing the character.&lt;/p&gt;

&lt;p&gt;It's deciding what happens when multiple application events occur.&lt;/p&gt;

&lt;p&gt;Imagine your app reports:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;assistantState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;isListening&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;isThinking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;isSpeaking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&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;Should the character listen or think?&lt;/p&gt;

&lt;p&gt;Without a defined priority system, the animation can behave unpredictably.&lt;/p&gt;

&lt;p&gt;A simple approach is to establish clear priorities.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getCharacterMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSpeaking&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;speaking&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isThinking&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;thinking&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isListening&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;listening&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;idle&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Speaking &amp;gt; Thinking &amp;gt; Listening &amp;gt; Idle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a conceptual application-state example, not a Rive runtime API.&lt;/p&gt;

&lt;p&gt;The resulting mode can be connected to the character through its documented Rive data-binding properties.&lt;/p&gt;

&lt;p&gt;For more complex applications, a single mutually exclusive mode can be preferable to several competing booleans.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why priority matters
&lt;/h3&gt;

&lt;p&gt;Without predictable transitions, you can encounter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple animations competing for control.&lt;/li&gt;
&lt;li&gt;Characters stuck in a speaking state.&lt;/li&gt;
&lt;li&gt;Repeated triggers restarting animations.&lt;/li&gt;
&lt;li&gt;Interrupted celebrations.&lt;/li&gt;
&lt;li&gt;Expressions not restoring correctly.&lt;/li&gt;
&lt;li&gt;Unexpected behavior during rapid API updates.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A well-designed character should handle these situations before production deployment.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Integrating an Interactive Rive Mascot Into React
&lt;/h2&gt;

&lt;p&gt;React applications are a common use case for interactive character systems.&lt;/p&gt;

&lt;p&gt;The first step is loading a &lt;code&gt;.riv&lt;/code&gt; file and rendering the character.&lt;/p&gt;

&lt;p&gt;For example, a basic setup using the current React runtime can look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; @rive-app/react-webgl2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useRive&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@rive-app/react-webgl2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;AssistantMascot&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;RiveComponent&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRive&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/mascot.riv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;stateMachine&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Assistant&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;autoplay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;style&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;320&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;320&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;RiveComponent&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example assumes your &lt;code&gt;.riv&lt;/code&gt; file contains a state machine named &lt;code&gt;Assistant&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For a production AI character, the next step is connecting the application's state to a Rive ViewModel.&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Conceptual application state&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;aiState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;listening&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;emotion&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="na"&gt;mouthShape&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your runtime integration maps those values to the appropriate Rive properties.&lt;/p&gt;

&lt;p&gt;The exact binding code depends on the runtime version and the ViewModel defined inside your Rive file.&lt;/p&gt;

&lt;p&gt;Refer to the &lt;a href="https://rive.app/" rel="noopener noreferrer"&gt;official Rive documentation&lt;/a&gt; for the current runtime APIs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Important developer consideration
&lt;/h3&gt;

&lt;p&gt;Don't build your application around undocumented animation input names.&lt;/p&gt;

&lt;p&gt;Before integration, request a contract that defines:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Rive file name&lt;/li&gt;
&lt;li&gt;Artboard name&lt;/li&gt;
&lt;li&gt;State machine name&lt;/li&gt;
&lt;li&gt;ViewModel name&lt;/li&gt;
&lt;li&gt;Property names and types&lt;/li&gt;
&lt;li&gt;Default values&lt;/li&gt;
&lt;li&gt;Supported number ranges&lt;/li&gt;
&lt;li&gt;Trigger behavior&lt;/li&gt;
&lt;li&gt;Completion events&lt;/li&gt;
&lt;li&gt;Reset and interruption rules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This small step can prevent significant integration problems later.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Building Rive Mascots for Flutter and React Native
&lt;/h2&gt;

&lt;p&gt;The same overall approach works for mobile applications.&lt;/p&gt;

&lt;p&gt;Your application manages business logic, while the Rive runtime displays and controls the character.&lt;/p&gt;

&lt;h3&gt;
  
  
  Flutter
&lt;/h3&gt;

&lt;p&gt;Flutter developers can use the official &lt;code&gt;rive&lt;/code&gt; package.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flutter pub add rive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For current integrations, review the latest Flutter runtime documentation, including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Loading Rive files&lt;/li&gt;
&lt;li&gt;State machine playback&lt;/li&gt;
&lt;li&gt;ViewModels&lt;/li&gt;
&lt;li&gt;Data binding&lt;/li&gt;
&lt;li&gt;Runtime lifecycle management&lt;/li&gt;
&lt;li&gt;Asset loading and caching&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  React Native
&lt;/h3&gt;

&lt;p&gt;React Native applications can use the official Rive runtime.&lt;/p&gt;

&lt;p&gt;A typical integration involves loading a &lt;code&gt;.riv&lt;/code&gt; asset, creating or accessing the appropriate ViewModel instance, and binding application properties to the character.&lt;/p&gt;

&lt;p&gt;The exact implementation depends on your React Native and Rive runtime versions.&lt;/p&gt;

&lt;p&gt;For new projects, confirm compatibility before choosing the integration approach.&lt;/p&gt;

&lt;h3&gt;
  
  
  Performance considerations
&lt;/h3&gt;

&lt;p&gt;Whether you're building for React, Flutter, or React Native, test the character on real target devices.&lt;/p&gt;

&lt;p&gt;Pay attention to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Animation complexity&lt;/li&gt;
&lt;li&gt;Mesh and vector detail&lt;/li&gt;
&lt;li&gt;Number of active animations&lt;/li&gt;
&lt;li&gt;Memory usage&lt;/li&gt;
&lt;li&gt;Asset loading behavior&lt;/li&gt;
&lt;li&gt;Reduced-motion preferences&lt;/li&gt;
&lt;li&gt;Runtime initialization and cleanup&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A character that looks smooth inside an editor still needs proper testing in the application.&lt;/p&gt;




&lt;h2&gt;
  
  
  Need a Production-Ready Rive Mascot for Your App?
&lt;/h2&gt;

&lt;p&gt;If you're a developer building an AI assistant, SaaS product, or mobile application, I can help with the character animation system.&lt;/p&gt;

&lt;p&gt;I create custom interactive Rive mascots with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reusable character rigging&lt;/li&gt;
&lt;li&gt;Developer-friendly state machines&lt;/li&gt;
&lt;li&gt;AI listening, thinking, and speaking animations&lt;/li&gt;
&lt;li&gt;Facial expressions and eye controls&lt;/li&gt;
&lt;li&gt;Lip-sync-ready mouth shapes&lt;/li&gt;
&lt;li&gt;App-driven animation properties&lt;/li&gt;
&lt;li&gt;Integration documentation&lt;/li&gt;
&lt;li&gt;React, Flutter, and React Native handoff support&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Wondering how much a custom mascot would cost for your app?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Just ask Praneeth.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://riveanimator.com" rel="noopener noreferrer"&gt;riveanimator.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Email:&lt;/strong&gt; &lt;a href="mailto:riveanimator@gmail.com?subject=Custom%20Rive%20Mascot%20Project%20-%20MEDIUM20"&gt;riveanimator@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WhatsApp:&lt;/strong&gt; &lt;a href="https://wa.me/94717000999?text=Hi%20Praneeth%21%20I%27m%20building%20an%20app%20and%20would%20like%20to%20know%20how%20much%20a%20custom%20interactive%20Rive%20mascot%20would%20cost.%20MEDIUM20" rel="noopener noreferrer"&gt;Send a quick project inquiry&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Special offer:&lt;/strong&gt; 20% off eligible new custom Rive projects with code &lt;code&gt;MEDIUM20&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You don't need to have a completed character design to request an estimate.&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Adding Real-Time Lip Sync to an AI Talking Avatar
&lt;/h2&gt;

&lt;p&gt;A talking avatar requires more than a simple mouth-open animation.&lt;/p&gt;

&lt;p&gt;For more natural speech animation, the character can support multiple mouth shapes corresponding to different speech sounds.&lt;/p&gt;

&lt;p&gt;These shapes are often called &lt;strong&gt;visemes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Viseme&lt;/th&gt;
&lt;th&gt;Visual behavior&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Neutral&lt;/td&gt;
&lt;td&gt;Resting mouth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;A&lt;/td&gt;
&lt;td&gt;Open mouth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;E&lt;/td&gt;
&lt;td&gt;Wide mouth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;O&lt;/td&gt;
&lt;td&gt;Rounded mouth&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;M/B/P&lt;/td&gt;
&lt;td&gt;Closed lips&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;F/V&lt;/td&gt;
&lt;td&gt;Lower lip and upper teeth&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The exact number of visemes depends on the project.&lt;/p&gt;

&lt;p&gt;Your speech system can generate or provide timing information, while the application updates the character's mouth-shape property.&lt;/p&gt;

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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updateAvatarSpeech&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;viseme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mouthShape&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;visemeToMouthShape&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;viseme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// Update the bound Rive mouthShape property&lt;/span&gt;
  &lt;span class="c1"&gt;// using your runtime's ViewModel API.&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Rive character renders the visual mouth shape.&lt;/p&gt;

&lt;p&gt;Your application remains responsible for audio playback, timing, and speech synchronization.&lt;/p&gt;

&lt;h3&gt;
  
  
  What about emotional expressions?
&lt;/h3&gt;

&lt;p&gt;Speech animation and facial emotion should ideally be designed as separate concerns.&lt;/p&gt;

&lt;p&gt;For example, a character may need to speak while smiling.&lt;/p&gt;

&lt;p&gt;Or blink while listening.&lt;/p&gt;

&lt;p&gt;Or look toward a user while thinking.&lt;/p&gt;

&lt;p&gt;A reusable rig can support independent facial controls so that these behaviors don't unnecessarily conflict.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Gamified Mascots: Beyond AI Chat Applications
&lt;/h2&gt;

&lt;p&gt;Interactive Rive characters are also useful for applications that depend on user progress.&lt;/p&gt;

&lt;p&gt;Examples include:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fitness apps:&lt;/strong&gt; Characters can react to workouts, progress milestones, and completed goals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Habit trackers:&lt;/strong&gt; Mascots can celebrate streaks and visually acknowledge task completion.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Language-learning apps:&lt;/strong&gt; Animated companions can encourage learners and react to lesson progress.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Productivity apps:&lt;/strong&gt; Characters can respond to completed tasks, achievements, and focus sessions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Educational apps:&lt;/strong&gt; Mascots can provide visual feedback during interactive lessons.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: Level-based character evolution
&lt;/h3&gt;

&lt;p&gt;Imagine an application with ten progression levels.&lt;/p&gt;

&lt;p&gt;The character could expose a property such as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;mascotProgress&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;progress&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.75&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;mood&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;happy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your app updates these values as the user progresses.&lt;/p&gt;

&lt;p&gt;The character might gradually change its glow, energy, posture, or decorative effects.&lt;/p&gt;

&lt;p&gt;Milestone levels could activate additional visual changes.&lt;/p&gt;

&lt;p&gt;For complex progression systems, it is important to define how animations behave when the user levels up during another action.&lt;/p&gt;

&lt;p&gt;Should the evolution animation interrupt the current state?&lt;/p&gt;

&lt;p&gt;Should it finish before returning to the previous state?&lt;/p&gt;

&lt;p&gt;Should the latest requested action take priority?&lt;/p&gt;

&lt;p&gt;These are product and animation architecture decisions that should be made before development.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Common Mistakes When Building Interactive App Mascots
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Mistake 1: Treating every action as a separate video
&lt;/h3&gt;

&lt;p&gt;This can make real-time transitions difficult to manage.&lt;/p&gt;

&lt;p&gt;A reusable Rive rig and state machine can provide a more integrated solution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 2: Ignoring rapid state changes
&lt;/h3&gt;

&lt;p&gt;AI applications can change state quickly.&lt;/p&gt;

&lt;p&gt;Your animation should handle repeated triggers, interrupted actions, and unexpected updates.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 3: Mixing facial controls with body animations
&lt;/h3&gt;

&lt;p&gt;If facial expressions are unnecessarily tied to body movement, developers lose flexibility.&lt;/p&gt;

&lt;p&gt;Independent controls can make the character more reusable.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 4: Not documenting the integration contract
&lt;/h3&gt;

&lt;p&gt;Developers shouldn't need to reverse-engineer a &lt;code&gt;.riv&lt;/code&gt; file to understand its inputs.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 5: Forgetting reduced-motion behavior
&lt;/h3&gt;

&lt;p&gt;Some users prefer less motion.&lt;/p&gt;

&lt;p&gt;Applications should consider the platform's reduced-motion settings and provide an appropriate fallback experience.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mistake 6: Designing only for large screens
&lt;/h3&gt;

&lt;p&gt;An expressive character that looks great at 500 pixels might become unreadable at 48 pixels.&lt;/p&gt;

&lt;p&gt;Character silhouettes, eyes, expressions, and fine details should be evaluated at the actual sizes used in the application.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. How Much Does a Custom Interactive Rive Mascot Cost?
&lt;/h2&gt;

&lt;p&gt;The cost depends on what the character needs to do.&lt;/p&gt;

&lt;p&gt;A simple animated mascot is different from a fully interactive AI avatar with multiple states, facial controls, and lip-sync.&lt;/p&gt;

&lt;p&gt;Here are my example starting prices for larger custom projects:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Starting Price&lt;/th&gt;
&lt;th&gt;20% Offer&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Interactive AI Companion&lt;/td&gt;
&lt;td&gt;$2,500&lt;/td&gt;
&lt;td&gt;$2,000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;AI Talking Avatar System&lt;/td&gt;
&lt;td&gt;$3,500&lt;/td&gt;
&lt;td&gt;$2,800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gamified Mascot System&lt;/td&gt;
&lt;td&gt;$5,000&lt;/td&gt;
&lt;td&gt;$4,000&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;These are indicative starting prices, not fixed quotations.&lt;/p&gt;

&lt;p&gt;Smaller projects can be estimated separately.&lt;/p&gt;

&lt;p&gt;The final price depends on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Character illustration complexity&lt;/li&gt;
&lt;li&gt;Number of animations and states&lt;/li&gt;
&lt;li&gt;Facial rigging requirements&lt;/li&gt;
&lt;li&gt;Lip-sync complexity&lt;/li&gt;
&lt;li&gt;State machine architecture&lt;/li&gt;
&lt;li&gt;Integration support&lt;/li&gt;
&lt;li&gt;Delivery timeline&lt;/li&gt;
&lt;li&gt;Revision requirements&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Offer code: &lt;code&gt;MEDIUM20&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The 20% introductory discount applies to eligible new projects, subject to the agreed scope and availability.&lt;/p&gt;

&lt;h2&gt;
  
  
  10. What I Deliver to Developers
&lt;/h2&gt;

&lt;p&gt;My goal isn't simply to create a character that looks good in an animation preview.&lt;/p&gt;

&lt;p&gt;It's to deliver a character system that can be integrated into a real application.&lt;/p&gt;

&lt;p&gt;Depending on the agreed scope, deliverables can include:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Deliverable&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;.riv&lt;/code&gt; file&lt;/td&gt;
&lt;td&gt;Interactive animation asset&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Editable source&lt;/td&gt;
&lt;td&gt;Future character modifications&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;State machine&lt;/td&gt;
&lt;td&gt;Animation behavior and transitions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;ViewModel contract&lt;/td&gt;
&lt;td&gt;Application-controlled properties&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input documentation&lt;/td&gt;
&lt;td&gt;Integration reference&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Completion events&lt;/td&gt;
&lt;td&gt;One-shot action handling&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testing guide&lt;/td&gt;
&lt;td&gt;Behavior validation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integration example&lt;/td&gt;
&lt;td&gt;Developer handoff&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I also consider state priorities, reset behavior, and animation interruptions during development.&lt;/p&gt;

&lt;p&gt;These details matter when the mascot becomes part of a production application.&lt;/p&gt;

&lt;h2&gt;
  
  
  11. Should You Build the Mascot Yourself or Hire a Rive Specialist?
&lt;/h2&gt;

&lt;p&gt;If you only need a simple loading animation or a basic hover effect, building it yourself may be practical.&lt;/p&gt;

&lt;p&gt;But if your application needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A custom character design&lt;/li&gt;
&lt;li&gt;Multiple interactive states&lt;/li&gt;
&lt;li&gt;Advanced rigging&lt;/li&gt;
&lt;li&gt;Independent facial expressions&lt;/li&gt;
&lt;li&gt;Lip-sync-ready animation&lt;/li&gt;
&lt;li&gt;Reliable state transitions&lt;/li&gt;
&lt;li&gt;Progress-based evolution&lt;/li&gt;
&lt;li&gt;Developer-ready integration&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Working with a specialist can help reduce the time spent on character production and animation architecture.&lt;/p&gt;

&lt;p&gt;Your development team can focus on the application while the animation system is built around the agreed integration requirements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thoughts: Give Your AI App a Character That Actually Responds
&lt;/h2&gt;

&lt;p&gt;AI products are becoming increasingly capable.&lt;/p&gt;

&lt;p&gt;But great application experiences aren't only about what the AI can do.&lt;/p&gt;

&lt;p&gt;They're also about how the product communicates with its users.&lt;/p&gt;

&lt;p&gt;An interactive mascot can help communicate listening, thinking, speaking, progress, and success in a more expressive way.&lt;/p&gt;

&lt;p&gt;With Rive, these behaviors can become part of your application's actual interaction system rather than a collection of disconnected animations.&lt;/p&gt;

&lt;p&gt;The most important principle is simple:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Build a character that responds to your app, not just an animation that plays inside it.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Building an AI App? Ask Praneeth for a Custom Mascot Quote
&lt;/h2&gt;

&lt;p&gt;I'm &lt;strong&gt;Praneeth Kawya Thathsara&lt;/strong&gt;, a Rive animator and interactive motion designer creating production-ready interactive characters for developers and product teams.&lt;/p&gt;

&lt;p&gt;I work on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Interactive AI companions&lt;/li&gt;
&lt;li&gt;AI talking avatars&lt;/li&gt;
&lt;li&gt;SaaS product mascots&lt;/li&gt;
&lt;li&gt;Gamified app characters&lt;/li&gt;
&lt;li&gt;Fitness and educational mascots&lt;/li&gt;
&lt;li&gt;React, Flutter, and React Native animation systems&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;How much would a custom mascot cost for your app?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Send me a quick message with your app idea. I'll help you estimate the animation scope and pricing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Website:&lt;/strong&gt; &lt;a href="https://riveanimator.com" rel="noopener noreferrer"&gt;riveanimator.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Email:&lt;/strong&gt; &lt;a href="mailto:riveanimator@gmail.com?subject=Rive%20Mascot%20Quote%20-%20MEDIUM20"&gt;riveanimator@gmail.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WhatsApp:&lt;/strong&gt; &lt;a href="https://wa.me/94717000999?text=Hi%20Praneeth%21%20How%20much%20would%20a%20custom%20interactive%20Rive%20mascot%20cost%20for%20my%20app%3F%20I%20found%20you%20on%20DEV.to.%20MEDIUM20" rel="noopener noreferrer"&gt;Ask Praneeth for a quick quote&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Save 20% on eligible new projects with code &lt;code&gt;MEDIUM20&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's bring your app character to life.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Written by Praneeth Kawya Thathsara — Rive Animator &amp;amp; Interactive Motion Designer.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rive</category>
      <category>react</category>
      <category>flutter</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
