<?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: Wasey Jamal</title>
    <description>The latest articles on DEV Community by Wasey Jamal (@waseyjamal).</description>
    <link>https://dev.to/waseyjamal</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3945151%2F1b114bb4-c24e-4a03-b8b7-35662bee796b.png</url>
      <title>DEV Community: Wasey Jamal</title>
      <link>https://dev.to/waseyjamal</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/waseyjamal"/>
    <language>en</language>
    <item>
      <title>I upgraded Flutter and lost 6 hours. Here's the fix that takes 10 minutes.</title>
      <dc:creator>Wasey Jamal</dc:creator>
      <pubDate>Mon, 25 May 2026 14:54:43 +0000</pubDate>
      <link>https://dev.to/waseyjamal/i-upgraded-flutter-and-lost-6-hours-heres-the-fix-that-takes-10-minutes-3pe0</link>
      <guid>https://dev.to/waseyjamal/i-upgraded-flutter-and-lost-6-hours-heres-the-fix-that-takes-10-minutes-3pe0</guid>
      <description>&lt;p&gt;I upgraded Flutter from 3.29 to 3.41.9 for my production app — DramaHub, an OTT streaming platform with 7,500+ downloads and 2,500+ daily active users. My ad SDK (CAS) required it. What followed was 6 hours of cascading errors that all had simple fixes — if you know where to look.&lt;/p&gt;

&lt;p&gt;This article documents every error, every root cause, and every fix in the exact order they happened. If you are upgrading Flutter on Windows, bookmark this.&lt;/p&gt;




&lt;h3&gt;
  
  
  My Setup
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;App:&lt;/strong&gt; Flutter Android app with Firebase, Appodeal ads, CAS SDK&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Old Flutter:&lt;/strong&gt; 3.29 (1.5 years stable)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Target Flutter:&lt;/strong&gt; 3.41.9&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;OS:&lt;/strong&gt; Windows 10, PowerShell&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  - &lt;strong&gt;Old SDK location:&lt;/strong&gt; C:\flutter (wrong — more on this below)
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Error 1: BaseApplicationNameHandler
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;unable&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;resolve&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; 
&lt;span class="nc"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;flutter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;gradle&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;BaseApplicationNameHandler&lt;/span&gt;
&lt;span class="err"&gt;@&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;column&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.flutter.gradle.BaseApplicationNameHandler&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; &lt;code&gt;android/settings.gradle.kts&lt;/code&gt; was missing the entire &lt;code&gt;pluginManagement&lt;/code&gt; block including the &lt;code&gt;includeBuild&lt;/code&gt; line. Without it, Gradle cannot find Flutter's own Gradle plugin classes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; Your &lt;code&gt;settings.gradle.kts&lt;/code&gt; must contain this complete block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;pluginManagement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;flutterSdkPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;properties&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;util&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Properties&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"local.properties"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;inputStream&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;flutterSdkPath&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;properties&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"flutter.sdk"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flutterSdkPath&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="s"&gt;"flutter.sdk not set in local.properties"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="n"&gt;flutterSdkPath&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;includeBuild&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"$flutterSdkPath/packages/flutter_tools/gradle"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;repositories&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;google&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;mavenCentral&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;gradlePluginPortal&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;plugins&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"dev.flutter.flutter-plugin-loader"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"1.0.0"&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.android.application"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"8.11.1"&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"org.jetbrains.kotlin.android"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"2.1.0"&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="nf"&gt;id&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"com.google.gms.google-services"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;version&lt;/span&gt; &lt;span class="s"&gt;"4.4.4"&lt;/span&gt; &lt;span class="n"&gt;apply&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":app"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  Error 2: Flutter SDK Ownership (Windows Only)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;fatal: detected dubious ownership in repository at 'C:/flutter'
Unable to determine engine version
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Flutter was installed at &lt;code&gt;C:\flutter&lt;/code&gt; by an Administrator account. Your regular user account does not own it. Flutter uses Git internally and Git blocks operations on directories owned by a different user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix — do this properly, once, permanently:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Download Flutter zip from &lt;a href="https://docs.flutter.dev/install/archive" rel="noopener noreferrer"&gt;https://docs.flutter.dev/install/archive&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Extract to &lt;code&gt;C:\Users\yourname\flutter&lt;/code&gt; — NOT &lt;code&gt;C:\&lt;/code&gt; root&lt;/li&gt;
&lt;li&gt;Add to PATH: &lt;code&gt;C:\Users\yourname\flutter\bin&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Run: &lt;code&gt;git config --global --add safe.directory C:/Users/yourname/flutter&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Update &lt;code&gt;android/local.properties&lt;/code&gt;: &lt;code&gt;flutter.sdk=C:\\Users\\yourname\\flutter&lt;/code&gt;
&lt;strong&gt;Rule:&lt;/strong&gt; Never install Flutter at &lt;code&gt;C:\flutter&lt;/code&gt;. Always install in your user folder. Your user owns it, no permission issues ever again.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Error 3: Gradle Build Failed to Produce an APK
&lt;/h3&gt;

&lt;p&gt;This was the hardest one. Build says &lt;code&gt;BUILD SUCCESSFUL&lt;/code&gt;. Flutter says it cannot find the APK.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running Gradle task 'assembleDebug'... BUILD SUCCESSFUL
Error: Gradle build failed to produce an .apk file. 
It's likely that this file was generated under 
C:\Projects\myapp\build, but the tool couldn't find it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The APK was actually at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;android/app/build/outputs/flutter-apk/app-debug.apk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But Flutter expected it at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;build/app/outputs/flutter-apk/app-debug.apk
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; The root &lt;code&gt;android/build.gradle.kts&lt;/code&gt; was missing the build directory redirect block that Flutter's own project template includes. Without it, Gradle outputs to the default Android location instead of the location Flutter's tooling looks in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix — add this to &lt;code&gt;android/build.gradle.kts&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;allprojects&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;repositories&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;google&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="nf"&gt;mavenCentral&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;newBuildDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Directory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
    &lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buildDirectory&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"../../build"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buildDirectory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newBuildDir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nf"&gt;subprojects&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;newSubprojectBuildDir&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Directory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;newBuildDir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dir&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buildDirectory&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newSubprojectBuildDir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;subprojects&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluationDependsOn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":app"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;register&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Delete&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"clean"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rootProject&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;layout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;buildDirectory&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 is directly from Flutter's official project template at:&lt;br&gt;
&lt;code&gt;flutter/packages/flutter_tools/templates/app/android-kotlin.tmpl/build.gradle.kts.tmpl&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;If your project was created with an older Flutter version, this block may be missing entirely.&lt;/p&gt;


&lt;h3&gt;
  
  
  Error 4: OutOfMemoryError — Java Heap Space
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight gradle"&gt;&lt;code&gt;&lt;span class="nl"&gt;ERROR:&lt;/span&gt; &lt;span class="nl"&gt;D8:&lt;/span&gt; &lt;span class="n"&gt;java&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;OutOfMemoryError&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Java&lt;/span&gt; &lt;span class="n"&gt;heap&lt;/span&gt; &lt;span class="n"&gt;space&lt;/span&gt;
&lt;span class="n"&gt;Execution&lt;/span&gt; &lt;span class="n"&gt;failed&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="s1"&gt;':app:mergeExtDexDebug'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Root cause:&lt;/strong&gt; Large ad SDKs (Appodeal, Unity Ads, AppLovin, etc.) require more JVM memory than the default 4GB during DEX merging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix — update &lt;code&gt;android/gradle.properties&lt;/code&gt;:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;org.gradle.jvmargs&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;-Xmx8192m -XX:MaxMetaspaceSize=512m&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change &lt;code&gt;4096m&lt;/code&gt; to &lt;code&gt;8192m&lt;/code&gt;. If you have many ad network adapters, 8GB is the minimum.&lt;/p&gt;




&lt;h3&gt;
  
  
  Final Working Configuration
&lt;/h3&gt;

&lt;p&gt;Here is the complete verified working setup:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;th&gt;Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Flutter&lt;/td&gt;
&lt;td&gt;3.41.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flutter location&lt;/td&gt;
&lt;td&gt;C:\Users\yourname\flutter&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gradle&lt;/td&gt;
&lt;td&gt;8.14.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android Gradle Plugin&lt;/td&gt;
&lt;td&gt;8.11.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kotlin&lt;/td&gt;
&lt;td&gt;2.1.0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;JVM heap&lt;/td&gt;
&lt;td&gt;8192m&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;build.gradle.kts&lt;/td&gt;
&lt;td&gt;newBuildDir block present&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  Why Not Flutter 3.44?
&lt;/h3&gt;

&lt;p&gt;Flutter 3.44 was released 9 days before I wrote this. It has a confirmed Windows bug where the APK path issue persists even with the correct build.gradle.kts. I verified this personally. Until that is patched, 3.41.9 is the recommended stable version for production Android apps on Windows.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Build Command That Finally Worked
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight powershell"&gt;&lt;code&gt;&lt;span class="n"&gt;flutter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clean&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;flutter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;pub&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;get&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="n"&gt;flutter&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;apk&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;--debug&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="c"&gt;# √ Built build\app\outputs\flutter-apk\app-debug.apk&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Six hours. Four errors. All fixable. Hope this saves you time.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I build DramaHub solo — a production OTT streaming app with 7,500+ downloads and 2,500+ daily active users running at ₹0/month infrastructure cost. Follow &lt;a href="https://twitter.com/waseybuilds" rel="noopener noreferrer"&gt;@waseybuilds&lt;/a&gt; for more real production stories.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;GitHub: &lt;a href="https://github.com/waseyjamal" rel="noopener noreferrer"&gt;github.com/waseyjamal&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;







</description>
      <category>flutter</category>
      <category>android</category>
      <category>gradle</category>
      <category>testing</category>
    </item>
    <item>
      <title>I had 1000+ unread user reports in my live Flutter app — here is how I fixed the whole system</title>
      <dc:creator>Wasey Jamal</dc:creator>
      <pubDate>Sat, 23 May 2026 19:42:54 +0000</pubDate>
      <link>https://dev.to/waseyjamal/i-ignored-1000-users-for-2-months-here-is-how-i-fixed-my-flutter-apps-feedback-system-51fh</link>
      <guid>https://dev.to/waseyjamal/i-ignored-1000-users-for-2-months-here-is-how-i-fixed-my-flutter-apps-feedback-system-51fh</guid>
      <description>&lt;p&gt;DramaHub has been live on the Google Play Store since March 2026. 7,000+ downloads. 3,000+ daily active users. Built solo. ₹0/month infrastructure cost.&lt;/p&gt;

&lt;p&gt;Today I found out I had been completely ignoring my users for 2 months.&lt;/p&gt;




&lt;h2&gt;
  
  
  The original setup
&lt;/h2&gt;

&lt;p&gt;Both the "Suggest a Drama" and "Report a Problem" screens were Google Forms embedded in a Flutter WebView. Simple, fast to build, works on day one.&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="n"&gt;_controller&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WebViewController&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setJavaScriptMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JavaScriptMode&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;unrestricted&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setBackgroundColor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Colors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;white&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;loadRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Uri&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_formUrl&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Responses went to a Google Sheet I never opened. No notification. No push alert. Nothing.&lt;/p&gt;

&lt;p&gt;I had &lt;strong&gt;1000+ drama suggestions&lt;/strong&gt; and &lt;strong&gt;1000+ problem reports&lt;/strong&gt; sitting there completely unread.&lt;/p&gt;

&lt;p&gt;Users were putting in effort to give me feedback. I was not receiving any of it.&lt;/p&gt;




&lt;h2&gt;
  
  
  The problems with the WebView approach
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. White background in a dark app&lt;/strong&gt;&lt;br&gt;
Google Forms load with a white background. My entire app is dark themed — &lt;code&gt;#0D0D0D&lt;/code&gt; background. Every time a user opened these screens they got a jarring white flash. Unprofessional.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Zero notification&lt;/strong&gt;&lt;br&gt;
Responses go to a Google Sheet. Unless you manually open the sheet, you never know a submission happened.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. No communication path&lt;/strong&gt;&lt;br&gt;
Users had no way to leave contact info. I had no way to follow up. Completely one-way.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. No offline handling&lt;/strong&gt;&lt;br&gt;
If the form failed to load, users saw a blank white screen with no error message.&lt;/p&gt;


&lt;h2&gt;
  
  
  What I built instead
&lt;/h2&gt;

&lt;p&gt;I replaced both screens with &lt;strong&gt;native Flutter forms&lt;/strong&gt; — dark themed, matching the rest of the app exactly.&lt;/p&gt;

&lt;p&gt;On submit, the form data is sent directly to a &lt;strong&gt;Telegram bot&lt;/strong&gt; via the Telegram Bot API. The bot posts a formatted message to my private Telegram group instantly.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Telegram message format
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;🚨 Problem Report                    🕐 23 May 2026, 14:32
─────────────────────────────
⚠️ Type: Video not playing
🎥 Drama / Episode: Arafta Episode 56
📝 Description: Video not loading on wifi at all
─────────────────────────────
👤 Contact: @username
📱 App Version: 1.0.7
🌍 Country: IN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Full detail. Instant. Every time.&lt;/p&gt;
&lt;h3&gt;
  
  
  The TelegramService
&lt;/h3&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;TelegramService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;TelegramService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;_&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;TelegramService&lt;/span&gt; &lt;span class="n"&gt;instance&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TelegramService&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;_&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;_botToken&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;AppConfigService&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;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;telegramBotToken&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="kd"&gt;get&lt;/span&gt; &lt;span class="n"&gt;_chatId&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;AppConfigService&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;config&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;telegramChatId&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;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;sendMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&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="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;uri&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kt"&gt;Uri&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="s"&gt;'https://api.telegram.org/bot&lt;/span&gt;&lt;span class="si"&gt;$_botToken&lt;/span&gt;&lt;span class="s"&gt;/sendMessage'&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;response&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;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;uri&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nl"&gt;headers:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;'Content-Type'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'application/json'&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;jsonEncode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="s"&gt;'chat_id'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_chatId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;'text'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s"&gt;'parse_mode'&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;'HTML'&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="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;timeout&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;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;seconds:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;statusCode&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;Key design decisions:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bot token and chat ID are &lt;strong&gt;never hardcoded&lt;/strong&gt; — fetched from &lt;code&gt;app_config.json&lt;/code&gt; via Cloudflare Worker at runtime&lt;/li&gt;
&lt;li&gt;Admin can rotate the token anytime from the config — zero app update needed&lt;/li&gt;
&lt;li&gt;Uses existing &lt;code&gt;http&lt;/code&gt; package — no new dependency&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Silent data attached to every submission
&lt;/h3&gt;

&lt;p&gt;The user never sees these — they just appear in the Telegram message:&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="c1"&gt;// App version — reads from pubspec.yaml automatically&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;info&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;PackageInfo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;fromPlatform&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;appVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// "1.0.7"&lt;/span&gt;

&lt;span class="c1"&gt;// Country — from device locale, no permission needed&lt;/span&gt;
&lt;span class="kd"&gt;final&lt;/span&gt; &lt;span class="n"&gt;locale&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;WidgetsBinding&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;platformDispatcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;locale&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;country&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;locale&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;countryCode&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s"&gt;'Unknown'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// "IN"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Spam protection
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1 hour cooldown between submissions per user&lt;/span&gt;
&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;_cooldownKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;'report_problem_last_submit'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;_cooldownMinutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&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;bool&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;_isCoolingDown&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;prefs&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;SharedPreferences&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInstance&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;lastSubmit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prefs&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_cooldownKey&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;lastSubmit&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="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&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;diff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;millisecondsSinceEpoch&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;lastSubmit&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;diff&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;minutes:&lt;/span&gt; &lt;span class="n"&gt;_cooldownMinutes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;inMilliseconds&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The security mistake I made
&lt;/h2&gt;

&lt;p&gt;While setting this up, I made a mistake — I added the Telegram bot token directly to &lt;code&gt;app_config.json&lt;/code&gt; in my &lt;strong&gt;public&lt;/strong&gt; GitHub repository.&lt;/p&gt;

&lt;p&gt;GitHub sent me a secret exposure alert within minutes.&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;"telegram_bot_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;"8940932851:XX"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"telegram_chat_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;"-51505959XX"&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;Anyone with read access to the repo could see the token and control the bot.&lt;/p&gt;

&lt;h3&gt;
  
  
  How I fixed it
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Step 1 — Revoke immediately&lt;/strong&gt;&lt;br&gt;
Went to &lt;a class="mentioned-user" href="https://dev.to/botfather"&gt;@botfather&lt;/a&gt; → &lt;code&gt;/mybots&lt;/code&gt; → API Token → Revoke. New token generated in 10 seconds.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 2 — Make repo private&lt;/strong&gt;&lt;br&gt;
But wait — my Cloudflare Worker fetches from this GitHub repo to serve data to the app. Making it private would break everything.&lt;/p&gt;

&lt;p&gt;The fix: add a GitHub PAT as an &lt;strong&gt;encrypted environment variable&lt;/strong&gt; in Cloudflare Worker.&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;// In Cloudflare Worker — before:&lt;/span&gt;
&lt;span class="nx"&gt;githubResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;githubUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User-Agent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DramaHub-CDN/1.0&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;span class="c1"&gt;// After:&lt;/span&gt;
&lt;span class="nx"&gt;githubResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;githubUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User-Agent&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;DramaHub-CDN/1.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Authorization&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`token &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;env.GITHUB_TOKEN&lt;/code&gt; is stored as an encrypted secret in Cloudflare Worker settings — never in code, never visible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Step 3 — Make repo private&lt;/strong&gt;&lt;br&gt;
With the Worker authenticated, the repo can be private. The full pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Flutter App
→ Cloudflare Worker (authenticated with GITHUB_TOKEN)
→ Private GitHub repo
→ Returns app_config.json with telegram_bot_token and telegram_chat_id
→ AppConfigService loads them at runtime
→ TelegramService uses them to send messages
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zero hardcoding anywhere. Admin panel can update the token via &lt;code&gt;app_config.json&lt;/code&gt; — no app update needed.&lt;/p&gt;




&lt;h2&gt;
  
  
  The form UI
&lt;/h2&gt;

&lt;p&gt;Both screens follow the same pattern — native Flutter, fully dark themed:&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="n"&gt;TextFormField&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nl"&gt;style:&lt;/span&gt; &lt;span class="n"&gt;AppTypography&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;copyWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;AppColors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;white&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nl"&gt;cursorColor:&lt;/span&gt; &lt;span class="n"&gt;AppColors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;primaryRed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;decoration:&lt;/span&gt; &lt;span class="n"&gt;InputDecoration&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nl"&gt;filled:&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;fillColor:&lt;/span&gt; &lt;span class="n"&gt;AppColors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;cardBackground&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// #1C1C1C&lt;/span&gt;
    &lt;span class="nl"&gt;focusedBorder:&lt;/span&gt; &lt;span class="n"&gt;OutlineInputBorder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nl"&gt;borderSide:&lt;/span&gt; &lt;span class="n"&gt;BorderSide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nl"&gt;color:&lt;/span&gt; &lt;span class="n"&gt;AppColors&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;primaryRed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;width:&lt;/span&gt; &lt;span class="mf"&gt;1.5&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Success state replaces the form entirely after submission — no snackbar, no dismissable toast:&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="c1"&gt;// After successful submit:&lt;/span&gt;
&lt;span class="n"&gt;setState&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;_submitted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Build method:&lt;/span&gt;
&lt;span class="nl"&gt;body:&lt;/span&gt; &lt;span class="n"&gt;_submitted&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;_buildSuccessState&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;_buildForm&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  What changed for users
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;White Google Form in dark app&lt;/td&gt;
&lt;td&gt;Native dark themed form&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Submissions silently disappear&lt;/td&gt;
&lt;td&gt;Instant Telegram notification&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No contact option&lt;/td&gt;
&lt;td&gt;Optional Telegram/email field&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No error handling&lt;/td&gt;
&lt;td&gt;Proper offline error state&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No success feedback&lt;/td&gt;
&lt;td&gt;Full success screen&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  The real lesson
&lt;/h2&gt;

&lt;p&gt;I was so focused on building new features that I never went back to check if existing ones were actually working.&lt;/p&gt;

&lt;p&gt;A 3,000 DAU app is not a side project. Real people use it every day. They deserve to be heard.&lt;/p&gt;

&lt;p&gt;Now they are.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;DramaHub is live on Google Play Store. Built solo. ₹0/month infrastructure.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Architecture: Flutter + GitHub as database + Cloudflare Workers CDN + Firebase&lt;/em&gt;&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>buildinpublic</category>
      <category>android</category>
      <category>security</category>
    </item>
    <item>
      <title>I built a streaming app with 7,000+ downloads at ₹0/month — here's the exact architecture</title>
      <dc:creator>Wasey Jamal</dc:creator>
      <pubDate>Fri, 22 May 2026 05:50:31 +0000</pubDate>
      <link>https://dev.to/waseyjamal/i-built-a-streaming-app-with-7000-downloads-at-0month-heres-the-exact-architecture-572h</link>
      <guid>https://dev.to/waseyjamal/i-built-a-streaming-app-with-7000-downloads-at-0month-heres-the-exact-architecture-572h</guid>
      <description>&lt;p&gt;I'm Wasey, a solo developer from Varanasi, India. I built DramaHub — &lt;br&gt;
a fully live OTT streaming app on Google Play Store. 7,000+ downloads, &lt;br&gt;
1,000+ daily active users, sub-1% crash rate, ₹0/month infrastructure.&lt;/p&gt;

&lt;p&gt;Here's the exact architecture that makes it possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;I had zero budget for servers. A typical OTT backend needs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A backend server (₹3,000–15,000/month)&lt;/li&gt;
&lt;li&gt;A database (₹1,500+/month)&lt;/li&gt;
&lt;li&gt;A CDN (₹500+/month)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I had ₹0. So I had to think differently.&lt;/p&gt;

&lt;h2&gt;
  
  
  GitHub as the Database
&lt;/h2&gt;

&lt;p&gt;All drama content, episode metadata, and app config live as JSON files &lt;br&gt;
in a public GitHub repository. Free. Unlimited. Versioned automatically.&lt;/p&gt;

&lt;p&gt;Every drama looks like this in JSON:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "id": "drama_001",&lt;br&gt;
  "title": "Drama Title",&lt;br&gt;
  "thumbnail": "&lt;a href="https://cdn.jsdelivr.net/.." rel="noopener noreferrer"&gt;https://cdn.jsdelivr.net/..&lt;/a&gt;.",&lt;br&gt;
  "episodes": [&lt;br&gt;
    {&lt;br&gt;
      "ep": 1,&lt;br&gt;
      "title": "Episode 1",&lt;br&gt;
      "source": "youtube_id_here"&lt;br&gt;
    }&lt;br&gt;
  ]&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;The app fetches this JSON on launch, caches it locally, and serves &lt;br&gt;
content instantly on subsequent opens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloudflare Workers as the CDN Proxy
&lt;/h2&gt;

&lt;p&gt;Hitting GitHub raw URLs directly has rate limits. So I built a &lt;br&gt;
Cloudflare Worker that sits in front of GitHub:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Caches all JSON responses at the edge globally&lt;/li&gt;
&lt;li&gt;Handles unlimited traffic&lt;/li&gt;
&lt;li&gt;Auto-invalidates when content version changes&lt;/li&gt;
&lt;li&gt;Cost: ₹0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Architecture: Flutter app → Cloudflare Worker → GitHub raw JSON&lt;/p&gt;

&lt;h2&gt;
  
  
  The Dual Player System
&lt;/h2&gt;

&lt;p&gt;DramaHub runs two video players:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Primary — YouTube WebView player&lt;/strong&gt;&lt;br&gt;
Most content is sourced from YouTube. The app uses a WebView-based &lt;br&gt;
player with custom controls and a URL whitelist for security.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Secondary — Custom video player with Cloudflare R2&lt;/strong&gt;&lt;br&gt;
For self-hosted content, I use a custom player backed by &lt;br&gt;
Cloudflare R2 storage.&lt;/p&gt;

&lt;p&gt;Both players are switchable instantly from the admin panel via &lt;br&gt;
remote JSON config. No APK update needed — ever.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Admin Panel
&lt;/h2&gt;

&lt;p&gt;This is the most important part of the architecture.&lt;/p&gt;

&lt;p&gt;The admin panel is a Flutter Web app that communicates directly &lt;br&gt;
with the GitHub REST API using Base64 encoding and SHA-based &lt;br&gt;
conflict detection. From the admin panel I can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add/edit/delete dramas and episodes&lt;/li&gt;
&lt;li&gt;Toggle ads per screen with cooldown timers&lt;/li&gt;
&lt;li&gt;Switch between YouTube and custom player instantly&lt;/li&gt;
&lt;li&gt;Change CDN URLs without any release&lt;/li&gt;
&lt;li&gt;Force update all users&lt;/li&gt;
&lt;li&gt;Control app config globally&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything is remote. Everything is instant. No app store review cycle.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Crash That Taught Me Everything
&lt;/h2&gt;

&lt;p&gt;One day the app went completely down. Every user got a crash on launch.&lt;/p&gt;

&lt;p&gt;The reason: my Cloudflare Worker wasn't caching properly. Every request &lt;br&gt;
was hitting GitHub directly and I burned through the API rate limit &lt;br&gt;
in minutes.&lt;/p&gt;

&lt;p&gt;I fixed the Worker caching. Then I built a full backup system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Direct GitHub raw URL as fallback&lt;/li&gt;
&lt;li&gt;Backup Worker as secondary fallback&lt;/li&gt;
&lt;li&gt;Switchable from admin panel in under 60 seconds&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No APK release needed. I built that entire system in one night.&lt;/p&gt;

&lt;h2&gt;
  
  
  Firebase Integration
&lt;/h2&gt;

&lt;p&gt;Even with zero backend cost, I use Firebase for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anonymous Auth&lt;/li&gt;
&lt;li&gt;FCM push notifications with deep links&lt;/li&gt;
&lt;li&gt;Crashlytics for real-time crash monitoring&lt;/li&gt;
&lt;li&gt;Analytics with 7 custom tracked events&lt;/li&gt;
&lt;li&gt;Atomic view counter increments via Firestore REST API&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Results After 18 Months
&lt;/h2&gt;

&lt;p&gt;📦 7,000+ downloads&lt;br&gt;
👥 1,000+ daily active users&lt;br&gt;&lt;br&gt;
💥 Sub-1% crash rate&lt;br&gt;
💰 ₹0/month infrastructure cost&lt;br&gt;
📱 Live on Google Play Store&lt;/p&gt;

&lt;h2&gt;
  
  
  Key Lesson
&lt;/h2&gt;

&lt;p&gt;Build your admin panel first. Remote config is not a feature — &lt;br&gt;
it's survival. The ability to change anything without a new release &lt;br&gt;
has saved me dozens of times.&lt;/p&gt;

&lt;p&gt;GitHub as a database works at production scale if you architect &lt;br&gt;
the caching layer correctly.&lt;/p&gt;




&lt;p&gt;GitHub: github.com/waseyjamal/dramahub&lt;br&gt;
Play Store: &lt;a href="https://play.google.com/store/apps/details?id=com.dramahub.drama_hub" rel="noopener noreferrer"&gt;https://play.google.com/store/apps/details?id=com.dramahub.drama_hub&lt;/a&gt;&lt;br&gt;
Twitter: @waseybuilds&lt;/p&gt;

&lt;p&gt;Happy to answer any questions about the architecture.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>github</category>
      <category>cloudflarechallenge</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
