<?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: Kiran Patel</title>
    <description>The latest articles on DEV Community by Kiran Patel (@numorotech).</description>
    <link>https://dev.to/numorotech</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4100896%2Fc0a6d483-3c8c-47c3-9960-67b7c194457f.png</url>
      <title>DEV Community: Kiran Patel</title>
      <link>https://dev.to/numorotech</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/numorotech"/>
    <language>en</language>
    <item>
      <title>Designing a shared daily trivia set in Flutter (without an account wall)</title>
      <dc:creator>Kiran Patel</dc:creator>
      <pubDate>Sun, 30 Aug 2026 02:23:12 +0000</pubDate>
      <link>https://dev.to/numorotech/designing-a-shared-daily-trivia-set-in-flutter-without-an-account-wall-5cj4</link>
      <guid>https://dev.to/numorotech/designing-a-shared-daily-trivia-set-in-flutter-without-an-account-wall-5cj4</guid>
      <description>&lt;p&gt;I shipped a small trivia app called &lt;strong&gt;Trivia Quest&lt;/strong&gt;. The product bet is boring on purpose: &lt;strong&gt;ten questions, same set for everyone in your language today&lt;/strong&gt;, with a short explanation after most misses. No ads. Solo play needs no account.&lt;/p&gt;

&lt;p&gt;This post is about the &lt;strong&gt;Daily&lt;/strong&gt; loop — not friend rooms, not monetization experiments. If you are building a habit app, the interesting part is what you &lt;em&gt;optimize for&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The habit we wanted
&lt;/h2&gt;

&lt;p&gt;Most trivia apps I tried opened with:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create an account
&lt;/li&gt;
&lt;li&gt;Pick seventeen modes
&lt;/li&gt;
&lt;li&gt;Get scored against strangers immediately
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I wanted the opposite: open, answer ten questions, optionally read why I was wrong, close the app.&lt;/p&gt;

&lt;p&gt;That implies a few constraints:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Shared content&lt;/strong&gt; — if everyone gets a different random set, you cannot compare scores or talk about "today's question #7."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Low friction&lt;/strong&gt; — account creation is a tax on a three-minute session.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Explanations that respect attention&lt;/strong&gt; — auto-advance is off by default. You can turn on 3s / 5s / 8s in Settings, but the default assumes you might want to read.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Same ten questions for everyone (per language, per day)
&lt;/h2&gt;

&lt;p&gt;We pick Daily questions with a &lt;strong&gt;seeded random draw&lt;/strong&gt; from the published catalog for the player's locale. Same calendar day + same language ⇒ same ten questions.&lt;/p&gt;

&lt;p&gt;Why seeded random instead of a hand-curated editorial list every morning?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scale&lt;/strong&gt; — we ship thousands of questions across categories; manual curation does not scale for three locales.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fairness&lt;/strong&gt; — everyone debates the same facts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Simplicity&lt;/strong&gt; — no "editor's pick" pipeline blocking release.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Trade-off: some days feel harder than others. We accept that. Category play still exists if you want a smoother difficulty curve.&lt;/p&gt;

&lt;h2&gt;
  
  
  Content in Firestore, progress on the device
&lt;/h2&gt;

&lt;p&gt;Questions and categories live in &lt;strong&gt;Firestore&lt;/strong&gt;. Solo progress (seen question IDs, in-progress session, streaks, XP) stays &lt;strong&gt;local&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That split was deliberate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Content can update without an app release.&lt;/li&gt;
&lt;li&gt;Privacy story stays simple for solo play: nothing leaves the phone for category mode.&lt;/li&gt;
&lt;li&gt;We still use anonymous Firebase Auth only when you opt into friend rooms (separate topic).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Daily, the client asks: &lt;em&gt;what is today's seed for this locale?&lt;/em&gt; then pulls the matching set. Explanations are required on medium and hard questions in the catalog; easy may omit.&lt;/p&gt;

&lt;p&gt;Pseudocode for the idea (simplified):&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;// Same calendar day + locale =&amp;gt; same 10 question IDs&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;dailySeed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt; &lt;span class="n"&gt;localDate&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;locale&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="kt"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;localDate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;year&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;localDate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;month&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;localDate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;day&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;locale&lt;/span&gt;&lt;span class="p"&gt;);&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;QuestionId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pickDailySet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;,&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;Question&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;catalog&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;rng&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Random&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&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;catalog&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;shuffled&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;take&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&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;q&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;q&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="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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In production we filter to &lt;strong&gt;published&lt;/strong&gt; questions for that locale and dedupe against validation rules — the point is deterministic selection, not &lt;code&gt;Random()&lt;/code&gt; per user.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explanations without turning into a textbook
&lt;/h2&gt;

&lt;p&gt;The failure mode for "educational trivia" is a paragraph after every tap. We tried to keep explanations &lt;strong&gt;one beat long&lt;/strong&gt; — enough to anchor the fact, not a Wikipedia sidebar.&lt;/p&gt;

&lt;p&gt;Product rule: &lt;strong&gt;medium + hard need an explanation before publish&lt;/strong&gt;. Marketing copy says "on most questions" because easy items may skip it.&lt;/p&gt;

&lt;p&gt;Default &lt;strong&gt;auto-advance is Off&lt;/strong&gt;. Early testers who wanted speed could enable it. Making "read the explanation" the default nudged the tone we wanted.&lt;/p&gt;

&lt;h2&gt;
  
  
  What we are not optimizing yet
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Friend compete&lt;/strong&gt; exists (short room codes, RTDB for live state). It is secondary in how we talk about the app until the Daily habit feels solid. Speed scoring is fun; it is not the wedge.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Web&lt;/strong&gt; is a light entry point at &lt;a href="https://jointrivia.app" rel="noopener noreferrer"&gt;jointrivia.app&lt;/a&gt; — landing and invites, not a full client pitch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stack (if you are curious)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Flutter&lt;/strong&gt; — UI shell, Riverpod for session state; Flame on play surfaces where motion matters
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firestore&lt;/strong&gt; — categories, questions, explanations
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Firebase RTDB&lt;/strong&gt; — live room state for compete
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;FCM&lt;/strong&gt; — opt-in daily reminder topics (no token warehouse; fail-closed design)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it / tell me what breaks
&lt;/h2&gt;

&lt;p&gt;If you build consumer mobile apps, I would genuinely like feedback on one thing: &lt;strong&gt;after a wrong answer, do you read the explanation or tap Next immediately?&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Links&lt;/strong&gt; (disclosure: I'm on the team behind Trivia Quest / Numoro Tech):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://jointrivia.app" rel="noopener noreferrer"&gt;https://jointrivia.app&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Android: &lt;a href="https://play.google.com/store/apps/details?id=com.numorotech.trivia_quest" rel="noopener noreferrer"&gt;https://play.google.com/store/apps/details?id=com.numorotech.trivia_quest&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;iOS: &lt;a href="https://apps.apple.com/app/id6795744848" rel="noopener noreferrer"&gt;https://apps.apple.com/app/id6795744848&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Questions I am still thinking about&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Should Daily ever let you pick a category, or does that break the "shared set" magic?
&lt;/li&gt;
&lt;li&gt;How many questions is the right daily dose — ten felt right on paper; I watch completion rates.
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy to answer Flutter / Firestore questions in the comments.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>firebase</category>
      <category>mobile</category>
      <category>gamedev</category>
    </item>
  </channel>
</rss>
