<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Anmol's blog]]></title><description><![CDATA[Anmol's blog]]></description><link>https://blog.anmolthedeveloper.com</link><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 18:16:28 GMT</lastBuildDate><atom:link href="https://blog.anmolthedeveloper.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[The Moment My Flutter App Resumes, I Don’t Trust Anyone]]></title><description><![CDATA[If you’ve ever built a real-world Flutter app (payments, delivery, attendance, tracking, security-sensitive apps), there comes a point where “what happens when the user leaves and comes back?” really matters.
That’s exactly where didChangeAppLifecycl...]]></description><link>https://blog.anmolthedeveloper.com/the-moment-my-flutter-app-resumes-i-dont-trust-anyone</link><guid isPermaLink="true">https://blog.anmolthedeveloper.com/the-moment-my-flutter-app-resumes-i-dont-trust-anyone</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Security]]></category><dc:creator><![CDATA[Anmol Singh Tuteja]]></dc:creator><pubDate>Wed, 14 Jan 2026 08:09:54 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768378075236/1891157a-0da9-4283-b9c0-ead5afd20048.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you’ve ever built a real-world Flutter app (payments, delivery, attendance, tracking, security-sensitive apps), there comes a point where <strong>“what happens when the user leaves and comes back?”</strong> really matters.</p>
<p>That’s exactly where <code>didChangeAppLifecycleState</code> shines ✨</p>
<p>Let’s break it down in a <strong>human, practical, developer-to-developer way</strong>.</p>
<hr />
<h2 id="heading-flutter-app-lifecycle-in-simple-words">Flutter App Lifecycle — in simple words</h2>
<p>Every mobile app goes through <strong>life phases</strong>, just like us:</p>
<ul>
<li><p>App opens</p>
</li>
<li><p>App goes to background</p>
</li>
<li><p>User switches apps</p>
</li>
<li><p>Screen locks</p>
</li>
<li><p>App comes back</p>
</li>
<li><p>App gets killed</p>
</li>
</ul>
<p>Flutter exposes these phases through <strong>App Lifecycle States</strong>, and the method that listens to these changes is:</p>
<pre><code class="lang-dart">didChangeAppLifecycleState(AppLifecycleState state)
</code></pre>
<h3 id="heading-when-does-this-method-get-triggered">When does this method get triggered?</h3>
<p>This method is called <strong>automatically by Flutter</strong> whenever the app moves between these states:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>State</td><td>Meaning</td></tr>
</thead>
<tbody>
<tr>
<td><code>resumed</code></td><td>App is visible &amp; user can interact</td></tr>
<tr>
<td><code>inactive</code></td><td>App is visible but not interactive (e.g. call overlay)</td></tr>
<tr>
<td><code>paused</code></td><td>App in background</td></tr>
<tr>
<td><code>detached</code></td><td>App is still running but detached from UI</td></tr>
</tbody>
</table>
</div><p>Think of it as Flutter’s version of:</p>
<ul>
<li><p><code>onResume()</code></p>
</li>
<li><p><code>onPause()</code></p>
</li>
<li><p><code>onStop()</code></p>
</li>
</ul>
<p>from native Android.</p>
<hr />
<h2 id="heading-your-use-case-a-very-real-amp-smart-one">Your use case (a very real &amp; smart one 👏)</h2>
<p>You’re doing this 👇</p>
<pre><code class="lang-dart"><span class="hljs-meta">@override</span>
<span class="hljs-keyword">void</span> didChangeAppLifecycleState(AppLifecycleState state) <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">if</span> (state == AppLifecycleState.resumed) {
    debugPrint(<span class="hljs-string">"App resumed (Similar to onResume() in Android)"</span>);
    checkDeveloperModeAndAppPermissions();
  }
}
</code></pre>
<h3 id="heading-why-this-is-important">Why this is important</h3>
<ul>
<li><p>A user can <strong>enable Developer Options</strong></p>
</li>
<li><p>Enable <strong>Mock Location</strong></p>
</li>
<li><p>Switch back to your app</p>
</li>
<li><p>And try to cheat your system</p>
</li>
</ul>
<p>By checking again on <code>resumed</code>:</p>
<ul>
<li><p>You <strong>don’t trust the app state</strong></p>
</li>
<li><p>You <strong>re-validate security</strong></p>
</li>
<li><p>You <strong>block usage instantly if something changes</strong></p>
</li>
</ul>
<p>This is <strong>exactly how production-grade apps behave</strong>.</p>
<blockquote>
<p>Real apps never assume the environment is still safe after backgrounding.</p>
</blockquote>
<hr />
<h2 id="heading-other-powerful-real-world-use-cases">Other powerful real-world use cases 🚀</h2>
<p>Let’s go beyond the obvious.</p>
<hr />
<h3 id="heading-1-security-re-validation-banking-fintech-delivery-attendance-apps">1️⃣ Security re-validation (Banking, Fintech, Delivery, Attendance apps)</h3>
<p><strong>Use case</strong></p>
<ul>
<li><p>Developer options</p>
</li>
<li><p>Mock location</p>
</li>
<li><p>VPN detection</p>
</li>
<li><p>Root / jailbreak checks</p>
</li>
<li><p>Screen recording detection</p>
</li>
</ul>
<p><strong>Why</strong> <code>resumed</code>?<br />Because users can change <strong>any of these while your app is in background</strong>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">if</span> (state == AppLifecycleState.resumed) {
  validateAppSecurity();
}
</code></pre>
<p>This is how apps like:</p>
<ul>
<li><p>Banking apps</p>
</li>
<li><p>Ride-hailing apps</p>
</li>
<li><p>Attendance systems</p>
</li>
</ul>
<p>protect themselves.</p>
<hr />
<h3 id="heading-2-auto-logout-after-inactivity">2️⃣ Auto logout after inactivity 🔐</h3>
<p>Very common in professional apps.</p>
<p><strong>Flow</strong></p>
<ul>
<li><p>User sends app to background</p>
</li>
<li><p>App stays paused for X minutes</p>
</li>
<li><p>User comes back → force login</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">if</span> (state == AppLifecycleState.paused) {
  lastPausedTime = <span class="hljs-built_in">DateTime</span>.now();
}

<span class="hljs-keyword">if</span> (state == AppLifecycleState.resumed) {
  <span class="hljs-keyword">if</span> (<span class="hljs-built_in">DateTime</span>.now().difference(lastPausedTime) &gt; <span class="hljs-built_in">Duration</span>(minutes: <span class="hljs-number">10</span>)) {
    logoutUser();
  }
}
</code></pre>
<p>This improves <strong>security + compliance</strong>.</p>
<hr />
<h3 id="heading-3-refresh-stale-data-without-annoying-reloads">3️⃣ Refresh stale data (without annoying reloads)</h3>
<p>Instead of refreshing data every time a page opens:</p>
<ul>
<li><p>User switches apps</p>
</li>
<li><p>Comes back after some time</p>
</li>
<li><p>Data might be outdated</p>
</li>
</ul>
<p>Perfect moment to:</p>
<ul>
<li><p>Refresh dashboard</p>
</li>
<li><p>Sync messages</p>
</li>
<li><p>Re-fetch order status</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">if</span> (state == AppLifecycleState.resumed) {
  refreshLatestData();
}
</code></pre>
<p>This feels <strong>smart</strong>, not aggressive.</p>
<hr />
<h3 id="heading-4-pause-amp-resume-things-properly-media-games-maps">4️⃣ Pause &amp; resume things properly (Media, Games, Maps)</h3>
<p><strong>Paused state</strong></p>
<ul>
<li><p>Stop animations</p>
</li>
<li><p>Pause video/audio</p>
</li>
<li><p>Stop GPS updates</p>
</li>
<li><p>Stop heavy streams</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">if</span> (state == AppLifecycleState.paused) {
  audioPlayer.pause();
  locationStream.cancel();
}
</code></pre>
<p><strong>Resumed state</strong></p>
<ul>
<li>Resume only what’s needed</li>
</ul>
<p>This saves:</p>
<ul>
<li><p>Battery 🔋</p>
</li>
<li><p>Data 📶</p>
</li>
<li><p>CPU 🧠</p>
</li>
</ul>
<hr />
<h3 id="heading-5-handle-permission-changes-gracefully">5️⃣ Handle permission changes gracefully</h3>
<p>Classic scenario:</p>
<ul>
<li><p>App asks for permission</p>
</li>
<li><p>User goes to settings</p>
</li>
<li><p>Enables it</p>
</li>
<li><p>Comes back</p>
</li>
</ul>
<p>You <strong>should not ask again</strong> — you should <strong>re-check silently</strong>.</p>
<pre><code class="lang-dart"><span class="hljs-keyword">if</span> (state == AppLifecycleState.resumed) {
  checkPermissions();
}
</code></pre>
<p>This creates a <strong>polished UX</strong>.</p>
<hr />
<h3 id="heading-6-analytics-amp-user-behavior-tracking">6️⃣ Analytics &amp; user behavior tracking 📊</h3>
<p>Apps track:</p>
<ul>
<li><p>When users leave</p>
</li>
<li><p>When they return</p>
</li>
<li><p>How often they background the app</p>
</li>
</ul>
<pre><code class="lang-dart"><span class="hljs-keyword">if</span> (state == AppLifecycleState.paused) {
  analytics.logEvent(<span class="hljs-string">"app_backgrounded"</span>);
}

<span class="hljs-keyword">if</span> (state == AppLifecycleState.resumed) {
  analytics.logEvent(<span class="hljs-string">"app_resumed"</span>);
}
</code></pre>
<p>This helps product teams understand:</p>
<ul>
<li><p>Engagement</p>
</li>
<li><p>Drop-offs</p>
</li>
<li><p>Session duration</p>
</li>
</ul>
<hr />
<h2 id="heading-how-new-developers-should-use-this-step-by-step">How new developers should use this (step-by-step)</h2>
<h3 id="heading-1-add-observer">1️⃣ Add observer</h3>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyAppState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">MyApp</span>&gt;
    <span class="hljs-title">with</span> <span class="hljs-title">WidgetsBindingObserver</span> </span>{

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    <span class="hljs-keyword">super</span>.initState();
    WidgetsBinding.instance.addObserver(<span class="hljs-keyword">this</span>);
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> dispose() {
    WidgetsBinding.instance.removeObserver(<span class="hljs-keyword">this</span>);
    <span class="hljs-keyword">super</span>.dispose();
  }
}
</code></pre>
<p>👉 <strong>Always remove the observer</strong> (very important).</p>
<hr />
<h3 id="heading-2-override-lifecycle-method">2️⃣ Override lifecycle method</h3>
<pre><code class="lang-dart"><span class="hljs-meta">@override</span>
<span class="hljs-keyword">void</span> didChangeAppLifecycleState(AppLifecycleState state) {
  <span class="hljs-keyword">switch</span> (state) {
    <span class="hljs-keyword">case</span> AppLifecycleState.resumed:
      <span class="hljs-comment">// App came back</span>
      <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> AppLifecycleState.paused:
      <span class="hljs-comment">// App in background</span>
      <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> AppLifecycleState.inactive:
      <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> AppLifecycleState.detached:
      <span class="hljs-keyword">break</span>;
  }
}
</code></pre>
<hr />
<h2 id="heading-best-practices-learned-the-hard-way">Best practices (learned the hard way 😅)</h2>
<p>✅ <strong>Keep logic light</strong></p>
<ul>
<li><p>Don’t do heavy API calls blindly</p>
</li>
<li><p>Use debounce / flags if needed</p>
</li>
</ul>
<p>✅ <strong>Never trust previous state</strong></p>
<ul>
<li>Always re-check permissions, security, auth</li>
</ul>
<p>✅ <strong>Avoid UI updates if widget is disposed</strong></p>
<ul>
<li>Especially in async calls</li>
</ul>
<p>✅ <strong>Centralize logic</strong></p>
<ul>
<li><p>Call services like:</p>
<ul>
<li><p><code>SecurityService</code></p>
</li>
<li><p><code>AuthService</code></p>
</li>
<li><p><code>PermissionService</code></p>
</li>
</ul>
</li>
</ul>
<p>Instead of dumping logic in the widget.</p>
<hr />
<h2 id="heading-final-thoughts">Final thoughts</h2>
<p><code>didChangeAppLifecycleState</code> is not just a lifecycle hook.</p>
<p>It’s your app’s <strong>awareness system</strong>.</p>
<ul>
<li><p>Security</p>
</li>
<li><p>Performance</p>
</li>
<li><p>UX</p>
</li>
<li><p>Trust</p>
</li>
<li><p>Professionalism</p>
</li>
</ul>
<p>The moment you start using it correctly, your app stops behaving like a demo and starts behaving like a <strong>real product</strong>.</p>
<p>And your use case of blocking users when Developer Options are enabled?<br />That’s not overengineering — that’s <strong>production thinking</strong> 💪</p>
]]></content:encoded></item><item><title><![CDATA[🎨 Flutter Theme Magic: Light, Dark, System — and Your Own Beautiful Brand]]></title><description><![CDATA[🌟 A tiny story to start
Meet Lumi (loves light mode) and Nyx (prefers dark). They share one Flutter app.Lumi wants bright cards and cheerful blues; Nyx wants calm grays and a cozy dark background.Your job as the developer? One app, two moods, zero d...]]></description><link>https://blog.anmolthedeveloper.com/flutter-theme-magic-light-dark-system-and-your-own-beautiful-brand</link><guid isPermaLink="true">https://blog.anmolthedeveloper.com/flutter-theme-magic-light-dark-system-and-your-own-beautiful-brand</guid><category><![CDATA[Flutter]]></category><category><![CDATA[theme]]></category><category><![CDATA[app development]]></category><category><![CDATA[Apple]]></category><category><![CDATA[Android]]></category><dc:creator><![CDATA[Anmol Singh Tuteja]]></dc:creator><pubDate>Sun, 05 Oct 2025 07:18:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759648593248/cc65246b-5fc7-4094-813a-04fa5e751596.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-a-tiny-story-to-start">🌟 A tiny story to start</h1>
<p>Meet <strong>Lumi</strong> (loves light mode) and <strong>Nyx</strong> (prefers dark). They share one Flutter app.<br />Lumi wants bright cards and cheerful blues; Nyx wants calm grays and a cozy dark background.<br />Your job as the developer? <strong>One app, two moods, zero duplication.</strong><br />That’s what <strong>theming</strong> is for.</p>
<p>We’ll build a clean, modern theme system that:</p>
<ul>
<li><p>switches between <strong>light / dark / system</strong> modes,</p>
</li>
<li><p>centralizes colors, typography, shapes, paddings,</p>
</li>
<li><p>supports <strong>quick toggles</strong> and <strong>persistence</strong>, and</p>
</li>
<li><p>scales with <strong>Theme Extensions</strong> for pro-level customization.</p>
</li>
</ul>
<hr />
<h2 id="heading-theme-101-what-actually-is-a-theme">🧠 Theme 101: What actually is a Theme?</h2>
<ul>
<li><p><code>ThemeData</code>: the big bag of app visuals (colors, typography, shapes, etc.).</p>
</li>
<li><p><code>ColorScheme</code>: the modern, Material 3–style color palette. Prefer this!</p>
</li>
<li><p><code>ThemeMode</code>: chooses <strong>light</strong>, <strong>dark</strong>, or <strong>system</strong>.</p>
</li>
<li><p><code>Theme.of(context)</code>: read current theme.</p>
</li>
<li><p><code>AnimatedTheme</code>: smoothly animate theme changes.</p>
</li>
<li><p><code>ThemeExtension</code>: add <strong>your own</strong> themeable tokens (e.g., brand spacing).</p>
</li>
</ul>
<hr />
<h2 id="heading-project-skeleton">🏗️ Project skeleton</h2>
<p>We’ll use <strong>Material 3</strong> with a <code>ColorScheme</code>, add a quick toggle, and persist the choice.</p>
<pre><code class="lang-plaintext">import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  final prefs = await SharedPreferences.getInstance();
  final saved = prefs.getString('themeMode') ?? 'system';
  final initialMode = _parseThemeMode(saved);

  runApp(AppRoot(initialMode: initialMode, prefs: prefs));
}

ThemeMode _parseThemeMode(String s) =&gt; switch (s) {
  'light' =&gt; ThemeMode.light,
  'dark' =&gt; ThemeMode.dark,
  _ =&gt; ThemeMode.system,
};

class AppRoot extends StatefulWidget {
  const AppRoot({super.key, required this.initialMode, required this.prefs});
  final ThemeMode initialMode;
  final SharedPreferences prefs;

  @override
  State&lt;AppRoot&gt; createState() =&gt; _AppRootState();
}

class _AppRootState extends State&lt;AppRoot&gt; {
  late ThemeMode _mode = widget.initialMode;

  Future&lt;void&gt; _setMode(ThemeMode m) async {
    setState(() =&gt; _mode = m);
    await widget.prefs.setString('themeMode', switch (m) {
      ThemeMode.light =&gt; 'light',
      ThemeMode.dark =&gt; 'dark',
      ThemeMode.system =&gt; 'system',
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Lumi &amp; Nyx',
      debugShowCheckedModeBanner: false,
      themeMode: _mode,
      theme: AppTheme.light,
      darkTheme: AppTheme.dark,
      home: HomeScreen(
        mode: _mode,
        onChangeMode: _setMode,
      ),
    );
  }
}
</code></pre>
<p><strong>What’s happening here</strong></p>
<ul>
<li><p>We load <code>SharedPreferences</code> to restore the last chosen <code>ThemeMode</code>.</p>
</li>
<li><p><code>MaterialApp</code> receives <strong>three</strong> knobs: <code>theme</code>, <code>darkTheme</code>, and <code>themeMode</code>.</p>
</li>
<li><p>Calling <code>_setMode</code> updates UI <strong>and</strong> persists the choice.</p>
</li>
</ul>
<hr />
<h2 id="heading-your-design-tokens-colorscheme-first-material-3">🎯 Your design tokens: ColorScheme-first (Material 3)</h2>
<pre><code class="lang-plaintext">class AppTheme {
  // Brand seeds
  static const _seed = Color(0xFF4F46E5); // Indigo-ish
  static const _error = Color(0xFFB00020);

  static final ColorScheme _lightScheme = ColorScheme.fromSeed(
    seedColor: _seed,
    brightness: Brightness.light,
    error: _error,
  );

  static final ColorScheme _darkScheme = ColorScheme.fromSeed(
    seedColor: _seed,
    brightness: Brightness.dark,
    error: _error,
  );

  static ThemeData get light =&gt; ThemeData(
        useMaterial3: true,
        colorScheme: _lightScheme,
        scaffoldBackgroundColor: _lightScheme.background,
        appBarTheme: AppBarTheme(
          backgroundColor: _lightScheme.surface,
          foregroundColor: _lightScheme.onSurface,
          elevation: 0,
          centerTitle: false,
        ),
        textTheme: _textTheme(_lightScheme),
        cardTheme: CardTheme(
          elevation: 0,
          margin: const EdgeInsets.all(12),
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(16),
          ),
        ),
        extensions: const &lt;ThemeExtension&lt;dynamic&gt;&gt;[
          AppSpacing.compact,
        ],
      );

  static ThemeData get dark =&gt; ThemeData(
        useMaterial3: true,
        colorScheme: _darkScheme,
        scaffoldBackgroundColor: _darkScheme.background,
        appBarTheme: AppBarTheme(
          backgroundColor: _darkScheme.surface,
          foregroundColor: _darkScheme.onSurface,
          elevation: 0,
          centerTitle: false,
        ),
        textTheme: _textTheme(_darkScheme),
        cardTheme: CardTheme(
          elevation: 0,
          margin: const EdgeInsets.all(12),
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(16),
          ),
        ),
        extensions: const &lt;ThemeExtension&lt;dynamic&gt;&gt;[
          AppSpacing.compact,
        ],
      );

  static TextTheme _textTheme(ColorScheme scheme) {
    return Typography.material2021().black.apply(
      bodyColor: scheme.onBackground,
      displayColor: scheme.onBackground,
    );
  }
}
</code></pre>
<p><strong>What’s happening</strong></p>
<ul>
<li><p><code>ColorScheme.fromSeed</code> gives you a complete palette (primary, secondary, surface, background, etc.) for <strong>light</strong> and <strong>dark</strong>.</p>
</li>
<li><p>We tune AppBar, Card, and Text with that scheme.</p>
</li>
<li><p>We already hint at <strong>ThemeExtension</strong> for spacing (next section).</p>
</li>
</ul>
<hr />
<h2 id="heading-pro-move-themeextension-for-your-brand-tokens">➕ Pro move: ThemeExtension for your brand tokens</h2>
<p>Add anything you want to theme (spacing, radii, shadows) — strongly typed.</p>
<pre><code class="lang-plaintext">@immutable
class AppSpacing extends ThemeExtension&lt;AppSpacing&gt; {
  final double xs, sm, md, lg, xl;
  const AppSpacing({
    required this.xs,
    required this.sm,
    required this.md,
    required this.lg,
    required this.xl,
  });

  static const compact = AppSpacing(xs: 4, sm: 8, md: 12, lg: 16, xl: 24);

  @override
  AppSpacing copyWith({double? xs, double? sm, double? md, double? lg, double? xl}) {
    return AppSpacing(
      xs: xs ?? this.xs,
      sm: sm ?? this.sm,
      md: md ?? this.md,
      lg: lg ?? this.lg,
      xl: xl ?? this.xl,
    );
  }

  @override
  AppSpacing lerp(ThemeExtension&lt;AppSpacing&gt;? other, double t) {
    if (other is! AppSpacing) return this;
    double lerpDouble(double a, double b) =&gt; a + (b - a) * t;
    return AppSpacing(
      xs: lerpDouble(xs, other.xs),
      sm: lerpDouble(sm, other.sm),
      md: lerpDouble(md, other.md),
      lg: lerpDouble(lg, other.lg),
      xl: lerpDouble(xl, other.xl),
    );
  }
}

extension SpacingX on BuildContext {
  AppSpacing get space =&gt; Theme.of(this).extension&lt;AppSpacing&gt;()!;
}
</code></pre>
<p><strong>Usage in widgets</strong></p>
<pre><code class="lang-plaintext">Padding(
  padding: EdgeInsets.all(context.space.lg),
  child: const Text('Hello, tokens 👋'),
)
</code></pre>
<hr />
<h2 id="heading-quick-theme-switching-ui-with-animation">⚡ Quick theme switching UI (with animation)</h2>
<pre><code class="lang-plaintext">class HomeScreen extends StatelessWidget {
  const HomeScreen({super.key, required this.mode, required this.onChangeMode});
  final ThemeMode mode;
  final ValueChanged&lt;ThemeMode&gt; onChangeMode;

  @override
  Widget build(BuildContext context) {
    final scheme = Theme.of(context).colorScheme;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Lumi &amp; Nyx'),
        actions: [
          PopupMenuButton&lt;ThemeMode&gt;(
            icon: const Icon(Icons.light_mode),
            initialValue: mode,
            onSelected: onChangeMode,
            itemBuilder: (context) =&gt; const [
              PopupMenuItem(value: ThemeMode.system, child: Text('System')),
              PopupMenuItem(value: ThemeMode.light, child: Text('Light')),
              PopupMenuItem(value: ThemeMode.dark, child: Text('Dark')),
            ],
          ),
        ],
      ),
      body: AnimatedTheme(
        duration: const Duration(milliseconds: 250),
        data: Theme.of(context),
        child: Center(
          child: Card(
            color: scheme.surface,
            child: Padding(
              padding: EdgeInsets.all(context.space.xl),
              child: Text(
                'This card adapts to theme!',
                style: Theme.of(context).textTheme.titleLarge,
              ),
            ),
          ),
        ),
      ),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () {
          final next = switch (mode) {
            ThemeMode.system =&gt; ThemeMode.light,
            ThemeMode.light =&gt; ThemeMode.dark,
            ThemeMode.dark =&gt; ThemeMode.system,
          };
          onChangeMode(next);
        },
        label: Text('Mode: ${mode.name}'),
        icon: const Icon(Icons.auto_mode),
      ),
    );
  }
}
</code></pre>
<p><strong>What’s happening</strong></p>
<ul>
<li><p>A <strong>menu</strong> for explicit selection and a <strong>FAB</strong> for quick cycling.</p>
</li>
<li><p><code>AnimatedTheme</code> gives a smooth fade between palettes.</p>
</li>
</ul>
<hr />
<h2 id="heading-custom-fonts-amp-typography-bonus-polish">🖍️ Custom fonts &amp; typography (bonus polish)</h2>
<ol>
<li>Add font to <code>pubspec.yaml</code> (short form):</li>
</ol>
<pre><code class="lang-plaintext">flutter:
  uses-material-design: true
  fonts:
    - family: Inter
      fonts:
        - asset: assets/fonts/Inter-Regular.ttf
        - asset: assets/fonts/Inter-Medium.ttf
          weight: 500
        - asset: assets/fonts/Inter-SemiBold.ttf
          weight: 600
</code></pre>
<ol start="2">
<li>Apply to <code>ThemeData</code>:</li>
</ol>
<pre><code class="lang-plaintext">static TextTheme _textTheme(ColorScheme scheme) {
  final base = Typography.material2021().black;
  return base.apply(
    fontFamily: 'Inter',
    bodyColor: scheme.onBackground,
    displayColor: scheme.onBackground,
  );
}
</code></pre>
<blockquote>
<p>Tip: If you need a different type scale in dark mode, make a second builder.</p>
</blockquote>
<hr />
<h2 id="heading-respect-the-system-theme-automatically">🖥️ Respect the <strong>system</strong> theme automatically</h2>
<ul>
<li><p>Using <code>ThemeMode.system</code> will follow iOS/Android/Mac/Windows settings.</p>
</li>
<li><p>You can also query <strong>current platform brightness</strong>:</p>
</li>
</ul>
<pre><code class="lang-plaintext">final platformBrightness = MediaQuery.of(context).platformBrightness;
final isDark = platformBrightness == Brightness.dark;
</code></pre>
<blockquote>
<p>Don’t mix manual toggles with this unless you <strong>persist</strong> the choice and clearly indicate what’s active.</p>
</blockquote>
<hr />
<h2 id="heading-using-theme-correctly-inside-widgets">🧪 Using theme correctly inside widgets</h2>
<h3 id="heading-do">✅ Do</h3>
<pre><code class="lang-plaintext">final cs = Theme.of(context).colorScheme;

Container(
  decoration: BoxDecoration(
    color: cs.surface,
    borderRadius: BorderRadius.circular(16),
    boxShadow: kElevationToShadow[1],
  ),
  child: Text('Hello', style: Theme.of(context).textTheme.bodyLarge),
);
</code></pre>
<h3 id="heading-avoid">❌ Avoid</h3>
<pre><code class="lang-plaintext">// Hardcoding breaks dark mode and brand consistency:
Container(color: Colors.white);
Text('Hello', style: TextStyle(color: Colors.black));
</code></pre>
<hr />
<h2 id="heading-status-bar-amp-system-overlays-important-detail">🧯 Status bar &amp; system overlays (important detail)</h2>
<p>To guarantee readable status-bar icons per theme:</p>
<pre><code class="lang-plaintext">AppBar(
  systemOverlayStyle: Theme.of(context).brightness == Brightness.dark
      ? SystemUiOverlayStyle.light
      : SystemUiOverlayStyle.dark,
);
</code></pre>
<p>Or wrap a custom area with <code>AnnotatedRegion&lt;SystemUiOverlayStyle&gt;</code>.</p>
<hr />
<h2 id="heading-theming-cupertino-widgets-when-needed">🧩 Theming Cupertino widgets (when needed)</h2>
<p>If you use Cupertino widgets, wrap them with <code>CupertinoTheme</code> or rely on <code>MaterialApp</code> bridging:</p>
<pre><code class="lang-plaintext">CupertinoTheme(
  data: CupertinoThemeData(
    brightness: Theme.of(context).brightness,
    primaryColor: Theme.of(context).colorScheme.primary,
  ),
  child: const CupertinoButton(child: Text('iOS vibe'), onPressed: null),
);
</code></pre>
<hr />
<h2 id="heading-multiple-brand-themes-eg-blue-vs-green">🗂️ Multiple brand themes (e.g., “Blue” vs “Green”)</h2>
<p>Create multiple <code>ColorScheme</code>s and switch at runtime exactly like <code>ThemeMode</code>, or store them as <strong>theme presets</strong>:</p>
<pre><code class="lang-plaintext">enum Brand { indigo, green }

final brandThemes = &lt;Brand, ThemeData&gt;{
  Brand.indigo: AppTheme.light,
  Brand.green: AppTheme.light.copyWith(
    colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF0EA5A5)),
  ),
};
</code></pre>
<p>Now you can toggle <code>brandThemes[currentBrand]!</code> with the same persistence approach.</p>
<hr />
<h2 id="heading-common-pitfalls-amp-fixes">🧱 Common pitfalls &amp; fixes</h2>
<ul>
<li><p><strong>Hardcoded colors</strong> → always pull from <code>Theme.of(context).colorScheme</code>.</p>
</li>
<li><p><strong>Forgetting darkTheme</strong> → light theme bleeds into dark devices. Provide both.</p>
</li>
<li><p><strong>Using</strong> <code>ThemeMode.dark</code> without user control → frustrates users. Offer a toggle.</p>
</li>
<li><p><strong>Text contrast issues</strong> → rely on <code>onSurface</code>, <code>onBackground</code>, etc., from <code>ColorScheme</code>.</p>
</li>
<li><p><strong>No animation</strong> → use <code>AnimatedTheme</code> for smoothness (tiny but delightful).</p>
</li>
<li><p><strong>Global tokens scattered</strong> → centralize in <code>ThemeExtension</code>s.</p>
</li>
</ul>
<hr />
<h2 id="heading-checklist-done-right-theming">🧭 Checklist: “Done right” theming</h2>
<ul>
<li><p>Material 3 with <code>ColorScheme</code>.</p>
</li>
<li><p>Both <code>theme</code> and <code>darkTheme</code>.</p>
</li>
<li><p><code>themeMode</code> persisted (<code>SharedPreferences</code> fine).</p>
</li>
<li><p>Quick toggle + system option.</p>
</li>
<li><p>Fonts applied via <code>TextTheme</code>.</p>
</li>
<li><p>Brand tokens via <code>ThemeExtension</code>.</p>
</li>
<li><p>Animated transitions.</p>
</li>
<li><p>Correct status bar icon colors.</p>
</li>
<li><p>No hardcoded colors in widgets.</p>
</li>
</ul>
<hr />
<h2 id="heading-key-takeaways">🧠 Key takeaways</h2>
<ul>
<li><p><strong>ThemeMode</strong> controls <em>which</em> theme: <strong>light/dark/system</strong>.</p>
</li>
<li><p><strong>ColorScheme</strong> is the modern source of truth — build from a <strong>seed</strong>.</p>
</li>
<li><p><strong>ThemeExtension</strong> lets you add <strong>your own design tokens</strong> (spacing, radii, shadows…).</p>
</li>
<li><p><strong>Persist</strong> user choice and <strong>animate</strong> changes for polish.</p>
</li>
<li><p>Always read colors/typography from <strong>Theme</strong> — not hardcoded values.</p>
</li>
</ul>
]]></content:encoded></item><item><title><![CDATA[🏭 Dart’s Secret Factory: Understanding Private Constructors, Factory Methods & Private Functions]]></title><description><![CDATA[✨ The Story of DartLand — Where Classes Guard Their Secrets
Once upon a time in DartLand, there was a clever engineer named Darty 🧑‍💻.He loved building things — boxes, shapes, utilities, you name it.
But chaos soon spread.People were misusing his c...]]></description><link>https://blog.anmolthedeveloper.com/darts-secret-factory-understanding-private-constructors-factory-methods-and-private-functions</link><guid isPermaLink="true">https://blog.anmolthedeveloper.com/darts-secret-factory-understanding-private-constructors-factory-methods-and-private-functions</guid><category><![CDATA[Dart]]></category><category><![CDATA[constructor]]></category><category><![CDATA[factory method]]></category><dc:creator><![CDATA[Anmol Singh Tuteja]]></dc:creator><pubDate>Sun, 05 Oct 2025 06:41:34 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759646400205/4c98864e-63f1-417f-a3db-f4b235ccd1ad.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-the-story-of-dartland-where-classes-guard-their-secrets">✨ The Story of DartLand — Where Classes Guard Their Secrets</h2>
<p>Once upon a time in DartLand, there was a clever engineer named <strong>Darty</strong> 🧑‍💻.<br />He loved building things — boxes, shapes, utilities, you name it.</p>
<p>But chaos soon spread.<br />People were misusing his classes — calling internal methods, creating random objects, and breaking stuff. 😩</p>
<p>So Darty locked some doors 🔒 using <strong>private constructors</strong> and <strong>private functions</strong>,<br />and built an organized <strong>factory</strong> 🏭 to decide what to create and when.</p>
<p>That’s the story we’re diving into today — a story of control, clarity, and clever design.</p>
<hr />
<h2 id="heading-step-1-constructors-the-builders-of-objects">🧱 Step 1: Constructors — The Builders of Objects</h2>
<p>A <strong>constructor</strong> in Dart is like a recipe for creating an object.</p>
<pre><code class="lang-plaintext">class ToyBox {
  ToyBox() {
    print('A new ToyBox is created!');
  }
}

void main() {
  var box = ToyBox();
}
</code></pre>
<p>Every time you call <code>ToyBox()</code>, a new toy box pops out 🎁.<br />But sometimes, you don’t want people to create objects freely — you want control.</p>
<hr />
<h2 id="heading-step-2-the-secret-door-private-constructors">🔒 Step 2: The Secret Door — Private Constructors</h2>
<p>A <strong>private constructor</strong> (written as <code>ClassName._()</code>) hides the class’s creation logic from outsiders.</p>
<pre><code class="lang-plaintext">class ToyBox {
  ToyBox._(); // private constructor

  static final ToyBox _instance = ToyBox._();

  factory ToyBox() =&gt; _instance;
}
</code></pre>
<p>Now, no one outside the file can write <code>ToyBox._()</code>.<br />Only the class itself can do that — perfect for <strong>Singletons</strong>, <strong>utility classes</strong>, or <strong>restricted instantiation</strong>.</p>
<hr />
<h2 id="heading-step-3-private-constructors-for-utility-or-static-only-classes">🧩 Step 3: Private Constructors for Utility or Static-Only Classes</h2>
<p>This is one of the <strong>most practical uses</strong> of private constructors —<br />when you want a class to be <strong>a namespace</strong> for static methods or constants, and <strong>never instantiated</strong>.</p>
<p>Let’s say you have a math utility:</p>
<pre><code class="lang-plaintext">class MathUtils {
  MathUtils._(); // Prevents instantiation

  static const double pi = 3.14159;

  static double areaOfCircle(double radius) =&gt; pi * radius * radius;
}

void main() {
  print(MathUtils.areaOfCircle(5)); // ✅ Works
  // var m = MathUtils(); ❌ Error: Constructor is private
}
</code></pre>
<h3 id="heading-why-do-this">🧠 Why do this?</h3>
<p>Because:</p>
<ul>
<li><p>You <strong>don’t want</strong> someone doing <code>MathUtils()</code> — that makes no sense.</p>
</li>
<li><p>The class acts as a <strong>static container</strong>, grouping related utilities together.</p>
</li>
<li><p>The private constructor says:</p>
<blockquote>
<p>“Hey, I’m not meant to be instantiated — just use my static stuff!”</p>
</blockquote>
</li>
</ul>
<p>✅ You’ll see this pattern everywhere — <code>Colors</code>, <code>Icons</code>, <code>ThemeData</code>, etc. in Flutter do the same thing.</p>
<hr />
<h2 id="heading-step-4-the-factory-constructor-smart-object-creation">🏭 Step 4: The Factory Constructor — Smart Object Creation</h2>
<p>Now, let’s talk about the <strong>factory constructor</strong> — a “smart” way to create or return objects.</p>
<p>It’s like having a “factory gate” that decides what happens when someone calls your class.</p>
<pre><code class="lang-plaintext">class ToyBox {
  static final ToyBox _instance = ToyBox._();

  ToyBox._(); // private

  factory ToyBox() {
    print('Welcome to the Toy Factory!');
    return _instance; // same box every time
  }
}

void main() {
  var box1 = ToyBox();
  var box2 = ToyBox();

  print(box1 == box2); // true ✅
}
</code></pre>
<p>💡 <strong>Factory constructors don’t have to return a new instance</strong> every time.<br />They can:</p>
<ul>
<li><p>Return the same instance (Singleton)</p>
</li>
<li><p>Return a subclass</p>
</li>
<li><p>Or even return something completely different</p>
</li>
</ul>
<hr />
<h2 id="heading-step-5-factory-constructor-returning-different-classes">🧙 Step 5: Factory Constructor Returning Different Classes</h2>
<p>A factory constructor can behave like a smart switchboard.</p>
<pre><code class="lang-plaintext">abstract class Shape {
  factory Shape(String type) {
    if (type == 'circle') return Circle();
    if (type == 'square') return Square();
    throw 'Unknown shape type';
  }
}

class Circle implements Shape {}
class Square implements Shape {}

void main() {
  var s = Shape('circle');
  print(s.runtimeType); // Circle ✅
}
</code></pre>
<p>You call <code>Shape('circle')</code>, but the factory decides <strong>which type</strong> of shape you actually get.<br />This is a great example of <strong>encapsulation</strong> — hiding logic and returning clean results.</p>
<hr />
<h2 id="heading-step-6-private-functions-the-hidden-helpers">🧰 Step 6: Private Functions — The Hidden Helpers</h2>
<p>Private functions are internal workers of your class.<br />They’re not meant to be accessed from outside — they keep your logic modular and safe.</p>
<pre><code class="lang-plaintext">class PasswordValidator {
  bool isValid(String password) {
    return _hasMinLength(password) &amp;&amp; _hasUppercase(password);
  }

  bool _hasMinLength(String password) =&gt; password.length &gt;= 8;
  bool _hasUppercase(String password) =&gt; password.contains(RegExp(r'[A-Z]'));
}
</code></pre>
<p>You expose a single clear method — <code>isValid()</code> —<br />while <code>_hasMinLength</code> and <code>_hasUppercase</code> do the behind-the-scenes work.</p>
<p>Outside code can’t call these private helpers.<br />They’re like the backstage crew of your app — invisible, but essential 🎬.</p>
<hr />
<h2 id="heading-real-world-use-cases">⚡ Real-World Use Cases</h2>
<p>Let’s connect all this to real Flutter and Dart examples 👇</p>
<h3 id="heading-1-singleton-for-services-or-configs">1️⃣ Singleton for Services or Configs</h3>
<p>You want one single instance across the app.</p>
<pre><code class="lang-plaintext">class DatabaseManager {
  static final DatabaseManager _instance = DatabaseManager._();

  DatabaseManager._(); // private

  factory DatabaseManager() =&gt; _instance;

  void connect() =&gt; print('Connected to DB!');
}
</code></pre>
<p>Every call to <code>DatabaseManager()</code> returns the same instance.</p>
<hr />
<h3 id="heading-2-factory-for-fromjson-objects">2️⃣ Factory for fromJson Objects</h3>
<p>Factories are perfect for data models that parse API responses.</p>
<pre><code class="lang-plaintext">class User {
  final String name;
  final int age;

  User._(this.name, this.age);

  factory User.fromJson(Map&lt;String, dynamic&gt; json) {
    return User._(json['name'], json['age']);
  }
}
</code></pre>
<p>Later, if you add caching or subclassing, your users’ code doesn’t break —<br />the factory hides all complexity.</p>
<hr />
<h3 id="heading-3-private-constructor-for-utility-classes">3️⃣ Private Constructor for Utility Classes</h3>
<p>Static-only utilities shouldn’t be instantiated:</p>
<pre><code class="lang-plaintext">class DateUtils {
  DateUtils._(); // prevents instantiation

  static String today() =&gt; DateTime.now().toIso8601String();
}
</code></pre>
<p>Calling <code>DateUtils()</code> makes no sense — and this pattern enforces that cleanly.</p>
<hr />
<h3 id="heading-4-private-functions-for-internal-logic">4️⃣ Private Functions for Internal Logic</h3>
<pre><code class="lang-plaintext">class EmailValidator {
  bool isValid(String email) =&gt; _containsAtSymbol(email) &amp;&amp; _isProperLength(email);

  bool _containsAtSymbol(String email) =&gt; email.contains('@');
  bool _isProperLength(String email) =&gt; email.length &gt;= 6;
}
</code></pre>
<p>Your public API stays tidy. The details remain hidden — encapsulation at its best.</p>
<hr />
<h2 id="heading-best-practices">🧭 Best Practices</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Concept</td><td>Use Case</td><td>Example</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Private Constructor</strong></td><td>Hide creation or enforce static-only</td><td><code>ClassName._()</code></td></tr>
<tr>
<td><strong>Factory Constructor</strong></td><td>Smart or conditional object creation</td><td><code>factory ClassName()</code></td></tr>
<tr>
<td><strong>Private Functions</strong></td><td>Hide internal helper logic</td><td><code>_methodName()</code></td></tr>
<tr>
<td><strong>Static Utility Class</strong></td><td>Group static functions/constants</td><td><code>Utils._()</code> + static methods</td></tr>
<tr>
<td><strong>Naming Factories</strong></td><td>Alternate constructors</td><td><code>.fromJson()</code>, <code>.cached()</code></td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-real-life-analogy">🍩 Real-Life Analogy</h2>
<p>Imagine a <strong>bakery</strong> 🍰:</p>
<ul>
<li><p>The <strong>private constructor</strong> is the <strong>secret recipe</strong> — only the baker knows it.</p>
</li>
<li><p>The <strong>factory constructor</strong> is the <strong>counter</strong> — it decides whether to give you a fresh cake, a pre-made one, or none at all.</p>
</li>
<li><p>The <strong>private functions</strong> are the <strong>kitchen helpers</strong> — they do the heavy work, but customers never see them.</p>
</li>
<li><p>The <strong>static-only class</strong> is like the <strong>menu board</strong> — you don’t buy the board, you just <em>read</em> it.</p>
</li>
</ul>
<hr />
<h2 id="heading-key-takeaways">✨ Key Takeaways</h2>
<ul>
<li><p><strong>Private constructors</strong> control how (or if) a class can be instantiated.<br />  → Perfect for Singletons and utility classes.</p>
</li>
<li><p><strong>Factory constructors</strong> let you control <em>what</em> instance is returned.<br />  → Great for caching, type selection, and lazy creation.</p>
</li>
<li><p><strong>Private functions</strong> keep your internal logic hidden and APIs clean.<br />  → The heart of encapsulation.</p>
</li>
<li><p><strong>Static-only classes</strong> use private constructors to prevent misuse.<br />  → You just use their static methods or constants.</p>
</li>
</ul>
<hr />
<h2 id="heading-final-thought">💬 Final Thought</h2>
<p>Private constructors and factory methods aren’t just “syntax tricks.”<br />They’re design patterns that give your code <strong>discipline</strong>, <strong>clarity</strong>, and <strong>power</strong> —<br />ensuring your Dart classes behave exactly how you intend.</p>
<blockquote>
<p>“Good code hides what should be hidden,<br />and exposes only what’s meant to be used.” 🧠✨</p>
</blockquote>
]]></content:encoded></item><item><title><![CDATA[🎨 Beyond Widgets: Crafting Truly Custom UIs in Flutter Like a Pro]]></title><description><![CDATA[Ever felt like Flutter's widget system, as powerful as it is, just couldn't keep up with your wild design ideas? You're not alone.

Hi there! I'm a Flutter developer who's spent years working deep in the trenches of custom UI design. I’ve built every...]]></description><link>https://blog.anmolthedeveloper.com/beyond-widgets-crafting-truly-custom-uis-in-flutter-like-a-pro</link><guid isPermaLink="true">https://blog.anmolthedeveloper.com/beyond-widgets-crafting-truly-custom-uis-in-flutter-like-a-pro</guid><category><![CDATA[Flutter]]></category><category><![CDATA[UI]]></category><category><![CDATA[Flutter Widgets]]></category><dc:creator><![CDATA[Anmol Singh Tuteja]]></dc:creator><pubDate>Thu, 22 May 2025 11:23:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747912937993/11ff49c5-c1f1-43eb-98c3-322225cf201a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Ever felt like Flutter's widget system, as powerful as it is, just couldn't keep up with your wild design ideas? You're not alone.</p>
</blockquote>
<p>Hi there! I'm a Flutter developer who's spent years working deep in the trenches of custom UI design. I’ve built everything from e-commerce apps to real-time dashboards—and sometimes, the rich set of built-in widgets just didn’t cut it. So today, I’m going to take you on a fun journey: what to do <strong>when conventional widgets simply aren’t enough</strong>.</p>
<p>Let’s get our hands dirty and build something truly unique from scratch. I’ll also throw in my perspective as a technical writer and marketer to keep things engaging and clear.</p>
<hr />
<h2 id="heading-the-problem-with-just-composing-widgets">🚧 The Problem with Just Composing Widgets</h2>
<p>Flutter’s widget composition model is amazing—it lets you stack, wrap, align, animate, and react with elegance. But sometimes, you want to:</p>
<ul>
<li><p>Draw smooth, fluid curves that dance with your user’s touch.</p>
</li>
<li><p>Build a custom graph, waveform, or progress arc.</p>
</li>
<li><p>Layout children in a completely non-standard way (think radial menus or fish-eye lists).</p>
</li>
<li><p>Build game-like UIs that demand pixel-perfect control.</p>
</li>
</ul>
<p>For these use cases, you need to go <em>beyond widgets</em>.</p>
<hr />
<h2 id="heading-level-1-custompaint-amp-custompainter-drawing-with-freedom">🛠️ Level 1: CustomPaint &amp; CustomPainter — Drawing with Freedom</h2>
<h3 id="heading-what-is-it">What is it?</h3>
<p>This is Flutter’s first step into the world of drawing things <em>your way</em>. You get access to a <code>Canvas</code> and <code>Paint</code> object, similar to how you’d draw in native platforms.</p>
<h3 id="heading-lets-draw-a-wave-background">Let’s draw a wave background</h3>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WavePainter</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">CustomPainter</span> </span>{
  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> paint(Canvas canvas, Size size) {
    <span class="hljs-keyword">final</span> paint = Paint()
      ..color = Colors.blueAccent
      ..style = PaintingStyle.fill;

    <span class="hljs-keyword">final</span> path = Path()
      ..moveTo(<span class="hljs-number">0</span>, size.height * <span class="hljs-number">0.8</span>)
      ..quadraticBezierTo(
        size.width * <span class="hljs-number">0.5</span>,
        size.height,
        size.width,
        size.height * <span class="hljs-number">0.8</span>,
      )
      ..lineTo(size.width, <span class="hljs-number">0</span>)
      ..lineTo(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>)
      ..close();

    canvas.drawPath(path, paint);
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">bool</span> shouldRepaint(CustomPainter oldDelegate) =&gt; <span class="hljs-keyword">false</span>;
}
</code></pre>
<h3 id="heading-using-it">Using it:</h3>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WaveHeader</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatelessWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> CustomPaint(
      painter: WavePainter(),
      child: Container(height: <span class="hljs-number">200</span>),
    );
  }
}
</code></pre>
<p><strong>Why use this?</strong> You get to define any shape, color, gradient, shadow, or combination. Want your app’s top bar to look like a mountain range or sine wave? Easy.</p>
<hr />
<h2 id="heading-level-2-renderbox-when-you-want-total-control">⚙️ Level 2: RenderBox — When You Want Total Control</h2>
<p>When even CustomPaint isn’t enough—when you want to control <em>layout</em>, <em>gesture hit testing</em>, and <em>painting</em>—you step into Flutter’s lower-level rendering layer.</p>
<h3 id="heading-whoa-this-is-advanced-should-i-be-scared">Whoa, this is advanced. Should I be scared?</h3>
<p>Not at all! Think of <code>RenderBox</code> as building a custom engine inside your car. It’s powerful, and with a little guidance, totally manageable.</p>
<h3 id="heading-lets-build-a-green-box-that-draws-itself">Let’s build a green box that draws itself</h3>
<pre><code class="lang-dart"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyRenderBox</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">RenderBox</span> </span>{
  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> performLayout() {
    size = constraints.constrain(Size(<span class="hljs-number">200</span>, <span class="hljs-number">100</span>));
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> paint(PaintingContext context, Offset offset) {
    <span class="hljs-keyword">final</span> canvas = context.canvas;
    <span class="hljs-keyword">final</span> paint = Paint()..color = Colors.green;
    canvas.drawRect(offset &amp; size, paint);
  }
}
</code></pre>
<p>Now, we’ll wrap this into a widget:</p>
<pre><code class="lang-kotlin"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GreenBox</span> <span class="hljs-title">extends</span> <span class="hljs-title">LeafRenderObjectWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  RenderObject createRenderObject(BuildContext context) {
    <span class="hljs-keyword">return</span> MyRenderBox();
  }
}
</code></pre>
<p>Drop <code>GreenBox()</code> anywhere in your widget tree, and you’ve got a render-layer object doing its own thing.</p>
<hr />
<h2 id="heading-bonus-add-interaction">🎨 Bonus: Add Interaction</h2>
<p>You can also handle gestures at this level by overriding:</p>
<pre><code class="lang-dart"><span class="hljs-meta">@override</span>
<span class="hljs-built_in">bool</span> hitTestSelf(Offset position) =&gt; <span class="hljs-keyword">true</span>;

<span class="hljs-meta">@override</span>
<span class="hljs-keyword">void</span> handleEvent(PointerEvent event, HitTestEntry entry) {
  <span class="hljs-keyword">if</span> (event <span class="hljs-keyword">is</span> PointerDownEvent) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">"Touched at \${event.position}"</span>);
  }
}
</code></pre>
<hr />
<h2 id="heading-level-3-scenebuilder-and-ultra-low-level-apis">🌟 Level 3: SceneBuilder and Ultra-Low-Level APIs</h2>
<p>If you want to dive even deeper into Flutter's rendering internals—think native platform integration or fully custom rendering pipelines—then <code>SceneBuilder</code> is your playground.</p>
<h3 id="heading-when-to-use-scenebuilder">When to use SceneBuilder:</h3>
<ul>
<li><p>You’re building a plugin or embedding Flutter in another engine.</p>
</li>
<li><p>You want ultra-high-performance rendering without any widget overhead.</p>
</li>
<li><p>You’re working on advanced graphics like shaders, animations, or hybrid compositions.</p>
</li>
</ul>
<p>This is rarely needed for day-to-day apps, but it's available when you need it. It’s also how Flutter internally builds the final frame before pushing it to the GPU.</p>
<hr />
<h2 id="heading-tools-that-support-custom-ui-development">🔧 Tools That Support Custom UI Development</h2>
<p>Creating custom UIs is a craft—and like any craft, you need the right tools:</p>
<ul>
<li><p><strong>Flutter DevTools</strong>: Inspect layout, performance, and rendering.</p>
</li>
<li><p><strong>CanvasKit (Web)</strong>: For pixel-perfect control on web builds.</p>
</li>
<li><p><strong>ShaderMask &amp; FragmentProgram</strong>: Bring GPU shaders into your app.</p>
</li>
<li><p><strong>Gestures &amp; HitTestBehavior</strong>: Manage fine-grained user input.</p>
</li>
<li><p><strong>Flame Engine</strong>: A lightweight game engine built on Flutter for game-like or animated UIs.</p>
</li>
<li><p><strong>AnimationController &amp; CustomPaint</strong>: For animating your drawings.</p>
</li>
</ul>
<p>These tools are your paintbrushes, chisels, and compasses as you craft experiences that break away from the mold.</p>
<hr />
<h2 id="heading-so-when-should-you-go-custom">🚀 So When Should You Go Custom?</h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Situation</td><td>Solution</td></tr>
</thead>
<tbody>
<tr>
<td>Custom shapes or visuals?</td><td><code>CustomPainter</code></td></tr>
<tr>
<td>Full control over layout + paint + touch?</td><td><code>RenderBox</code></td></tr>
<tr>
<td>Simple tweaks to existing widget visuals?</td><td>Compose or extend widgets</td></tr>
<tr>
<td>Game-like or graph-heavy UI?</td><td>Consider <code>CustomPainter</code>, <code>RenderBox</code>, or <code>Flame</code></td></tr>
<tr>
<td>Rendering without widgets?</td><td><code>SceneBuilder</code></td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-real-world-examples">💡 Real-World Examples</h2>
<ul>
<li><p><strong>Fitness App Progress Rings</strong> — Use <code>CustomPaint</code> for arcs.</p>
</li>
<li><p><strong>Voice Recorder Waveform</strong> — Real-time drawing with <code>Canvas</code>.</p>
</li>
<li><p><strong>Interactive Mind Map</strong> — RenderBox with custom hit testing.</p>
</li>
<li><p><strong>Design Tool UI (like Figma)</strong> — You’ll need both <code>RenderBox</code> and <code>CustomPaint</code>.</p>
</li>
<li><p><strong>Flutter Game UI</strong> — Consider Flame or low-level scene rendering.</p>
</li>
</ul>
<hr />
<h2 id="heading-final-thoughts-from-a-flutter-dev-amp-marketer">✍️ Final Thoughts from a Flutter Dev &amp; Marketer</h2>
<p>Creating custom UIs isn’t just about showing off—it’s about <strong>creating unique, delightful experiences</strong>. It’s your secret weapon to make your app stand out. As a marketer, I know people <em>remember design</em>. As a developer, I know it’s easier than it looks when you know what tools to use.</p>
<p>Flutter gives you all the freedom you need—you just need to step off the beaten path now and then.</p>
<p>So go on, build that fancy radial menu, glowing progress orb, or wavy onboarding screen. And if you get stuck, just remember: under all those widgets, Flutter is a canvas—and <strong>you</strong> are the artist.</p>
<hr />
<p>Happy painting! 🎨</p>
<p><em>Got an idea for a wild UI and want help building it? Drop it in the comments or reach out—I’d love to help!</em></p>
]]></content:encoded></item><item><title><![CDATA[Flutter to Native: The Secret Sauce of Platform Channels (with Pizza!)]]></title><description><![CDATA[In the magical land of Flutter, everything feels perfect. Widgets flutter, animations flow, and UIs look crisp across devices. But there’s one small problem…
Flutter can’t talk to native Android or iOS code directly.
It’s like being in a group chat w...]]></description><link>https://blog.anmolthedeveloper.com/flutter-to-native-the-secret-sauce-of-platform-channels-with-pizza</link><guid isPermaLink="true">https://blog.anmolthedeveloper.com/flutter-to-native-the-secret-sauce-of-platform-channels-with-pizza</guid><category><![CDATA[Flutter]]></category><category><![CDATA[native]]></category><category><![CDATA[Android]]></category><dc:creator><![CDATA[Anmol Singh Tuteja]]></dc:creator><pubDate>Thu, 15 May 2025 05:48:23 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747288009112/7229b3ef-ed0a-4278-9ffa-9431ab66089e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<hr />
<p>In the magical land of Flutter, everything feels perfect. Widgets flutter, animations flow, and UIs look crisp across devices. But there’s one small problem…</p>
<p>Flutter can’t talk to native Android or iOS code directly.</p>
<p>It’s like being in a group chat where everyone else speaks Kotlin or Swift, and Flutter’s there like, “Anyone speak Dart?” No response. Just confused stares.</p>
<p>But Flutter, being the clever creature it is, came up with a brilliant idea—<strong>Platform Channels</strong>—the secret tunnel that lets Dart code talk to native code like old friends sharing pizza.</p>
<hr />
<h2 id="heading-what-is-a-platform-channel"><strong>What is a Platform Channel?</strong></h2>
<p>Imagine Flutter and Native (Android/iOS) living in separate houses across the street. They don’t speak the same language, but they can pass messages through paper airplanes.</p>
<p>Those paper airplanes are Platform Channels—<strong>a way for Flutter to send and receive messages to and from native code.</strong></p>
<p>There are three kinds of messengers in this system:</p>
<ul>
<li><p><strong>MethodChannel</strong> – The one-time task messenger</p>
</li>
<li><p><strong>BasicMessageChannel</strong> – The casual chatty buddy</p>
</li>
<li><p><strong>EventChannel</strong> – The non-stop DJ streaming updates from native to Flutter</p>
</li>
</ul>
<p>Let’s meet them.</p>
<hr />
<h2 id="heading-methodchannel-the-task-doer"><strong>MethodChannel – The Task Doer</strong></h2>
<p>Think of MethodChannel as a personal assistant. You give a task, and expect a result.</p>
<blockquote>
<p>Flutter: “Hey Android, what’s the battery level?”<br />Android: “Checking... it’s 78%.”<br />Flutter: “Thanks, buddy.”</p>
</blockquote>
<p>That’s MethodChannel. You ask once, get an answer, done.</p>
<h3 id="heading-flutter-dart"><strong>Flutter (Dart):</strong></h3>
<pre><code class="lang-dart"><span class="hljs-keyword">const</span> platform = MethodChannel(<span class="hljs-string">'samples.flutter.dev/battery'</span>);

Future&lt;<span class="hljs-keyword">void</span>&gt; getBatteryLevel() <span class="hljs-keyword">async</span> {
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">final</span> <span class="hljs-built_in">int</span> result = <span class="hljs-keyword">await</span> platform.invokeMethod(<span class="hljs-string">'getBatteryLevel'</span>);
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Battery is <span class="hljs-subst">$result</span>%.'</span>);
  } <span class="hljs-keyword">catch</span> (e) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Error: <span class="hljs-subst">$e</span>'</span>);
  }
}
</code></pre>
<h3 id="heading-android-kotlin"><strong>Android (Kotlin):</strong></h3>
<pre><code class="lang-kotlin">MethodChannel(flutterEngine.dartExecutor.binaryMessenger, <span class="hljs-string">"samples.flutter.dev/battery"</span>)
  .setMethodCallHandler { call, result -&gt;
    <span class="hljs-keyword">if</span> (call.method == <span class="hljs-string">"getBatteryLevel"</span>) {
        <span class="hljs-keyword">val</span> batteryLevel = getBatteryLevel()
        result.success(batteryLevel)
    } <span class="hljs-keyword">else</span> {
        result.notImplemented()
    }
}
</code></pre>
<p>Use this when Flutter needs to say:<br /><strong>"Do this once and let me know."</strong></p>
<hr />
<h2 id="heading-basicmessagechannel-the-chill-conversationalist"><strong>BasicMessageChannel – The Chill Conversationalist</strong></h2>
<p>BasicMessageChannel is more like two buddies texting each other casually.</p>
<blockquote>
<p>Flutter: “Hey, just checking in!”<br />Android: “All good here. You?”<br />Flutter: “Living the widget life.”</p>
</blockquote>
<p>It’s perfect for <strong>flexible, bi-directional messaging</strong> without commands.</p>
<h3 id="heading-flutter-dart-1"><strong>Flutter (Dart):</strong></h3>
<pre><code class="lang-dart"><span class="hljs-keyword">const</span> channel = BasicMessageChannel&lt;<span class="hljs-built_in">String</span>&gt;(
  <span class="hljs-string">'samples.flutter.dev/message'</span>, StringCodec());

<span class="hljs-keyword">void</span> sendMessage() {
  channel.send(<span class="hljs-string">'Hello Native!'</span>);
}

<span class="hljs-keyword">void</span> receiveMessages() {
  channel.setMessageHandler((message) <span class="hljs-keyword">async</span> {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'From Native: <span class="hljs-subst">$message</span>'</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-string">'Message received!'</span>;
  });
}
</code></pre>
<h3 id="heading-android-kotlin-1"><strong>Android (Kotlin):</strong></h3>
<pre><code class="lang-kotlin">channel = BasicMessageChannel(
  flutterEngine.dartExecutor.binaryMessenger,
  <span class="hljs-string">"samples.flutter.dev/message"</span>,
  StringCodec.INSTANCE
)

channel.setMessageHandler { message, reply -&gt;
  println(<span class="hljs-string">"Flutter says: <span class="hljs-variable">$message</span>"</span>)
  reply.reply(<span class="hljs-string">"Hey Flutter, I got your message!"</span>)
}
</code></pre>
<p>Use this when you need <strong>ongoing communication</strong> and don’t want the pressure of a command–response format.</p>
<hr />
<h2 id="heading-eventchannel-the-party-dj"><strong>EventChannel – The Party DJ</strong></h2>
<p>Now meet EventChannel—Flutter’s connection to the <strong>non-stop stream of updates</strong>.</p>
<p>It’s like a DJ at a party who keeps sending beats to the dancefloor (Flutter). Once Flutter tunes in, the music (data) flows continuously until someone pulls the plug.</p>
<blockquote>
<p>Flutter: “Hey DJ Native, start the stream!”<br />Native: “Here comes the vibe… 98 BPM, 99 BPM, 100 BPM…”</p>
</blockquote>
<h3 id="heading-flutter-dart-2"><strong>Flutter (Dart):</strong></h3>
<pre><code class="lang-dart"><span class="hljs-keyword">const</span> eventChannel = EventChannel(<span class="hljs-string">'samples.flutter.dev/sensor'</span>);

<span class="hljs-keyword">void</span> listenToSensor() {
  eventChannel.receiveBroadcastStream().listen((event) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Sensor update: <span class="hljs-subst">$event</span>'</span>);
  }, onError: (error) {
    <span class="hljs-built_in">print</span>(<span class="hljs-string">'Error: <span class="hljs-subst">$error</span>'</span>);
  });
}
</code></pre>
<h3 id="heading-android-kotlin-2"><strong>Android (Kotlin):</strong></h3>
<pre><code class="lang-kotlin">EventChannel(flutterEngine.dartExecutor.binaryMessenger, <span class="hljs-string">"samples.flutter.dev/sensor"</span>)
  .setStreamHandler(<span class="hljs-keyword">object</span> : EventChannel.StreamHandler {
    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onListen</span><span class="hljs-params">(arguments: <span class="hljs-type">Any</span>?, events: <span class="hljs-type">EventChannel</span>.<span class="hljs-type">EventSink</span>)</span></span> {
        startSendingSensorData(events)
    }

    <span class="hljs-keyword">override</span> <span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">onCancel</span><span class="hljs-params">(arguments: <span class="hljs-type">Any</span>?)</span></span> {
        stopSensorUpdates()
    }
})
</code></pre>
<p>Use this when native code needs to <strong>continuously push data</strong> to Flutter: sensor readings, location updates, audio level, etc.</p>
<hr />
<h2 id="heading-pizza-analogy-time"><strong>Pizza Analogy Time!</strong></h2>
<p>Let’s say Flutter and Native are trying to decide dinner plans:</p>
<ul>
<li><p><strong>MethodChannel</strong>: “Can you order a pizza?” → One request, one response</p>
</li>
<li><p><strong>BasicMessageChannel</strong>: “I’m thinking pizza. You?” → Chill chat, both talk freely</p>
</li>
<li><p><strong>EventChannel</strong>: “Start the pizza livestream!” → A constant stream of pizza updates until you say stop</p>
</li>
</ul>
<p>Delicious, right?</p>
<hr />
<h2 id="heading-quick-comparison-table"><strong>Quick Comparison Table</strong></h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Channel Type</td><td>Acts Like...</td><td>Purpose</td><td>Direction</td></tr>
</thead>
<tbody>
<tr>
<td>MethodChannel</td><td>Task request</td><td>One-time command &amp; response</td><td>Bi-directional</td></tr>
<tr>
<td>BasicMessageChannel</td><td>Text conversation</td><td>Flexible, two-way data exchange</td><td>Bi-directional</td></tr>
<tr>
<td>EventChannel</td><td>Party DJ</td><td>Streaming continuous updates</td><td>Native → Flutter</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Platform Channels are Flutter’s passport to the native world. Whether you’re trying to open the camera, stream gyroscope data, or simply get the battery level—there’s a channel for that.</p>
<ul>
<li><p>Use <strong>MethodChannel</strong> for quick commands</p>
</li>
<li><p>Use <strong>BasicMessageChannel</strong> for free-flow messages</p>
</li>
<li><p>Use <strong>EventChannel</strong> when the data never stops coming</p>
</li>
</ul>
<p>With these tools in your pocket, you can build apps that not only look beautiful but also <em>do</em> powerful things behind the scenes.</p>
<p>So the next time you’re building with Flutter and need to speak to the native side—just pass a note through the tunnel.</p>
<p>And maybe order some pizza while you're at it.</p>
]]></content:encoded></item><item><title><![CDATA[🎨 Flutter Picasso: Painting Widgets from Scratch with CustomPaint & GestureDetector]]></title><description><![CDATA["Once upon a runtime, in the land of Widgets and Pixels, a developer had a dream — to draw outside the box... literally."


✨ The Awakening of the Flutter Picasso
Meet Aryan — a curious developer with a spark in his eyes and too many Container() widg...]]></description><link>https://blog.anmolthedeveloper.com/flutter-picasso-painting-widgets-from-scratch-with-custompaint-and-gesturedetector</link><guid isPermaLink="true">https://blog.anmolthedeveloper.com/flutter-picasso-painting-widgets-from-scratch-with-custompaint-and-gesturedetector</guid><category><![CDATA[Flutter]]></category><category><![CDATA[canvas]]></category><category><![CDATA[CustomPaint]]></category><dc:creator><![CDATA[Anmol Singh Tuteja]]></dc:creator><pubDate>Thu, 15 May 2025 05:33:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1747287027281/b370e760-a726-45fd-852d-b257799ec05a.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p><em>"Once upon a runtime, in the land of Widgets and Pixels, a developer had a dream — to draw outside the box... literally."</em></p>
</blockquote>
<hr />
<h3 id="heading-the-awakening-of-the-flutter-picasso">✨ The Awakening of the Flutter Picasso</h3>
<p>Meet Aryan — a curious developer with a spark in his eyes and too many <code>Container()</code> widgets in his codebase.</p>
<p>One fine Monday morning (because bugs usually sleep on Mondays), Aryan stared at his screen and sighed, <em>“I’m tired of square widgets. I want to draw stuff. Real stuff. Like circles, triangles... maybe a unicorn.”</em></p>
<p>His inner artist, long suppressed under layers of <code>ListView.builder()</code>, whispered: <strong>“Use the brush, Aryan. Use the</strong> <code>CustomPaint</code>.”</p>
<hr />
<h3 id="heading-what-is-custompaint">🎯 What is CustomPaint?</h3>
<p>Think of <code>CustomPaint</code> as your blank canvas in Flutter. It doesn’t assume anything. It doesn’t judge. It just says:</p>
<blockquote>
<p>“Here’s a canvas, boss. Go nuts!”</p>
</blockquote>
<p>It gives you low-level access to the drawing board — whether it’s lines, shapes, or even doodling a stickman eating pizza (hey, it’s your canvas).</p>
<hr />
<h3 id="heading-lets-paint-building-a-custom-circle-drawer">🖌️ Let’s Paint: Building a Custom Circle Drawer</h3>
<p>Imagine we’re creating a custom widget where the user can tap to draw circles on the screen. Yes. Like a toddler with crayons — only in code.</p>
<hr />
<h4 id="heading-step-1-the-canvas-whisperer-custompainter">Step 1: The Canvas Whisperer (CustomPainter)</h4>
<pre><code class="lang-dart">CirclePainter <span class="hljs-keyword">extends</span> CustomPainter {
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;Offset&gt; points;

  CirclePainter(<span class="hljs-keyword">this</span>.points);

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> paint(Canvas canvas, Size size) {
    <span class="hljs-keyword">final</span> paint = Paint()
      ..color = Colors.blue
      ..style = PaintingStyle.fill;

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">final</span> point <span class="hljs-keyword">in</span> points) {
      canvas.drawCircle(point, <span class="hljs-number">20.0</span>, paint);
    }
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">bool</span> shouldRepaint(CirclePainter oldDelegate) {
    <span class="hljs-keyword">return</span> oldDelegate.points != points;
  }
}
</code></pre>
<hr />
<h4 id="heading-step-2-the-masterpiece-frame-custompaint-gesturedetector">Step 2: The Masterpiece Frame (CustomPaint + GestureDetector)</h4>
<pre><code class="lang-dart">CircleCanvas <span class="hljs-keyword">extends</span> StatefulWidget {
  <span class="hljs-meta">@override</span>
  _CircleCanvasState createState() =&gt; _CircleCanvasState();
}

_CircleCanvasState <span class="hljs-keyword">extends</span> State&lt;CircleCanvas&gt; {
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;Offset&gt; _points = [];

  <span class="hljs-keyword">void</span> _addPoint(TapDownDetails details) {
    setState(() {
      _points.add(details.localPosition);
    });
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> GestureDetector(
      onTapDown: _addPoint,
      child: CustomPaint(
        painter: CirclePainter(_points),
        child: Container(
          color: Colors.white,
          height: <span class="hljs-built_in">double</span>.infinity,
          width: <span class="hljs-built_in">double</span>.infinity,
        ),
      ),
    );
  }
}
</code></pre>
<hr />
<h2 id="heading-part-2-level-up-drawing-like-a-pro-with-signature-pad-amp-animations">🚀 PART 2: Level Up – Drawing Like a Pro (With Signature Pad &amp; Animations!)</h2>
<p>Aryan felt powerful.</p>
<p>Tapping to draw circles? Done.<br />But his inner artist wasn’t satisfied.</p>
<p><em>“What if I could draw freehand?”</em><br /><em>“What if I could animate it?”</em><br /><em>“What if... I could build a signature pad and launch my own DocuSign clone?”</em></p>
<p>Enter: <strong>Pan gestures and animation magic.</strong></p>
<hr />
<h3 id="heading-freehand-drawing-like-a-smooth-criminal">✍️ Freehand Drawing Like a Smooth Criminal</h3>
<p>To draw freely as the user drags their finger — you switch from <code>onTapDown</code> to <code>onPanUpdate</code>.</p>
<h4 id="heading-step-1-update-your-painter">Step 1: Update Your Painter</h4>
<pre><code class="lang-dart">PathPainter <span class="hljs-keyword">extends</span> CustomPainter {
  <span class="hljs-keyword">final</span> <span class="hljs-built_in">List</span>&lt;Offset&gt; points;

  PathPainter(<span class="hljs-keyword">this</span>.points);

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> paint(Canvas canvas, Size size) {
    <span class="hljs-keyword">final</span> paint = Paint()
      ..color = Colors.black
      ..strokeWidth = <span class="hljs-number">3.0</span>
      ..strokeCap = StrokeCap.round;

    <span class="hljs-keyword">for</span> (<span class="hljs-built_in">int</span> i = <span class="hljs-number">0</span>; i &lt; points.length - <span class="hljs-number">1</span>; i++) {
      <span class="hljs-keyword">if</span> (points[i] != <span class="hljs-keyword">null</span> &amp;&amp; points[i + <span class="hljs-number">1</span>] != <span class="hljs-keyword">null</span>) {
        canvas.drawLine(points[i], points[i + <span class="hljs-number">1</span>], paint);
      }
    }
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-built_in">bool</span> shouldRepaint(PathPainter oldDelegate) =&gt; <span class="hljs-keyword">true</span>;
}
</code></pre>
<h4 id="heading-step-2-handle-drag-with-gesturedetector">Step 2: Handle Drag with GestureDetector</h4>
<pre><code class="lang-dart">FreeDrawCanvas <span class="hljs-keyword">extends</span> StatefulWidget {
  <span class="hljs-meta">@override</span>
  _FreeDrawCanvasState createState() =&gt; _FreeDrawCanvasState();
}

_FreeDrawCanvasState <span class="hljs-keyword">extends</span> State&lt;FreeDrawCanvas&gt; {
  <span class="hljs-built_in">List</span>&lt;Offset&gt; _points = [];

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> GestureDetector(
      onPanUpdate: (details) {
        setState(() {
          RenderBox renderBox = context.findRenderObject() <span class="hljs-keyword">as</span> RenderBox;
          _points.add(renderBox.globalToLocal(details.globalPosition));
        });
      },
      onPanEnd: (details) {
        _points.add(<span class="hljs-keyword">null</span>); <span class="hljs-comment">// Marks end of stroke</span>
      },
      child: CustomPaint(
        painter: PathPainter(_points),
        child: Container(
          color: Colors.yellow[<span class="hljs-number">100</span>],
          height: <span class="hljs-built_in">double</span>.infinity,
          width: <span class="hljs-built_in">double</span>.infinity,
        ),
      ),
    );
  }
}
</code></pre>
<p>Now you’ve got a <strong>live sketchpad</strong> — freehand, fluid, and fun. Great for:</p>
<ul>
<li><p>Sketching apps</p>
</li>
<li><p>Signature pads</p>
</li>
<li><p>Drawing games</p>
</li>
<li><p>Whiteboard apps</p>
</li>
</ul>
<hr />
<h3 id="heading-bonus-animating-your-paint">🌈 Bonus: Animating Your Paint</h3>
<p>Want your drawing to <strong>grow</strong> like it’s being sketched in real time? Try mixing in an <code>AnimationController</code> and slowly revealing the points one by one.</p>
<p>For example, build an animation that reveals paths or changes colors dynamically. Animation with <code>CustomPainter</code> is a deep rabbit hole — but a rewarding one.</p>
<hr />
<h2 id="heading-final-words-from-the-artists-studio">🧠 Final Words from the Artist’s Studio</h2>
<p>Whether you want to:</p>
<ul>
<li><p>Draw circles on tap</p>
</li>
<li><p>Sketch freely with finger gestures</p>
</li>
<li><p>Build signature pads</p>
</li>
<li><p>Animate brush strokes</p>
</li>
</ul>
<p>...<code>CustomPaint</code> + <code>GestureDetector</code> is your dynamic duo.</p>
<p>They're like Batman and Alfred:</p>
<ul>
<li><p>One gives you <strong>power to control the pixels</strong>.</p>
</li>
<li><p>The other <strong>notifies you of every interaction</strong>.</p>
</li>
</ul>
<hr />
<h3 id="heading-tldr-you-are-now-the-flutter-picasso">📦 TL;DR – You Are Now the Flutter Picasso 🎨</h3>
<ul>
<li><p><code>CustomPainter</code>: The brush.</p>
</li>
<li><p><code>CustomPaint</code>: The canvas.</p>
</li>
<li><p><code>GestureDetector</code>: The hand holding the brush.</p>
</li>
<li><p>You? The artist. The architect. The <em>creator of chaos with style</em>.</p>
</li>
</ul>
<p>So go ahead — draw that unicorn, sign that virtual document, or paint your dreams in code. Flutter’s canvas is yours.</p>
]]></content:encoded></item><item><title><![CDATA[📘 “Dear Future Me”: A Flutter Dev’s Guide to Writing Code Docs Without Crying Later]]></title><description><![CDATA[Because readable code is a myth when it's 2AM and you're debugging your own madness.

🛠 Once Upon a Time in a Flutter Project…
It was a Tuesday. A quiet, unassuming Tuesday.
I opened a dusty project I hadn’t touched in 4 months. My goal? Fix a simpl...]]></description><link>https://blog.anmolthedeveloper.com/dear-future-me-a-flutter-devs-guide-to-writing-code-docs-without-crying-later</link><guid isPermaLink="true">https://blog.anmolthedeveloper.com/dear-future-me-a-flutter-devs-guide-to-writing-code-docs-without-crying-later</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Mobile Development]]></category><category><![CDATA[Android]]></category><dc:creator><![CDATA[Anmol Singh Tuteja]]></dc:creator><pubDate>Wed, 23 Apr 2025 06:13:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1745388634398/ab3da2ba-ff8a-4420-a8e6-384bb889199b.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>Because readable code is a myth when it's 2AM and you're debugging your own madness.</em></p>
<hr />
<h3 id="heading-once-upon-a-time-in-a-flutter-project">🛠 Once Upon a Time in a Flutter Project…</h3>
<p>It was a Tuesday. A quiet, unassuming Tuesday.</p>
<p>I opened a dusty project I hadn’t touched in 4 months. My goal? Fix a simple bug. Easy, right? That’s what I thought—until I ran into a method named <code>doMagic()</code>.</p>
<p>I blinked.</p>
<p>"Wait, which part is the magic? Why is it async? Why does it mutate the <code>context</code> <em>and</em> a global state variable named <code>spookyGhost</code>?"</p>
<p>I checked the comments. There were none. Not even a sad TODO.</p>
<p><strong>Past Me had betrayed Present Me.</strong></p>
<p>At that moment, I made a solemn vow: <em>Never again shall I leave undocumented code for Future Me to suffer.</em></p>
<hr />
<h3 id="heading-but-i-write-clean-code">🧠 “But I Write Clean Code!”</h3>
<p>Sure, you do. So do I. But let me ask you this:</p>
<p>What does this do?</p>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> thing = getThing(userId, <span class="hljs-keyword">true</span>);
</code></pre>
<p>If you're thinking:</p>
<ul>
<li><p>What’s a <code>thing</code>?</p>
</li>
<li><p>What does <code>true</code> mean here?</p>
</li>
<li><p>What does <code>getThing()</code> actually <em>get</em>?</p>
</li>
</ul>
<p>Congratulations, you now understand why <em>readable ≠ self-explanatory</em>.</p>
<p>Your code may be beautiful, but unless you write documentation, it’s like a beautiful novel with the chapter titles ripped out.</p>
<hr />
<h3 id="heading-dart-doc-101-the-basics-you-should-actually-know">📚 Dart Doc 101 – The Basics You Should <em>Actually</em> Know</h3>
<p>Dart uses special comments for documentation: <code>///</code></p>
<p>Here’s a quick template for what you might write:</p>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">Loads the user's favorite widget based on their preferences.</span></span>
<span class="hljs-comment">///
<span class="markdown">/// This is typically used on the dashboard. If no preferences are found,</span></span>
<span class="hljs-comment">/// <span class="markdown">it defaults to the most popular widget.</span></span>
Widget getThing(<span class="hljs-built_in">String</span> userId, <span class="hljs-built_in">bool</span> fallbackToDefault) { ... }
</code></pre>
<p>And here comes the <strong>cool part</strong> that many devs forget:</p>
<p>You can reference other classes, variables, methods, or parameters by <strong>wrapping them in square brackets</strong>.</p>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">This method uses [userId] to fetch preferences and falls back</span></span>
<span class="hljs-comment">/// <span class="markdown">if [fallbackToDefault] is true.</span></span>
<span class="hljs-comment">///
<span class="markdown">/// Also see: [UserPreferencesService]</span></span>
</code></pre>
<p>When rendered by tools like DartDoc, <code>[userId]</code> becomes a link that takes you straight to the definition. It’s magical. It’s like a breadcrumb trail in a scary forest of logic.</p>
<hr />
<h3 id="heading-when-and-what-to-document">💡 When and What to Document</h3>
<h4 id="heading-document-when">✅ Document when:</h4>
<ul>
<li><p>The function/class is <strong>public</strong> or <strong>used across multiple files</strong></p>
</li>
<li><p>You used a <strong>clever workaround</strong> you’re kind of proud of (but kinda scared of too)</p>
</li>
<li><p>You built a <strong>custom widget</strong> with parameters or configuration</p>
</li>
<li><p>A variable’s name <strong>isn't 100% obvious</strong> (like <code>isXReady</code> vs <code>hasPendingAuthFlow</code>)</p>
</li>
</ul>
<h4 id="heading-dont-document">🛑 Don’t document:</h4>
<ul>
<li><p>Obvious one-liners like <code>i++</code> or <code>count = list.length;</code></p>
</li>
<li><p>Code that’s about to be deleted (RIP)</p>
</li>
<li><p>Things that are still being spiked (use <code>TODO</code> or <code>/// Temporary</code> instead)</p>
</li>
</ul>
<hr />
<h3 id="heading-real-life-example-the-doc-sandwich">🍔 Real-Life Example: The Doc Sandwich</h3>
<p>Here’s how I structure my docs—a tasty 3-layer sandwich:</p>
<pre><code class="lang-dart"><span class="hljs-comment">/// <span class="markdown">Shows a profile card with an optional action button.</span></span>
<span class="hljs-comment">/// 
<span class="markdown">/// This widget is used on the user dashboard to highlight key info.</span></span>
<span class="hljs-comment">/// <span class="markdown">If [onActionPressed] is null, the button is hidden.</span></span>
<span class="hljs-comment">/// 
<span class="markdown">/// Example:</span></span>
<span class="hljs-comment">/// <span class="markdown"><span class="hljs-code">```dart</span></span></span>
<span class="hljs-comment">/// <span class="markdown"><span class="hljs-code">ProfileCard(</span></span></span>
<span class="hljs-comment">///   <span class="markdown"><span class="hljs-code">user: currentUser,</span></span></span>
<span class="hljs-comment">///   <span class="markdown"><span class="hljs-code">onActionPressed: () =&gt; print('Tapped!'),</span></span></span>
<span class="hljs-comment">/// <span class="markdown"><span class="hljs-code">)</span></span></span>
<span class="hljs-comment">///</span>
</code></pre>
<p>ProfileCard extends StatelessWidget {
  final User user;
  final VoidCallback? onActionPressed;</p>
<p>  ...
}
```</p>
<p>See what we did there?</p>
<ol>
<li><p><strong>Top line:</strong> What it does</p>
</li>
<li><p><strong>Middle lines:</strong> When and why to use it</p>
</li>
<li><p><strong>Bottom lines:</strong> A code example—because examples are better than a thousand words</p>
</li>
</ol>
<p>Bonus points: we used square brackets to cross-link <code>[onActionPressed]</code>!</p>
<hr />
<h3 id="heading-power-tips-for-pro-level-docs">😎 Power Tips for Pro-Level Docs</h3>
<ul>
<li><p><strong>Use markdown inside</strong> <code>///</code> – Dart supports <code>*italic*</code>, <code>**bold**</code>, <code>- bullet points</code>, and even code blocks.</p>
</li>
<li><p><strong>Group parameters with</strong> <code>@param</code>, if needed – Dart doesn’t <em>require</em> Java-style tags, but they can help for longer methods.</p>
</li>
<li><p><strong>Mention return values</strong> if the function returns something non-obvious.</p>
</li>
<li><p><strong>Cross-reference related functions</strong> with <code>See also:</code> and square brackets.</p>
</li>
<li><p><strong>Use</strong> <code>/// {@macro myMacro}</code> for re-usable doc snippets (like for similar widgets/functions).</p>
</li>
</ul>
<hr />
<h3 id="heading-your-undocumented-code-will-come-back-to-haunt-you">🧟‍♂️ Your Undocumented Code Will Come Back to Haunt You</h3>
<p>Don't believe me? Try this:<br />Ask your teammate to explain a method you wrote 3 months ago… with no docs.</p>
<p>Watch the panic set in.<br />Listen to the silence.<br />Feel the dread.</p>
<p>Writing documentation is not just about “being nice.” It’s <strong>how you build trust</strong> with your team (and yourself). Great docs make a repo feel like a well-kept library—not an abandoned warehouse full of mystery boxes.</p>
<hr />
<h3 id="heading-the-5-minute-challenge-one-comment-a-day">📅 The 5-Minute Challenge: One Comment a Day</h3>
<p>You don’t have to become Documentation Batman overnight.</p>
<p><strong>Try this:</strong></p>
<ul>
<li><p>Pick one undocumented function today.</p>
</li>
<li><p>Add a 3-line comment explaining what it does, when to use it, and any edge cases.</p>
</li>
<li><p>Repeat tomorrow.</p>
</li>
</ul>
<p>That’s it. Just one a day. After a month, your codebase will feel like a cozy, well-labeled home instead of a haunted mansion of <code>utils.dart</code>.</p>
<hr />
<h3 id="heading-in-summary-yes-you-can-skim-here">📝 In Summary (Yes, You Can Skim Here)</h3>
<ul>
<li><p>Use <code>///</code> for Dart doc comments</p>
</li>
<li><p>Wrap <code>[identifiers]</code> in brackets to create links in generated docs</p>
</li>
<li><p>Always describe:</p>
<ul>
<li><p>What the function/class does</p>
</li>
<li><p>When/why to use it</p>
</li>
<li><p>Any quirks or gotchas</p>
</li>
</ul>
</li>
<li><p>Use markdown and code examples to level up your docs</p>
</li>
<li><p>Write docs like you’re helping your future self through a tough day</p>
</li>
</ul>
<hr />
<h3 id="heading-bonus-want-a-flutter-doc-template-cheat-sheet">🎁 Bonus: Want a Flutter Doc Template Cheat Sheet?</h3>
<p>Let me know, and I’ll send over a downloadable <code>.md</code> or <code>.txt</code> file you can plug right into your project. Think of it as your personal doc-writing sidekick.</p>
<p>Until then—keep fluttering, and don’t forget to leave breadcrumbs for yourself. Because the real magic?<br />It’s not in <code>doMagic()</code>.<br />It’s in writing down <em>what</em> the magic is. 😉</p>
]]></content:encoded></item><item><title><![CDATA[📸 “Tap to Focus Like a Pro: Turning Your Camera App Into a DSLR with Flutter!”]]></title><description><![CDATA[— by a Flutter dev who once tried to pinch-zoom an actual whiteboard.

Once upon a debug session, I found myself staring into the live camera preview of my Flutter app, testing what should’ve been a simple feature.
All I wanted was this:

“When a use...]]></description><link>https://blog.anmolthedeveloper.com/tap-to-focus-like-a-pro-turning-your-camera-app-into-a-dslr-with-flutter</link><guid isPermaLink="true">https://blog.anmolthedeveloper.com/tap-to-focus-like-a-pro-turning-your-camera-app-into-a-dslr-with-flutter</guid><category><![CDATA[Flutter]]></category><category><![CDATA[focus]]></category><category><![CDATA[Dart]]></category><category><![CDATA[Flutter Widgets]]></category><category><![CDATA[Flutter Examples]]></category><dc:creator><![CDATA[Anmol Singh Tuteja]]></dc:creator><pubDate>Thu, 10 Apr 2025 07:08:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746600154298/191b6cc2-4663-42a2-b758-106668e8f51e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>— by a Flutter dev who once tried to pinch-zoom an actual whiteboard.</em></p>
<hr />
<p><strong>Once upon a debug session</strong>, I found myself staring into the live camera preview of my Flutter app, testing what should’ve been a simple feature.</p>
<p>All I wanted was this:</p>
<blockquote>
<p>“When a user taps on the screen… focus the camera <em>right there</em>. Not over there. Not wherever the camera feels like. <em>Right there</em>, buddy.”</p>
</blockquote>
<p>Sounds simple, right?</p>
<p>Wrong.</p>
<p>I tapped, it ignored me. I tapped again, the camera mocked me with an out-of-focus blur like a rebellious teenager refusing to clean their room. That’s when I rolled up my sleeves (metaphorically, I was wearing a t-shirt) and dove into the <code>camera</code> package to teach it some manners.</p>
<p>Let me take you on a little journey of how I made my camera app behave like a DSLR — complete with tap-to-focus magic.</p>
<hr />
<h3 id="heading-the-goal">🎯 The Goal</h3>
<p>We want our users to <strong>tap anywhere on the camera preview</strong> and have the camera <strong>focus on that exact spot</strong>. Like the cool kids on iPhones and Samsungs.</p>
<p>To do that, we need three ingredients:</p>
<ol>
<li><p>The <strong>tap location</strong> (where the user touched).</p>
</li>
<li><p>A way to <strong>normalize that position</strong> into something the camera understands.</p>
</li>
<li><p>A friendly whisper to the camera: “Focus, dear lens… focus.”</p>
</li>
</ol>
<hr />
<h3 id="heading-the-gesturedetector-spell">🧙 The GestureDetector Spell</h3>
<p>Here’s the magical widget that makes it all happen:</p>
<pre><code class="lang-dart">GestureDetector(
  onTapDown: (TapDownDetails details) {
    <span class="hljs-keyword">final</span> RenderBox box = context.findRenderObject() <span class="hljs-keyword">as</span> RenderBox;
    <span class="hljs-keyword">final</span> Offset localPosition = box.globalToLocal(details.globalPosition);

    <span class="hljs-keyword">final</span> <span class="hljs-built_in">double</span> dx = localPosition.dx / box.size.width;
    <span class="hljs-keyword">final</span> <span class="hljs-built_in">double</span> dy = localPosition.dy / box.size.height;

    _controller.setFocusPoint(Offset(dx, dy));
    _controller.setFocusMode(FocusMode.auto);
  },
  child: AspectRatio(
    aspectRatio: <span class="hljs-number">3</span> / <span class="hljs-number">4</span>,
    child: CameraPreview(_controller),
  ),
),
</code></pre>
<p>Let’s break it down like a DJ on a mission.</p>
<hr />
<h3 id="heading-tapdown-catching-the-clue">🕵️‍♂️ TapDown: Catching the Clue</h3>
<pre><code class="lang-dart">TapDown: (TapDownDetails details)
</code></pre>
<p>Why <code>onTapDown</code> and not <code>onTap</code>? Because <code>onTap</code> is chill and doesn’t tell us where the finger landed. <code>onTapDown</code>, on the other hand, <em>snitches immediately</em> — giving us exact coordinates. Just what we need.</p>
<hr />
<h3 id="heading-renderbox-the-widget-whisperer">📦 RenderBox: The Widget Whisperer</h3>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> RenderBox box = context.findRenderObject() <span class="hljs-keyword">as</span> RenderBox;
</code></pre>
<p>Here we summon the mighty <code>RenderBox</code> — the unsung hero that knows <strong>where your widget is and how big it is</strong>. Think of it like the camera preview’s GPS.</p>
<hr />
<h3 id="heading-from-global-to-local">📍 From Global to Local</h3>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> Offset localPosition = box.globalToLocal(details.globalPosition);
</code></pre>
<p>This is where we translate the user’s touch from <strong>"screen land"</strong> into <strong>"widget land"</strong>. It’s like converting world coordinates to Minecraft block coordinates (gamers, you know what I mean).</p>
<hr />
<h3 id="heading-normalizing-the-tap">📐 Normalizing the Tap</h3>
<pre><code class="lang-dart"><span class="hljs-keyword">final</span> <span class="hljs-built_in">double</span> dx = localPosition.dx / box.size.width;
<span class="hljs-keyword">final</span> <span class="hljs-built_in">double</span> dy = localPosition.dy / box.size.height;
</code></pre>
<p>The camera wants <strong>normalized coordinates</strong> — between 0.0 and 1.0.</p>
<p>So we take the position, divide by the width and height, and now <code>(dx, dy)</code> points exactly where the user tapped — in a way the camera understands.</p>
<p>It’s like whispering to the lens in its native language.</p>
<hr />
<h3 id="heading-focus-mode-putting-it-all-together">🔍 Focus Mode: Putting It All Together</h3>
<pre><code class="lang-dart">_controller.setFocusPoint(Offset(dx, dy));
_controller.setFocusMode(FocusMode.auto);
</code></pre>
<p>These two lines are where the magic happens.</p>
<ul>
<li><p>First, we set the <strong>focus point</strong>.</p>
</li>
<li><p>Then, we tell the camera: “Hey, time to actually <em>focus</em> there.”</p>
</li>
</ul>
<p>Like nudging your friend at a party: <em>"Dude, pay attention."</em></p>
<hr />
<h3 id="heading-bonus-show-a-focus-ring">🎨 Bonus: Show a Focus Ring</h3>
<p>Want to <strong>visually show the user</strong> where they tapped? Add a ring or animation — maybe even a shimmer or haptic feedback. Not just functional, but delightful.</p>
<hr />
<h3 id="heading-final-thoughts">🧠 Final Thoughts</h3>
<p>Adding tap-to-focus isn’t just about functionality. It’s about making your camera feel alive. Responsive. <em>Professional</em>.</p>
<p>By capturing the user’s intent and giving them control over the lens — you elevate the whole experience from “meh” to <em>“woah, did I just build this?”</em></p>
<p>So go ahead — treat your camera preview like a canvas, and let your users tap to paint it with focus.</p>
<hr />
<h3 id="heading-tldr-for-the-busy-dev">🔧 TL;DR for the Busy Dev:</h3>
<ul>
<li><p>Use <code>GestureDetector</code> with <code>onTapDown</code>.</p>
</li>
<li><p>Convert the tap to <strong>normalized coordinates</strong> using <code>RenderBox</code>.</p>
</li>
<li><p>Call <code>setFocusPoint()</code> and <code>setFocusMode()</code>.</p>
</li>
</ul>
<p>And voilà — your Flutter camera app now takes focus orders like a loyal knight.</p>
<hr />
<p>Happy focusing, Flutter wizard! ✨</p>
]]></content:encoded></item><item><title><![CDATA[Mastering Flutter & Android Lifecycle: A Developer's Guide]]></title><description><![CDATA[Flutter has become the go-to framework for building cross-platform applications, but understanding the lifecycle of an app remains crucial—especially for developers coming from a native Android background. In Android, we rely on specific lifecycle me...]]></description><link>https://blog.anmolthedeveloper.com/mastering-flutter-and-android-lifecycle-a-developers-guide</link><guid isPermaLink="true">https://blog.anmolthedeveloper.com/mastering-flutter-and-android-lifecycle-a-developers-guide</guid><category><![CDATA[Android]]></category><category><![CDATA[Flutter]]></category><category><![CDATA[lifecycle]]></category><dc:creator><![CDATA[Anmol Singh Tuteja]]></dc:creator><pubDate>Thu, 27 Feb 2025 07:35:02 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740641640906/424a2e48-0cf6-4c3b-936b-75b2e2af4b12.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Flutter has become the go-to framework for building cross-platform applications, but understanding the <strong>lifecycle of an app</strong> remains crucial—especially for developers coming from a native Android background. In Android, we rely on specific lifecycle methods like <code>onResume()</code>, <code>onPause()</code>, <code>onStop()</code>, and <code>onDestroy()</code>. However, in Flutter, the app lifecycle is managed differently.</p>
<p>In this article, we’ll explore Android lifecycle methods and their equivalents in Flutter, along with real-world use cases.</p>
<h2 id="heading-android-app-lifecycle-overview"><strong>Android App Lifecycle Overview</strong></h2>
<p>Android applications follow a well-defined lifecycle, controlled by the <code>Activity</code> class. Here’s how the primary lifecycle methods work:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Lifecycle Method</strong></td><td><strong>Description</strong></td></tr>
</thead>
<tbody>
<tr>
<td><code>onCreate()</code></td><td>Called when the activity is first created.</td></tr>
<tr>
<td><code>onStart()</code></td><td>Called when the activity becomes visible.</td></tr>
<tr>
<td><code>onResume()</code></td><td>Called when the user starts interacting with the activity.</td></tr>
<tr>
<td><code>onPause()</code></td><td>Called when the activity is partially obscured (e.g., another activity is opening).</td></tr>
<tr>
<td><code>onStop()</code></td><td>Called when the activity is no longer visible.</td></tr>
<tr>
<td><code>onRestart()</code></td><td>Called when the activity is coming back to the foreground.</td></tr>
<tr>
<td><code>onDestroy()</code></td><td>Called before the activity is destroyed.</td></tr>
</tbody>
</table>
</div><h2 id="heading-flutters-lifecycle-approach"><strong>Flutter’s Lifecycle Approach</strong></h2>
<p>Unlike Android, Flutter does not have an <strong>Activity-based lifecycle</strong>. Instead, Flutter apps use <code>WidgetsBindingObserver</code> to track lifecycle changes at the app level.</p>
<p>Flutter provides four main lifecycle states:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Flutter Lifecycle State</strong></td><td><strong>Equivalent Android Lifecycle</strong></td></tr>
</thead>
<tbody>
<tr>
<td><code>AppLifecycleState.resumed</code></td><td><code>onResume()</code></td></tr>
<tr>
<td><code>AppLifecycleState.inactive</code></td><td>Part of <code>onPause()</code></td></tr>
<tr>
<td><code>AppLifecycleState.paused</code></td><td><code>onStop()</code></td></tr>
<tr>
<td><code>AppLifecycleState.detached</code></td><td><code>onDestroy()</code></td></tr>
</tbody>
</table>
</div><h2 id="heading-understanding-initstate-dispose-and-other-methods-in-flutter"><strong>Understanding</strong> <code>initState()</code>, <code>dispose()</code>, and Other Methods in Flutter</h2>
<p>Flutter's StatefulWidget lifecycle consists of several key methods:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td><strong>Method</strong></td><td><strong>Description</strong></td></tr>
</thead>
<tbody>
<tr>
<td><code>initState()</code></td><td>Called once when the widget is inserted into the widget tree. Ideal for initializing resources like controllers, animations, or network calls.</td></tr>
<tr>
<td><code>didChangeDependencies()</code></td><td>Called when the widget’s dependencies change (e.g., inherited widgets update).</td></tr>
<tr>
<td><code>build()</code></td><td>Called every time the widget needs to be rebuilt. Returns the UI.</td></tr>
<tr>
<td><code>didUpdateWidget()</code></td><td>Called when the parent widget rebuilds and provides a new instance of the same widget. Useful for responding to changes in the widget properties.</td></tr>
<tr>
<td><code>deactivate()</code></td><td>Called when the widget is removed from the tree but might be reinserted later.</td></tr>
<tr>
<td><code>dispose()</code></td><td>Called when the widget is permanently removed. Used for cleanup, like closing streams or disposing of controllers.</td></tr>
</tbody>
</table>
</div><h3 id="heading-example-using-initstate-and-dispose-in-flutter"><strong>Example: Using</strong> <code>initState()</code> and <code>dispose()</code> in Flutter</h3>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ExampleStatefulWidget</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  _ExampleStatefulWidgetState createState() =&gt; _ExampleStatefulWidgetState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_ExampleStatefulWidgetState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">ExampleStatefulWidget</span>&gt; </span>{
  <span class="hljs-keyword">late</span> TextEditingController _controller;

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    <span class="hljs-keyword">super</span>.initState();
    _controller = TextEditingController(); <span class="hljs-comment">// Initializing controller</span>
    <span class="hljs-built_in">print</span>(<span class="hljs-string">"Widget initialized"</span>);
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> dispose() {
    _controller.dispose(); <span class="hljs-comment">// Cleaning up resources</span>
    <span class="hljs-built_in">print</span>(<span class="hljs-string">"Widget disposed"</span>);
    <span class="hljs-keyword">super</span>.dispose();
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      appBar: AppBar(title: Text(<span class="hljs-string">"Lifecycle Example"</span>)),
      body: Padding(
        padding: EdgeInsets.all(<span class="hljs-number">16.0</span>),
        child: TextField(controller: _controller),
      ),
    );
  }
}
</code></pre>
<h2 id="heading-implementing-lifecycle-listening-in-flutter"><strong>Implementing Lifecycle Listening in Flutter</strong></h2>
<p>To listen to app lifecycle changes in Flutter, implement the <code>WidgetsBindingObserver</code> class as shown below:</p>
<pre><code class="lang-dart"><span class="hljs-keyword">import</span> <span class="hljs-string">'package:flutter/material.dart'</span>;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AppLifecycleListener</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">StatefulWidget</span> </span>{
  <span class="hljs-meta">@override</span>
  _AppLifecycleListenerState createState() =&gt; _AppLifecycleListenerState();
}

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">_AppLifecycleListenerState</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">State</span>&lt;<span class="hljs-title">AppLifecycleListener</span>&gt; <span class="hljs-title">with</span> <span class="hljs-title">WidgetsBindingObserver</span> </span>{
  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> initState() {
    <span class="hljs-keyword">super</span>.initState();
    WidgetsBinding.instance.addObserver(<span class="hljs-keyword">this</span>);
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> dispose() {
    WidgetsBinding.instance.removeObserver(<span class="hljs-keyword">this</span>);
    <span class="hljs-keyword">super</span>.dispose();
  }

  <span class="hljs-meta">@override</span>
  <span class="hljs-keyword">void</span> didChangeAppLifecycleState(AppLifecycleState state) {
    <span class="hljs-keyword">switch</span> (state) {
      <span class="hljs-keyword">case</span> AppLifecycleState.resumed:
        debugPrint(<span class="hljs-string">"App is in foreground"</span>);
        <span class="hljs-keyword">break</span>;
      <span class="hljs-keyword">case</span> AppLifecycleState.inactive:
        debugPrint(<span class="hljs-string">"App is inactive"</span>);
        <span class="hljs-keyword">break</span>;
      <span class="hljs-keyword">case</span> AppLifecycleState.paused:
        debugPrint(<span class="hljs-string">"App is in background"</span>);
        <span class="hljs-keyword">break</span>;
      <span class="hljs-keyword">case</span> AppLifecycleState.detached:
        debugPrint(<span class="hljs-string">"App is terminated"</span>);
        <span class="hljs-keyword">break</span>;
    }
  }

  <span class="hljs-meta">@override</span>
  Widget build(BuildContext context) {
    <span class="hljs-keyword">return</span> Scaffold(
      body: Center(child: Text(<span class="hljs-string">"Listening to App Lifecycle"</span>)),
    );
  }
}
</code></pre>
<h2 id="heading-real-world-use-cases"><strong>Real-World Use Cases</strong></h2>
<h3 id="heading-1-handling-background-tasks"><strong>1. Handling Background Tasks</strong></h3>
<ul>
<li><p>When an app moves to the background (<code>paused</code> state), stop active tasks like video playback or data fetching.</p>
</li>
<li><p>When the app resumes, restart those tasks.</p>
</li>
</ul>
<h3 id="heading-2-detecting-user-inactivity"><strong>2. Detecting User Inactivity</strong></h3>
<ul>
<li>If a user goes inactive (<code>inactive</code> state), you can pause UI updates or disable certain features temporarily.</li>
</ul>
<h3 id="heading-3-preventing-data-loss"><strong>3. Preventing Data Loss</strong></h3>
<ul>
<li>Save user progress when <code>paused</code> or <code>detached</code> is triggered to prevent data loss if the app is closed.</li>
</ul>
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>Understanding app lifecycle behavior is essential for optimizing app performance and user experience. While Android follows an Activity-based lifecycle, Flutter provides <strong>AppLifecycleState</strong> with <code>WidgetsBindingObserver</code>. Additionally, knowing how to manage widget lifecycle methods like <code>initState()</code> and <code>dispose()</code> is critical for efficient state management.</p>
<p>By leveraging these lifecycle events, we can build more efficient and responsive applications across platforms.</p>
<p>Let me know if you have any questions or need further clarifications! 🚀</p>
]]></content:encoded></item><item><title><![CDATA[“The Mysterious Case of the Missing App: Solving the 'App Not Installed' Error”]]></title><description><![CDATA[A Developer’s Nightmare Begins
It was a late night, and Alex, a passionate Android developer, had just finished building a new feature for his app. Excited to test it on his device, he transferred the APK and tapped "Install." But instead of seeing t...]]></description><link>https://blog.anmolthedeveloper.com/the-mysterious-case-of-the-missing-app-solving-the-app-not-installed-error</link><guid isPermaLink="true">https://blog.anmolthedeveloper.com/the-mysterious-case-of-the-missing-app-solving-the-app-not-installed-error</guid><category><![CDATA[Android]]></category><category><![CDATA[Android Studio]]></category><category><![CDATA[android app development]]></category><category><![CDATA[Bugs and Errors]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Programming Tips]]></category><dc:creator><![CDATA[Anmol Singh Tuteja]]></dc:creator><pubDate>Wed, 26 Feb 2025 07:56:56 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740556518298/d27165b3-8826-44bc-9747-7d6255276417.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-a-developers-nightmare-begins"><strong>A Developer’s Nightmare Begins</strong></h2>
<p>It was a late night, and Alex, a passionate Android developer, had just finished building a new feature for his app. Excited to test it on his device, he transferred the APK and tapped "Install." But instead of seeing the success screen, an error popped up: <strong>"App not installed."</strong></p>
<p>Confused, he tried again. Still nothing. Frustration set in. What went wrong?</p>
<p>This is a common issue many developers face, but the solution isn't always straightforward. Let’s break down the possible reasons and how to fix them.</p>
<hr />
<h2 id="heading-common-reasons-amp-fixes-for-app-not-installed"><strong>Common Reasons &amp; Fixes for 'App Not Installed'</strong></h2>
<h3 id="heading-1-conflicting-app-versions"><strong>1️⃣ Conflicting App Versions</strong></h3>
<p>If a previous version of the app is already installed (signed with a different key), Android won’t allow the new installation. To check and fix this:</p>
<h4 id="heading-solution-uninstall-the-existing-app-first"><strong>Solution:</strong> Uninstall the existing app first</h4>
<pre><code class="lang-bash">adb uninstall com.yourpackage.name
</code></pre>
<p>Then, try reinstalling the new APK.</p>
<h4 id="heading-uninstalling-from-a-specific-device"><strong>Uninstalling from a Specific Device</strong></h4>
<p>If multiple devices are connected, specify which device to uninstall from:</p>
<pre><code class="lang-bash">adb -s &lt;device_id&gt; uninstall com.yourpackage.name
</code></pre>
<p>Find the device ID using:</p>
<pre><code class="lang-bash">adb devices
</code></pre>
<hr />
<h3 id="heading-2-apk-signing-issues"><strong>2️⃣ APK Signing Issues</strong></h3>
<p>If you built the app in <strong>debug mode</strong> but had a <strong>release version installed before</strong>, Android treats them as different apps.</p>
<h4 id="heading-solution-install-with-the-correct-signature"><strong>Solution:</strong> Install with the correct signature</h4>
<p>If using a debug build, allow installation of test APKs:</p>
<pre><code class="lang-bash">adb install -t your_app.apk
</code></pre>
<p>Or, sign the APK correctly before installing:</p>
<pre><code class="lang-bash">jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore my-release-key.keystore my_app.apk alias_name
</code></pre>
<hr />
<h3 id="heading-3-corrupt-or-incomplete-apk"><strong>3️⃣ Corrupt or Incomplete APK</strong></h3>
<p>A partially downloaded or corrupted APK can cause this issue.</p>
<h4 id="heading-solution-verify-and-reinstall"><strong>Solution:</strong> Verify and reinstall</h4>
<p>Check if the APK is valid:</p>
<pre><code class="lang-bash">adb install your_app.apk
</code></pre>
<p>If you see <code>INSTALL_PARSE_FAILED_NO_CERTIFICATES</code>, rebuild and sign the APK properly.</p>
<hr />
<h3 id="heading-4-insufficient-storage"><strong>4️⃣ Insufficient Storage</strong></h3>
<p>If the device is low on storage, Android may reject the installation.</p>
<h4 id="heading-solution-free-up-space"><strong>Solution:</strong> Free up space</h4>
<ul>
<li><p>Delete unnecessary files.</p>
</li>
<li><p>Check storage: <strong>Settings → Storage</strong>.</p>
</li>
<li><p>Try installing again.</p>
</li>
</ul>
<hr />
<h3 id="heading-5-unknown-sources-not-enabled"><strong>5️⃣ "Unknown Sources" Not Enabled</strong></h3>
<p>If installing from a file manager, ensure the setting is enabled:</p>
<h4 id="heading-solution-enable-install-unknown-apps"><strong>Solution:</strong> Enable "Install Unknown Apps"</h4>
<ol>
<li><p>Go to <strong>Settings → Apps &amp; Notifications</strong>.</p>
</li>
<li><p>Tap <strong>Special App Access</strong> → <strong>Install Unknown Apps</strong>.</p>
</li>
<li><p>Enable for the app you're using to install the APK.</p>
</li>
</ol>
<hr />
<h3 id="heading-6-app-bundle-installation-issues"><strong>6️⃣ App Bundle Installation Issues</strong></h3>
<p>If you’re using an <strong>Android App Bundle (AAB)</strong> instead of an APK, ensure it’s correctly converted before installation.</p>
<h4 id="heading-solution-use-bundletool-to-extract-apks"><strong>Solution:</strong> Use Bundletool to extract APKs</h4>
<pre><code class="lang-bash">bundletool build-apks --bundle=my_app.aab --output=my_app.apks --mode=universal
bundletool install-apks --apks=my_app.apks
</code></pre>
<hr />
<h3 id="heading-7-feature-or-permission-conflicts"><strong>7️⃣ Feature or Permission Conflicts</strong></h3>
<p>Certain features declared in <code>AndroidManifest.xml</code> can cause installation failure if the device doesn’t support them.</p>
<h4 id="heading-solution-check-manifest-permissions"><strong>Solution:</strong> Check manifest permissions</h4>
<p>If your app requires NFC but the device doesn’t support it, change:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">uses-feature</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.hardware.nfc"</span> <span class="hljs-attr">android:required</span>=<span class="hljs-string">"true"</span>/&gt;</span>
</code></pre>
<p>To:</p>
<pre><code class="lang-xml"><span class="hljs-tag">&lt;<span class="hljs-name">uses-feature</span> <span class="hljs-attr">android:name</span>=<span class="hljs-string">"android.hardware.nfc"</span> <span class="hljs-attr">android:required</span>=<span class="hljs-string">"false"</span>/&gt;</span>
</code></pre>
<hr />
<h3 id="heading-8-clear-package-installer-cache"><strong>8️⃣ Clear Package Installer Cache</strong></h3>
<p>The system’s package manager may have cached data causing issues.</p>
<h4 id="heading-solution-clear-package-installer-data"><strong>Solution:</strong> Clear Package Installer data</h4>
<ol>
<li><p>Go to <strong>Settings → Apps → Show System Apps</strong>.</p>
</li>
<li><p>Find <strong>Package Installer</strong>.</p>
</li>
<li><p>Tap <strong>Storage &amp; Cache → Clear Data &amp; Clear Cache</strong>.</p>
</li>
<li><p>Restart the device and retry installation.</p>
</li>
</ol>
<hr />
<h3 id="heading-9-developer-mode-and-adb-conflicts"><strong>9️⃣ Developer Mode and ADB Conflicts</strong></h3>
<p>If Developer Mode is enabled, some security restrictions might interfere.</p>
<h4 id="heading-solution-disable-developer-mode-optional"><strong>Solution:</strong> Disable Developer Mode (optional)</h4>
<pre><code class="lang-bash">adb shell settings put global development_settings_enabled 0
</code></pre>
<p>Or, try installing with <code>-r</code> to replace an existing app:</p>
<pre><code class="lang-bash">adb install -r your_app.apk
</code></pre>
<hr />
<h2 id="heading-bonus-debugging-the-issue-with-logcat"><strong>Bonus: Debugging the Issue with Logcat</strong></h2>
<p>If none of the solutions work, check for specific error messages:</p>
<pre><code class="lang-bash">adb logcat | grep <span class="hljs-string">"PackageManager"</span>
</code></pre>
<p>This will give insights into why the installation is failing.</p>
<hr />
<h2 id="heading-final-thoughts-a-happy-ending"><strong>Final Thoughts: A Happy Ending</strong></h2>
<p>Back to Alex. After hours of troubleshooting, he found that an old debug version of the app was conflicting with the new release. He uninstalled it, signed the APK properly, and—finally—the app installed successfully!</p>
<p>Next time you or your team face this issue, you’ll know exactly where to look and how to fix it. Happy coding! 🚀</p>
]]></content:encoded></item><item><title><![CDATA[“Caught in the Act: Detecting Fake GPS Locations in Your Android App”]]></title><description><![CDATA[The Unexpected "Trip" of a Delivery Agent
It was a normal day at "SwiftDeliver," a booming delivery startup that prided itself on fast and reliable service. The app was built with real-time location tracking, ensuring that customers could track their...]]></description><link>https://blog.anmolthedeveloper.com/caught-in-the-act-detecting-fake-gps-locations-in-your-android-app</link><guid isPermaLink="true">https://blog.anmolthedeveloper.com/caught-in-the-act-detecting-fake-gps-locations-in-your-android-app</guid><category><![CDATA[fake-location]]></category><category><![CDATA[Android]]></category><category><![CDATA[gps]]></category><category><![CDATA[location api]]></category><dc:creator><![CDATA[Anmol Singh Tuteja]]></dc:creator><pubDate>Wed, 26 Feb 2025 07:28:41 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740554595968/885f7e71-80fe-4d60-bf13-7ee8c918fec4.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-the-unexpected-trip-of-a-delivery-agent"><strong>The Unexpected "Trip" of a Delivery Agent</strong></h2>
<p>It was a normal day at "SwiftDeliver," a booming delivery startup that prided itself on fast and reliable service. The app was built with real-time location tracking, ensuring that customers could track their orders accurately. However, one day, something strange happened.</p>
<p>A delivery agent, Alex, had just accepted an order in New York, but within seconds, his location updated—he was suddenly in Los Angeles! The system flagged this as impossible, yet Alex swore he never left the city.</p>
<p>What went wrong? A classic case of <strong>mock location abuse</strong>.</p>
<h3 id="heading-understanding-mock-locations-in-android"><strong>Understanding Mock Locations in Android</strong></h3>
<p>Android allows developers and testers to <strong>simulate</strong> GPS locations using mock providers. While this is useful for testing, it can be exploited by users who manipulate their GPS location using third-party apps. This can be a major problem for apps that rely on real-time location tracking, such as delivery services, ride-sharing platforms, and banking apps.</p>
<h2 id="heading-how-to-detect-fake-gps-locations-in-your-android-app"><strong>How to Detect Fake GPS Locations in Your Android App</strong></h2>
<p>Android provides two methods to check if a location is fake:</p>
<ol>
<li><p><code>isMock()</code> <em>(Android 12+)</em></p>
</li>
<li><p><code>isFromMockProvider()</code> <em>(Deprecated in Android 12, used in older versions)</em></p>
</li>
</ol>
<h3 id="heading-key-differences"><strong>Key Differences</strong></h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Feature</td><td><code>isMock()</code> (API 31+)</td><td><code>isFromMockProvider()</code> (Deprecated)</td></tr>
</thead>
<tbody>
<tr>
<td>Availability</td><td>Android 12+ (API 31)</td><td>Android 11 and below</td></tr>
<tr>
<td>Checks for Mocking</td><td>Yes, at the location level</td><td>Yes, at the provider level</td></tr>
<tr>
<td>Recommended Use</td><td>Yes, for modern apps</td><td>No, deprecated in Android 12</td></tr>
</tbody>
</table>
</div><p>Here’s how to handle both cases to ensure compatibility across Android versions:</p>
<h3 id="heading-code-to-detect-mock-locations"><strong>Code to Detect Mock Locations</strong></h3>
<pre><code class="lang-kotlin"><span class="hljs-keyword">import</span> android.location.Location
<span class="hljs-keyword">import</span> android.os.Build

<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">isLocationMocked</span><span class="hljs-params">(location: <span class="hljs-type">Location</span>)</span></span>: <span class="hljs-built_in">Boolean</span> {
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">if</span> (Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.S) {
        location.isMock
    } <span class="hljs-keyword">else</span> {
        location.isFromMockProvider
    }
}
</code></pre>
<h3 id="heading-breaking-it-down"><strong>Breaking It Down</strong></h3>
<ul>
<li><p>If the device is running <strong>Android 12+ (API 31)</strong>, we use <code>location.isMock</code>, which is the recommended method.</p>
</li>
<li><p>For <strong>older versions</strong>, we use <code>location.isFromMockProvider()</code>, which was deprecated in Android 12.</p>
</li>
<li><p>This ensures your app remains compatible across all Android versions.</p>
</li>
</ul>
<h2 id="heading-what-to-do-if-a-mock-location-is-detected"><strong>What to Do If a Mock Location Is Detected?</strong></h2>
<p>Simply detecting a mock location is not enough—you need to decide how your app should respond. Here are a few strategies:</p>
<h3 id="heading-1-show-a-warning-message"><strong>1. Show a Warning Message</strong></h3>
<pre><code class="lang-kotlin"><span class="hljs-keyword">if</span> (isLocationMocked(location)) {
    showToast(<span class="hljs-string">"Mock locations are not allowed!"</span>)
}
</code></pre>
<h3 id="heading-2-restrict-app-features"><strong>2. Restrict App Features</strong></h3>
<p>For example, a ride-sharing app could prevent a user from booking a ride if a mock location is detected.</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">if</span> (isLocationMocked(location)) {
    disableBookingButton()
}
</code></pre>
<h3 id="heading-3-log-the-event-for-further-analysis"><strong>3. Log the Event for Further Analysis</strong></h3>
<pre><code class="lang-kotlin"><span class="hljs-keyword">if</span> (isLocationMocked(location)) {
    Log.w(<span class="hljs-string">"LocationSecurity"</span>, <span class="hljs-string">"Mock location detected! User might be spoofing GPS."</span>)
}
</code></pre>
<h2 id="heading-nearby-topics-enhancing-location-security"><strong>Nearby Topics: Enhancing Location Security</strong></h2>
<h3 id="heading-1-enforcing-location-accuracy-with-locationrequestpriorityhighaccuracy"><strong>1. Enforcing Location Accuracy with</strong> <code>LocationRequest.PRIORITY_HIGH_ACCURACY</code></h3>
<p>To reduce location spoofing, always request high-accuracy GPS updates:</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">val</span> locationRequest = LocationRequest.Builder(Priority.PRIORITY_HIGH_ACCURACY, <span class="hljs-number">5000</span>).build()
</code></pre>
<p>This ensures the device uses GPS, Wi-Fi, and mobile networks for the most precise location.</p>
<h3 id="heading-2-verifying-location-with-backend-checks"><strong>2. Verifying Location with Backend Checks</strong></h3>
<p>A more robust approach is to verify location changes on the server-side. If a user’s location suddenly jumps hundreds of miles in seconds, flag it as suspicious.</p>
<h3 id="heading-3-blocking-developer-mode-optional"><strong>3. Blocking Developer Mode (Optional)</strong></h3>
<p>Many mock location apps require <strong>Developer Mode</strong> to be enabled. You can check this setting:</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">val</span> isDevMode = Settings.Secure.getInt(
    contentResolver,
    Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, <span class="hljs-number">0</span>
) != <span class="hljs-number">0</span>
</code></pre>
<p>If enabled, you can warn the user or restrict certain features.</p>
<h2 id="heading-final-thoughts"><strong>Final Thoughts</strong></h2>
<p>Back at SwiftDeliver, after implementing these checks, Alex’s "teleportation" problem was solved. The app could now <strong>detect and prevent fake GPS locations</strong>, ensuring that all deliveries were legitimate.</p>
<p>By using <code>isMock()</code> and <code>isFromMockProvider()</code> smartly, you can <strong>protect your app from GPS spoofing and fraudulent activity</strong>, keeping your users safe and your business secure.</p>
<p>Are you ready to level up your app’s location security? Implement these checks today and stay ahead of GPS spoofers!</p>
]]></content:encoded></item><item><title><![CDATA[The Ultimate Guide to Getting Location in Android (GPS, Wi-Fi & More!)]]></title><description><![CDATA[Introduction
Have you ever wondered how your Android phone knows where you are? Whether you're using Google Maps, a ride-hailing app, or a weather app, location services play a huge role in making your experience seamless.
In Android, there are diffe...]]></description><link>https://blog.anmolthedeveloper.com/the-ultimate-guide-to-getting-location-in-android-gps-wi-fi-and-more</link><guid isPermaLink="true">https://blog.anmolthedeveloper.com/the-ultimate-guide-to-getting-location-in-android-gps-wi-fi-and-more</guid><category><![CDATA[gps]]></category><category><![CDATA[Android]]></category><category><![CDATA[location ]]></category><category><![CDATA[tracking]]></category><dc:creator><![CDATA[Anmol Singh Tuteja]]></dc:creator><pubDate>Tue, 18 Feb 2025 10:50:47 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1739875590660/50763ef9-dc52-4f42-ab78-4f1b3fc310c8.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h3 id="heading-introduction">Introduction</h3>
<p>Have you ever wondered how your Android phone knows where you are? Whether you're using Google Maps, a ride-hailing app, or a weather app, location services play a huge role in making your experience seamless.</p>
<p>In Android, there are different ways to get location data. You can use the traditional <strong>LocationManager</strong> (which provides GPS, Network, and Passive location), or you can use the <strong>Fused Location Provider (FLP)</strong>, which is Google's smarter and more efficient solution.</p>
<p>In this blog, we'll break down how location services work, the difference between traditional methods and the Fused Location Provider, and how you can implement them in your Android app.</p>
<hr />
<h2 id="heading-1-different-ways-to-get-location-in-android"><strong>1. Different Ways to Get Location in Android</strong></h2>
<p>Android provides three main location providers:</p>
<h3 id="heading-11-gpsprovider"><strong>1.1 GPS_PROVIDER</strong></h3>
<p>✅ Uses the device’s GPS hardware to get location.</p>
<p>✅ Provides high accuracy.</p>
<p>❌ Requires a clear view of the sky (doesn't work well indoors).</p>
<p>❌ Drains battery faster.</p>
<p><strong>Code Example:</strong></p>
<pre><code class="lang-java">LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

<span class="hljs-keyword">if</span> (ActivityCompat.checkSelfPermission(<span class="hljs-keyword">this</span>, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, <span class="hljs-number">5000</span>, <span class="hljs-number">10</span>, locationListener);
}
</code></pre>
<hr />
<h3 id="heading-12-networkprovider"><strong>1.2 NETWORK_PROVIDER</strong></h3>
<p>✅ Uses Wi-Fi and mobile networks for location.</p>
<p>✅ Works indoors and is power-efficient.</p>
<p>❌ Less accurate than GPS.</p>
<p><strong>Code Example:</strong></p>
<pre><code class="lang-java">LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

<span class="hljs-keyword">if</span> (ActivityCompat.checkSelfPermission(<span class="hljs-keyword">this</span>, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
    locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, <span class="hljs-number">5000</span>, <span class="hljs-number">10</span>, locationListener);
}
</code></pre>
<hr />
<h3 id="heading-13-passiveprovider"><strong>1.3 PASSIVE_PROVIDER</strong></h3>
<p>✅ Listens for location updates requested by other apps.</p>
<p>✅ Saves battery (since it doesn’t request updates itself).</p>
<p>❌ No control over update frequency.</p>
<p><strong>Code Example:</strong></p>
<pre><code class="lang-java">LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

<span class="hljs-keyword">if</span> (ActivityCompat.checkSelfPermission(<span class="hljs-keyword">this</span>, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
    locationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, locationListener);
}
</code></pre>
<hr />
<h2 id="heading-2-the-better-way-fused-location-provider-flp"><strong>2. The Better Way: Fused Location Provider (FLP)</strong></h2>
<h3 id="heading-21-what-is-fused-location-provider"><strong>2.1 What is Fused Location Provider?</strong></h3>
<p>Instead of using GPS, Network, or Passive location separately, <strong>Google recommends using FusedLocationProviderClient</strong>, which is part of Google Play Services. It automatically selects the best location provider based on accuracy, battery usage, and availability.</p>
<p>Fused Location Provider can use:</p>
<ul>
<li><p><strong>GPS</strong> (when high accuracy is needed)</p>
</li>
<li><p><strong>Wi-Fi &amp; Mobile Networks</strong> (for faster but less accurate location)</p>
</li>
<li><p><strong>Device Sensors</strong> (to improve accuracy and detect movement)</p>
</li>
</ul>
<hr />
<h3 id="heading-22-how-to-use-fusedlocationproviderclient-in-android"><strong>2.2 How to Use FusedLocationProviderClient in Android</strong></h3>
<p>To get the last known location, follow these steps:</p>
<p><strong>Step 1: Initialize FusedLocationProviderClient</strong></p>
<pre><code class="lang-java">FusedLocationProviderClient fusedLocationClient = LocationServices.getFusedLocationProviderClient(<span class="hljs-keyword">this</span>);
</code></pre>
<p><strong>Step 2: Get Last Known Location</strong></p>
<pre><code class="lang-java"><span class="hljs-keyword">if</span> (ActivityCompat.checkSelfPermission(<span class="hljs-keyword">this</span>, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
    fusedLocationClient.getLastLocation()
        .addOnSuccessListener(<span class="hljs-keyword">this</span>, <span class="hljs-keyword">new</span> OnSuccessListener&lt;Location&gt;() {
            <span class="hljs-meta">@Override</span>
            <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onSuccess</span><span class="hljs-params">(Location location)</span> </span>{
                <span class="hljs-keyword">if</span> (location != <span class="hljs-keyword">null</span>) {
                    <span class="hljs-keyword">double</span> latitude = location.getLatitude();
                    <span class="hljs-keyword">double</span> longitude = location.getLongitude();
                    Log.d(<span class="hljs-string">"FusedLocation"</span>, <span class="hljs-string">"Latitude: "</span> + latitude + <span class="hljs-string">", Longitude: "</span> + longitude);
                }
            }
        });
}
</code></pre>
<hr />
<h2 id="heading-3-why-use-fused-location-provider"><strong>3. Why Use Fused Location Provider?</strong></h2>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Feature</td><td>LocationManager</td><td>FusedLocationProviderClient</td></tr>
</thead>
<tbody>
<tr>
<td><strong>Accuracy</strong></td><td>High (GPS) / Medium (Network)</td><td>Automatically optimized</td></tr>
<tr>
<td><strong>Battery Consumption</strong></td><td>High (GPS)</td><td>Low (Smart switching between GPS, Wi-Fi, and sensors)</td></tr>
<tr>
<td><strong>Ease of Use</strong></td><td>Requires manual setup</td><td>Google-managed and optimized</td></tr>
<tr>
<td><strong>Indoor Functionality</strong></td><td>Works poorly indoors (GPS)</td><td>Works well indoors (uses Wi-Fi &amp; sensors)</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-conclusion"><strong>Conclusion</strong></h2>
<p>If you want <strong>high accuracy and better battery performance</strong>, the <strong>Fused Location Provider (FLP)</strong> is the best choice for getting location in Android apps. It <strong>automatically picks the best provider</strong> (GPS, Wi-Fi, or sensors), ensuring efficient location tracking with minimal battery drain.</p>
<p>Would you like to learn how to get <strong>real-time location updates</strong> using FLP? Stay tuned for our next blog! 🚀</p>
]]></content:encoded></item><item><title><![CDATA[How to Build and Serve a Flutter Web App for Production]]></title><description><![CDATA[Flutter is a powerful framework for building cross-platform apps, including web applications. If you’ve developed a Flutter app and want to release it on the web, this guide will walk you through the process of building and serving your Flutter web a...]]></description><link>https://blog.anmolthedeveloper.com/how-to-build-and-serve-a-flutter-web-app-for-production</link><guid isPermaLink="true">https://blog.anmolthedeveloper.com/how-to-build-and-serve-a-flutter-web-app-for-production</guid><category><![CDATA[Flutter]]></category><category><![CDATA[flutter web]]></category><dc:creator><![CDATA[Anmol Singh Tuteja]]></dc:creator><pubDate>Sun, 16 Feb 2025 18:30:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1739864953258/3f958574-c665-4581-b986-bdae1c53a1ab.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Flutter is a powerful framework for building cross-platform apps, including web applications. If you’ve developed a Flutter app and want to release it on the web, this guide will walk you through the process of building and serving your Flutter web app for production.</p>
<hr />
<h2 id="heading-step-1-enable-flutter-web-support">Step 1: Enable Flutter Web Support</h2>
<p>Before building for the web, ensure that Flutter’s web support is enabled on your system. Run the following command:</p>
<pre><code class="lang-sh">flutter doctor
</code></pre>
<p>If web support is not enabled, enable it with:</p>
<pre><code class="lang-sh">flutter config --enable-web
</code></pre>
<hr />
<h2 id="heading-step-2-build-the-web-release-version">Step 2: Build the Web Release Version</h2>
<p>Navigate to your Flutter project directory and run:</p>
<pre><code class="lang-sh">flutter build web
</code></pre>
<p>This will generate an optimized production build inside the <code>build/web/</code> directory.</p>
<hr />
<h2 id="heading-step-3-serve-the-web-app-locally">Step 3: Serve the Web App Locally</h2>
<p>Unlike mobile apps, Flutter doesn’t have a <code>flutter serve</code> command. Instead, you can use the following methods to test your web app locally before deployment.</p>
<h3 id="heading-option-1-run-in-chrome-debug-mode"><strong>Option 1: Run in Chrome (Debug Mode)</strong></h3>
<p>For quick testing, run:</p>
<pre><code class="lang-sh">flutter run -d chrome
</code></pre>
<p>For better performance (closer to release mode), use:</p>
<pre><code class="lang-sh">flutter run -d chrome --profile
</code></pre>
<h3 id="heading-option-2-serve-using-a-simple-http-server"><strong>Option 2: Serve Using a Simple HTTP Server</strong></h3>
<p>After building the web version, you can serve it using an HTTP server.</p>
<h4 id="heading-using-python-if-installed"><strong>Using Python (if installed)</strong></h4>
<pre><code class="lang-sh"><span class="hljs-built_in">cd</span> build/web
python3 -m http.server 8000
</code></pre>
<p>Then, open <a target="_blank" href="http://localhost:8000/"><strong>http://localhost:8000</strong></a> in your browser.</p>
<h4 id="heading-using-nodejs-if-installed"><strong>Using Node.js (if installed)</strong></h4>
<p>First, install <code>http-server</code> globally:</p>
<pre><code class="lang-sh">npm install -g http-server
</code></pre>
<p>Then, serve the app:</p>
<pre><code class="lang-sh"><span class="hljs-built_in">cd</span> build/web
http-server -p 8000
</code></pre>
<h4 id="heading-using-darts-dhttpd"><strong>Using Dart's dhttpd</strong></h4>
<p>If you prefer a Dart-based solution:</p>
<pre><code class="lang-sh">dart pub global activate dhttpd
dhttpd --path build/web --port 8000
</code></pre>
<p>Then, visit <a target="_blank" href="http://localhost:8000/"><strong>http://localhost:8000</strong></a> in your browser.</p>
<hr />
<h2 id="heading-step-4-deploy-your-flutter-web-app">Step 4: Deploy Your Flutter Web App</h2>
<p>Once your web app is ready, you can deploy the <code>build/web/</code> folder to any static hosting service, such as:</p>
<ul>
<li><p><strong>Firebase Hosting</strong></p>
<pre><code class="lang-sh">  firebase deploy
</code></pre>
</li>
<li><p><strong>Vercel</strong> (<code>vercel</code> CLI tool required)</p>
<pre><code class="lang-sh">  vercel build &amp;&amp; vercel deploy
</code></pre>
</li>
<li><p><strong>Netlify</strong> (drag and drop <code>build/web</code> into Netlify dashboard)</p>
</li>
<li><p><strong>GitHub Pages</strong></p>
</li>
<li><p><strong>AWS S3 + CloudFront</strong></p>
</li>
</ul>
<hr />
<h2 id="heading-conclusion">Conclusion</h2>
<p>Building and serving a Flutter web app is straightforward. Just ensure you have web support enabled, create a release build, and serve it locally before deployment. With various hosting options available, you can quickly make your Flutter web app accessible to users worldwide!</p>
<p>Do you have any questions or need help? Drop a comment below!</p>
]]></content:encoded></item><item><title><![CDATA[📲 “Ship Happens!” – A Flutter Dev’s No-BS Guide to Publishing on Play Store & App Store 🚀]]></title><description><![CDATA[Because writing code is easy. Publishing it? That’s where the real boss level begins.

So, you've built your first Flutter app — your widget tree is blooming, animations are smooth, your app actually works (most of the time), and now it’s time to put...]]></description><link>https://blog.anmolthedeveloper.com/ship-happens-a-flutter-devs-no-bs-guide-to-publishing-on-play-store-and-app-store</link><guid isPermaLink="true">https://blog.anmolthedeveloper.com/ship-happens-a-flutter-devs-no-bs-guide-to-publishing-on-play-store-and-app-store</guid><category><![CDATA[Flutter]]></category><category><![CDATA[Android]]></category><category><![CDATA[iOS]]></category><category><![CDATA[app development]]></category><category><![CDATA[publishing]]></category><dc:creator><![CDATA[Anmol Singh Tuteja]]></dc:creator><pubDate>Thu, 30 Jan 2025 18:30:00 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p><em>Because writing code is easy. Publishing it? That’s where the real boss level begins.</em></p>
</blockquote>
<p>So, you've built your first Flutter app — your widget tree is blooming, animations are smooth, your app actually works (most of the time), and now it’s time to <strong>put it out in the wild</strong>.</p>
<p>But then… BAM! Welcome to the world of <strong>developer consoles, keystores, certificates, screenshots, privacy policies, and platform policies that read like a novel.</strong> 😅</p>
<p>No worries, my fellow Flutternaut. I've been through the trenches, and this is your ultimate guide to publishing your app <em>without pulling your hair out</em>. Let’s take it <strong>one platform at a time</strong>:</p>
<hr />
<h2 id="heading-part-1-google-play-store-androids-playground">👽 PART 1: Google Play Store – Android’s Playground</h2>
<h3 id="heading-step-1-prepare-your-app-for-the-world">🔧 Step 1: Prepare Your App for the World</h3>
<p><strong>1. Give your app a proper identity</strong><br />You're not shipping <code>com.example.flutterdemo</code>. Give it a name, icon, and version like it’s your baby!</p>
<ul>
<li><p>Update your app name:</p>
<pre><code class="lang-xml">  <span class="hljs-comment">&lt;!-- android/app/src/main/AndroidManifest.xml --&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">application</span> <span class="hljs-attr">android:label</span>=<span class="hljs-string">"MyCoolApp"</span>&gt;</span>
</code></pre>
</li>
<li><p>Add a custom icon:</p>
<pre><code class="lang-bash">  flutter pub add flutter_launcher_icons
  flutter pub run flutter_launcher_icons
</code></pre>
<p>  In <code>pubspec.yaml</code>:</p>
<pre><code class="lang-yaml">  <span class="hljs-attr">flutter_icons:</span>
    <span class="hljs-attr">android:</span> <span class="hljs-literal">true</span>
    <span class="hljs-attr">image_path:</span> <span class="hljs-string">"assets/icon.png"</span>
</code></pre>
</li>
<li><p>Set your version:</p>
<pre><code class="lang-yaml">  <span class="hljs-attr">version:</span> <span class="hljs-number">1.0</span><span class="hljs-number">.0</span><span class="hljs-string">+1</span>
</code></pre>
</li>
</ul>
<p><strong>2. Sign It Like a Pro (AAB)</strong><br />You don’t want Google screaming “UNSIGNED APK!” like a bad horror movie.</p>
<ul>
<li><p>Generate a keystore:</p>
<pre><code class="lang-bash">  keytool -genkey -v -keystore ~/my-release-key.jks -keyalg RSA -keysize 2048 -validity 10000 -<span class="hljs-built_in">alias</span> my-key
</code></pre>
</li>
<li><p>Add <code>key.properties</code> file:</p>
<pre><code class="lang-plaintext">  storePassword=yourpassword
  keyPassword=yourpassword
  keyAlias=my-key
  storeFile=../my-release-key.jks
</code></pre>
</li>
<li><p>In <code>android/app/build.gradle</code>, tell Gradle what to do with your keys:</p>
<pre><code class="lang-plaintext">  def keystoreProperties = new Properties()
  keystoreProperties.load(new FileInputStream(file("key.properties")))

  signingConfigs {
    release {
      storeFile file(keystoreProperties['storeFile'])
      storePassword keystoreProperties['storePassword']
      keyAlias keystoreProperties['keyAlias'])
      keyPassword keystoreProperties['keyPassword'])
    }
  }
</code></pre>
</li>
</ul>
<p><strong>3. Build the bundle (AAB &gt; APK):</strong></p>
<pre><code class="lang-bash">flutter build appbundle
</code></pre>
<p>Look for the <code>.aab</code> file in:<br /><code>build/app/outputs/bundle/release/app-release.aab</code></p>
<hr />
<h3 id="heading-step-2-open-the-gates-google-play-console">🧙 Step 2: Open the Gates – Google Play Console</h3>
<ol>
<li><p>Go to Google Play Console</p>
</li>
<li><p>Pay the <strong>one-time fee of $25</strong> (yes, just once — Google’s surprisingly chill)</p>
</li>
<li><p>Create a new app:</p>
<ul>
<li><p>Title</p>
</li>
<li><p>Default language</p>
</li>
<li><p>App or Game?</p>
</li>
<li><p>Free or Paid?</p>
</li>
</ul>
</li>
</ol>
<hr />
<h3 id="heading-step-3-store-listing-make-it-sexy">🖼 Step 3: Store Listing – Make It Sexy</h3>
<p>You’ll need:</p>
<ul>
<li><p>App title (max 50 chars)</p>
</li>
<li><p>Short description (max 80)</p>
</li>
<li><p>Full description (up to 4000 — tell your story)</p>
</li>
<li><p>App icon (512x512)</p>
</li>
<li><p>Feature graphic (1024x500)</p>
</li>
<li><p>Screenshots (min 2, max 8 per device type)</p>
</li>
<li><p>Privacy policy (hosted on your website or GitHub)</p>
</li>
<li><p>Contact email &amp; website</p>
</li>
</ul>
<hr />
<h3 id="heading-step-4-policies-amp-permissions">☢️ Step 4: Policies &amp; Permissions</h3>
<p>Google will ask you about:</p>
<ul>
<li><p><strong>Data Safety</strong> – Explain what data you collect and why. Be honest. They <em>will</em> find out.</p>
</li>
<li><p><strong>Content rating</strong> – Questionnaire-based.</p>
</li>
<li><p><strong>App access</strong> – Does your app need login? Provide access creds for reviewers.</p>
</li>
<li><p><strong>Ads</strong> – Show ‘em if you got ‘em.</p>
</li>
<li><p><strong>Target audience</strong> – No, it can’t be "everyone"; be specific.</p>
</li>
</ul>
<hr />
<h3 id="heading-step-5-upload-aab-amp-release">🚀 Step 5: Upload AAB &amp; Release</h3>
<ul>
<li><p>Go to <strong>Production &gt; Create Release</strong></p>
</li>
<li><p>Upload your <code>.aab</code> file</p>
</li>
<li><p>Add release notes like “Bug fixes and performance improvements™”</p>
</li>
<li><p>Click <strong>Review &amp; Publish</strong></p>
</li>
</ul>
<p><strong>Google takes 1–7 days</strong> to review new apps. Updates are usually faster (2–3 hours sometimes).</p>
<hr />
<h3 id="heading-android-gotchas-dont-get-rejected">😬 Android Gotchas (Don’t Get Rejected!)</h3>
<ul>
<li><p>Don’t use “WhatsApp”, “YouTube” or other trademarks in your app name.</p>
</li>
<li><p>If your app has login, <strong>provide a test account</strong>.</p>
</li>
<li><p>Don’t fake reviews, clicks, installs. Google ain’t dumb.</p>
</li>
<li><p>Follow the Developer Policy Center</p>
</li>
</ul>
<hr />
<h2 id="heading-part-2-apple-app-store-where-the-rules-are-tight-and-the-vibe-is-serious">🧙 PART 2: Apple App Store – Where the Rules are Tight and the Vibe is Serious 🍏</h2>
<h3 id="heading-step-1-get-a-developer-account">🔧 Step 1: Get a Developer Account</h3>
<ol>
<li><p>Go to <a target="_blank" href="https://developer.apple.com/">developer.apple.com</a></p>
</li>
<li><p>Pay <strong>$99/year</strong> – recurring. Ouch.</p>
</li>
<li><p>Enable 2-Factor Authentication (they <em>love</em> it).</p>
</li>
<li><p>Open Xcode at least once and sign in with your Apple ID.</p>
</li>
</ol>
<hr />
<h3 id="heading-step-2-prepare-ios-app-in-flutter">🛠 Step 2: Prepare iOS App in Flutter</h3>
<ul>
<li><p>Update <code>pubspec.yaml</code> version:</p>
<pre><code class="lang-yaml">  <span class="hljs-attr">version:</span> <span class="hljs-number">1.0</span><span class="hljs-number">.0</span><span class="hljs-string">+1</span>
</code></pre>
</li>
<li><p>Update app name in <code>Info.plist</code>:</p>
<pre><code class="lang-xml">  <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>CFBundleDisplayName<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>MyCoolApp<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
</li>
<li><p>Add permissions to <code>Info.plist</code>:</p>
<pre><code class="lang-xml">  <span class="hljs-tag">&lt;<span class="hljs-name">key</span>&gt;</span>NSCameraUsageDescription<span class="hljs-tag">&lt;/<span class="hljs-name">key</span>&gt;</span>
  <span class="hljs-tag">&lt;<span class="hljs-name">string</span>&gt;</span>We need camera for scanning magic<span class="hljs-tag">&lt;/<span class="hljs-name">string</span>&gt;</span>
</code></pre>
</li>
<li><p>Use this to generate splash screens and icons:</p>
<pre><code class="lang-bash">  flutter pub add flutter_native_splash
  flutter pub run flutter_native_splash:create
</code></pre>
</li>
</ul>
<hr />
<h3 id="heading-step-3-build-amp-archive">📦 Step 3: Build &amp; Archive</h3>
<ol>
<li><p>Open <code>ios/Runner.xcworkspace</code> in Xcode.</p>
</li>
<li><p>Set:</p>
<ul>
<li><p>Version &amp; Build number</p>
</li>
<li><p>Team and Bundle ID</p>
</li>
<li><p>Capabilities (e.g., push, location)</p>
</li>
</ul>
</li>
<li><p>From the menu:<br /> <code>Product &gt; Archive</code><br /> (Go grab a coffee. Slow builds build character.)</p>
</li>
<li><p>Xcode Organizer will open. Click <strong>Distribute App &gt; App Store Connect</strong></p>
</li>
</ol>
<hr />
<h3 id="heading-step-4-app-store-connect-setup">🌐 Step 4: App Store Connect Setup</h3>
<ol>
<li><p>Go to <a target="_blank" href="https://appstoreconnect.apple.com/">App Store Connect</a></p>
</li>
<li><p>Create a new app:</p>
<ul>
<li><p>Name, language, bundle ID</p>
</li>
<li><p>SKU (any unique string you want)</p>
</li>
</ul>
</li>
<li><p>Fill in:</p>
<ul>
<li><p>App description</p>
</li>
<li><p>Screenshots (iPhone 6.5", 5.5", iPad if supported)</p>
</li>
<li><p>App icon (1024x1024)</p>
</li>
<li><p>Keywords, support email, privacy policy</p>
</li>
</ul>
</li>
</ol>
<hr />
<h3 id="heading-step-5-submit-for-review">🔎 Step 5: Submit for Review</h3>
<ol>
<li><p>Select your build uploaded via Xcode</p>
</li>
<li><p>Add app privacy details (very strict)</p>
</li>
<li><p>Submit for review</p>
</li>
</ol>
<p><strong>Apple takes 1–3 days</strong> for first reviews. And yes, <strong>they might reject you for a misaligned button</strong>.</p>
<hr />
<h3 id="heading-apple-gotchas">😱 Apple Gotchas</h3>
<ul>
<li><p>Don't ship broken builds. <strong>They will reject it.</strong></p>
</li>
<li><p>Don’t use placeholder text or lorem ipsum anywhere.</p>
</li>
<li><p>Don’t access user data unless absolutely necessary.</p>
</li>
<li><p>Make sure your UI isn’t cut off or broken on iPhone SE.</p>
</li>
</ul>
<p>Check <a target="_blank" href="https://developer.apple.com/app-store/review/guidelines/">Apple’s App Store Guidelines</a></p>
<hr />
<h2 id="heading-how-to-publish-an-update-both-stores">🔁 How to Publish an Update (Both Stores)</h2>
<h3 id="heading-android">Android:</h3>
<ul>
<li><p>Bump version &amp; build number</p>
</li>
<li><p>Rebuild:</p>
<pre><code class="lang-bash">  flutter build appbundle
</code></pre>
</li>
<li><p>Go to Play Console → Create a new release → Upload → Publish</p>
</li>
</ul>
<h3 id="heading-ios">iOS:</h3>
<ul>
<li><p>Bump version &amp; build in Xcode</p>
</li>
<li><p>Archive and upload via Xcode</p>
</li>
<li><p>Go to App Store Connect → Add to existing version → Submit</p>
</li>
</ul>
<hr />
<h2 id="heading-automation-once-youve-done-it-manually">⚙️ Automation – Once You’ve Done It Manually</h2>
<blockquote>
<p><em>Because doing this 100 times manually would age you faster than Flutter’s state management debates.</em></p>
</blockquote>
<h3 id="heading-tools-youll-love">🛠 Tools You’ll Love:</h3>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Tool</td><td>Purpose</td></tr>
</thead>
<tbody>
<tr>
<td><strong>fastlane</strong></td><td>Automate iOS + Android deploy steps</td></tr>
<tr>
<td><strong>Codemagic</strong></td><td>CI/CD for Flutter (build + publish)</td></tr>
<tr>
<td><strong>GitHub Actions</strong></td><td>Build and deploy with versioning</td></tr>
<tr>
<td><strong>Bitrise</strong></td><td>Visual pipeline for mobile CI/CD</td></tr>
<tr>
<td><strong>pubspec_version</strong></td><td>Auto bump version in <code>pubspec.yaml</code></td></tr>
<tr>
<td><strong>flutter_launcher_icons</strong></td><td>Icon generation</td></tr>
<tr>
<td><strong>flutter_native_splash</strong></td><td>Splash screen automation</td></tr>
</tbody>
</table>
</div><hr />
<h2 id="heading-final-tips-aka-dev-wisdom-from-battle-scars">🧠 Final Tips (aka: Dev Wisdom from Battle Scars)</h2>
<ul>
<li><p><strong>Test on real devices</strong> before shipping — simulators lie.</p>
</li>
<li><p>Don’t leave <code>debugShowCheckedModeBanner: true</code> — ever.</p>
</li>
<li><p><strong>Save your keystore and credentials</strong> somewhere safe (Google Drive is not "safe").</p>
</li>
<li><p>Double-check all store descriptions, images, and privacy texts before submitting.</p>
</li>
</ul>
<hr />
<h2 id="heading-tldr">🤘 TL;DR:</h2>
<ul>
<li><p>✅ Build &amp; sign your app properly.</p>
</li>
<li><p>✅ Upload AAB to Play Store and archive iOS with Xcode.</p>
</li>
<li><p>✅ Fill out all forms (they love forms).</p>
</li>
<li><p>✅ Submit and <em>hope</em> for the best.</p>
</li>
<li><p>✅ Automate when you get tired of the repetition.</p>
</li>
</ul>
<hr />
<p><strong>Publishing is hard. But you’re harder.</strong></p>
<p>Go ahead, take your masterpiece and launch it. The world deserves your Flutter magic — just remember, when “ship happens,” you're not alone.</p>
]]></content:encoded></item><item><title><![CDATA[How to Send Mock SMS Using ADB on an Android Emulator]]></title><description><![CDATA[In this blog, I will walk you through setting up an Android emulator, turning it on, and sending mock SMS messages using Android Debug Bridge (ADB). This guide is designed for developers who need to simulate SMS behavior for app testing. Let’s dive i...]]></description><link>https://blog.anmolthedeveloper.com/how-to-send-mock-sms-using-adb-on-an-android-emulator</link><guid isPermaLink="true">https://blog.anmolthedeveloper.com/how-to-send-mock-sms-using-adb-on-an-android-emulator</guid><category><![CDATA[Android]]></category><category><![CDATA[adb]]></category><category><![CDATA[Android Emulator]]></category><category><![CDATA[emulators]]></category><category><![CDATA[sms]]></category><dc:creator><![CDATA[Anmol Singh Tuteja]]></dc:creator><pubDate>Sat, 25 Jan 2025 08:06:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740276927293/0e1e160c-8d70-44ed-85be-8c9c68f76653.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In this blog, I will walk you through setting up an Android emulator, turning it on, and sending mock SMS messages using Android Debug Bridge (ADB). This guide is designed for developers who need to simulate SMS behavior for app testing. Let’s dive in!</p>
<hr />
<h2 id="heading-1-setting-up-the-android-emulator-optional">1. Setting Up the Android Emulator (Optional)</h2>
<p>If you already have an emulator set up, feel free to skip this section. Otherwise, follow these steps:</p>
<h3 id="heading-install-android-studio">Install Android Studio</h3>
<ul>
<li><p>Download and install <strong>Android Studio</strong> from <a target="_blank" href="https://developer.android.com/studio">developer.android.com</a>.</p>
</li>
<li><p>During installation, ensure the <strong>Android Virtual Device (AVD)</strong> Manager is selected.</p>
</li>
</ul>
<h3 id="heading-create-an-emulator">Create an Emulator</h3>
<ol>
<li><p>Open Android Studio and go to <strong>Tools &gt; Device Manager</strong>.</p>
</li>
<li><p>Click on <strong>Create Device</strong>.</p>
</li>
<li><p>Select a hardware profile (e.g., <strong>Pixel 5</strong>) and click <strong>Next</strong>.</p>
</li>
<li><p>Choose a system image (e.g., <strong>Android 13.0</strong>) and download it if not already available.</p>
</li>
<li><p>Configure the emulator settings, such as RAM and storage, and click <strong>Finish</strong>.</p>
</li>
</ol>
<p>Now, you have an emulator ready to use.</p>
<hr />
<h2 id="heading-2-turning-on-the-emulator">2. Turning On the Emulator</h2>
<p>Before sending a mock SMS, you need to start the emulator.</p>
<h3 id="heading-using-android-studio">Using Android Studio</h3>
<ul>
<li><p>Open the <strong>Device Manager</strong> in Android Studio.</p>
</li>
<li><p>Click the <strong>Play</strong> button next to your emulator.</p>
</li>
</ul>
<h3 id="heading-using-the-emulator-command-cli">Using the Emulator Command (CLI)</h3>
<ol>
<li><p>Ensure the <code>emulator</code> executable is available in your PATH. It is typically located in the <code>\\&lt;Android SDK path&gt;\\emulator\\</code> directory.</p>
</li>
<li><p>Open a terminal or command prompt.</p>
</li>
<li><p>List available emulators:</p>
<pre><code class="lang-plaintext"> &lt;Android SDK path&gt;/emulator/emulator -list-avds
</code></pre>
<p> Replace <code>&lt;Android SDK path&gt;</code> with the actual location of your Android SDK.</p>
</li>
<li><p>Start an emulator:</p>
<pre><code class="lang-plaintext"> &lt;Android SDK path&gt;/emulator/emulator -avd &lt;emulator_name&gt;
</code></pre>
<p> Replace <code>&lt;emulator_name&gt;</code> with the name of your emulator (e.g., <code>Pixel_5_API_33</code>).</p>
</li>
</ol>
<h3 id="heading-checking-emulator-status">Checking Emulator Status</h3>
<p>Use the following command to confirm that the emulator is running:</p>
<pre><code class="lang-plaintext">adb devices
</code></pre>
<p>You should see your emulator listed with the status <code>device</code>.</p>
<hr />
<h2 id="heading-3-sending-a-mock-sms">3. Sending a Mock SMS</h2>
<p>ADB allows you to send mock SMS messages to an emulator, making it easy to test SMS-related features in your app.</p>
<h3 id="heading-command-to-send-sms">Command to Send SMS</h3>
<p>Use the following command to send a mock SMS:</p>
<pre><code class="lang-plaintext">adb emu sms send &lt;phone_number&gt; "&lt;message_content&gt;"
</code></pre>
<h3 id="heading-example">Example</h3>
<pre><code class="lang-plaintext">adb emu sms send 5551234567 "Hello, this is a test message!"
</code></pre>
<ul>
<li><p><code>5551234567</code>: The sender’s phone number (use any dummy number for testing).</p>
</li>
<li><p><code>Hello, this is a test message!</code>: The content of the SMS.</p>
</li>
</ul>
<h3 id="heading-verify-sms-on-the-emulator">Verify SMS on the Emulator</h3>
<ol>
<li><p>Open the default <strong>Messages</strong> app on the emulator.</p>
</li>
<li><p>Check the received SMS to verify that it matches the mock data you sent.</p>
</li>
</ol>
<hr />
<h2 id="heading-explanation-of-adb-commands">Explanation of ADB Commands</h2>
<h3 id="heading-key-commands">Key Commands</h3>
<ol>
<li><p><code>adb devices</code> Lists all connected devices and emulators.</p>
<pre><code class="lang-plaintext"> adb devices
</code></pre>
<p> Output example:</p>
<pre><code class="lang-plaintext"> List of devices attached
 emulator-5554   device
</code></pre>
</li>
<li><p><code>adb emu</code> Sends commands to the emulator. For example, the <code>sms send</code> subcommand allows sending mock SMS.</p>
</li>
<li><p><code>adb shell</code> Opens a shell session on the emulator for advanced debugging.</p>
<pre><code class="lang-plaintext"> adb shell
</code></pre>
</li>
</ol>
<h3 id="heading-related-commands">Related Commands</h3>
<ul>
<li><p><strong>Restart the Emulator</strong>:</p>
<pre><code class="lang-plaintext">  adb emu kill
  &lt;Android SDK path&gt;/emulator/emulator -avd &lt;emulator_name&gt;
</code></pre>
</li>
<li><p><strong>Clear App Data</strong>:</p>
<pre><code class="lang-plaintext">  adb shell pm clear &lt;package_name&gt;
</code></pre>
</li>
<li><p><strong>Install an APK</strong>:</p>
<pre><code class="lang-plaintext">  adb install &lt;path_to_apk&gt;
</code></pre>
</li>
<li><p><strong>Uninstall an App</strong>:</p>
<pre><code class="lang-plaintext">  adb uninstall &lt;package_name&gt;
</code></pre>
</li>
</ul>
<hr />
<h2 id="heading-common-issues-and-troubleshooting">Common Issues and Troubleshooting</h2>
<ul>
<li><p><strong>Command Not Found for</strong> <code>emulator</code>: Ensure the <code>emulator</code> executable is in your PATH. If not, specify the full path to the executable as shown above.</p>
</li>
<li><p><strong>Emulator Not Listed in</strong> <code>adb devices</code>: Ensure the emulator is running and properly configured. Restart the ADB server:</p>
<pre><code class="lang-plaintext">  adb kill-server
  adb start-server
</code></pre>
</li>
<li><p><strong>Mock SMS Not Received</strong>: Check the Messages app settings and ensure the emulator supports SMS simulation.</p>
</li>
</ul>
<hr />
<h2 id="heading-conclusion">Conclusion</h2>
<p>Using ADB to send mock SMS messages is a powerful tool for testing SMS-related features in Android apps. By setting up an emulator, starting it, and running simple commands, you can simulate real-world scenarios and streamline your development process.</p>
]]></content:encoded></item><item><title><![CDATA[How I achieved Flutter Background Location Tracking ? (even when app is being killed)]]></title><description><![CDATA[After extensive research and trial, I recently discovered a reliable method for achieving background location tracking in Flutter something many other developers have been pursuing as well. Although there’s a package on pub.dev that provides backgrou...]]></description><link>https://blog.anmolthedeveloper.com/how-i-achieved-flutter-background-location-tracking-even-when-app-is-being-killed</link><guid isPermaLink="true">https://blog.anmolthedeveloper.com/how-i-achieved-flutter-background-location-tracking-even-when-app-is-being-killed</guid><category><![CDATA[Flutter]]></category><category><![CDATA[location ]]></category><category><![CDATA[Android]]></category><dc:creator><![CDATA[Anmol Singh Tuteja]]></dc:creator><pubDate>Wed, 30 Oct 2024 17:47:16 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1740276717319/792e5c5e-0f5d-45c8-890b-538df88b53c1.webp" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>After extensive research and trial, I recently discovered a reliable method for achieving background location tracking in Flutter something many other developers have been pursuing as well. Although there’s a package on pub.dev that provides background location tracking and geolocation functionalities, it comes with a license fee of around $375/year per app. For my project, this wasn’t feasible, so I set out to develop an alternative approach using Android’s native capabilities.</p>
<p>I realized that the most robust way to ensure consistent location tracking in the background, even when the app is closed, was through a foreground service. Unlike other services that may be terminated by Android OS to conserve resources, a foreground service has greater longevity. I configured it to display latitude and longitude updates in the notification body as confirmation that background location tracking was working correctly.</p>
<p>Once I achieved reliable location tracking, I integrated this code into the Android section of my Flutter app. Additionally, I enabled it to push location data to Firebase directly from the foreground service. One key insight from this journey was understanding why certain packages fail to maintain location tracking when the app is terminated. Most packages run on the main UI thread, which ceases once the app is closed. In contrast, the foreground service operates on a separate thread, allowing it to continue running independently of the app’s lifecycle. Therefore, any API calls or data pushes to Firebase should be implemented within the service thread to ensure they persist, even if the app is killed.</p>
<p>This approach has proven highly effective for continuous background location tracking in Flutter applications, providing a cost-effective alternative to third-party packages. While I won’t be diving into the code specifics here, I wanted to share this journey to hopefully inspire other Flutter developers facing similar challenges.</p>
]]></content:encoded></item></channel></rss>