<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Syed Waleed Nawaz</title>
    <description>The latest articles on DEV Community by Syed Waleed Nawaz (@syedwaleednawaz).</description>
    <link>https://dev.to/syedwaleednawaz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4201072%2F0fd7fa4b-acd3-457e-9c11-37b3a3203b05.png</url>
      <title>DEV Community: Syed Waleed Nawaz</title>
      <link>https://dev.to/syedwaleednawaz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/syedwaleednawaz"/>
    <language>en</language>
    <item>
      <title>Flutter Clean Architecture with BLoC or Impeller Migration article</title>
      <dc:creator>Syed Waleed Nawaz</dc:creator>
      <pubDate>Tue, 27 Oct 2026 19:00:00 +0000</pubDate>
      <link>https://dev.to/syedwaleednawaz/flutter-clean-architecture-with-bloc-or-impeller-migration-article-1gdm</link>
      <guid>https://dev.to/syedwaleednawaz/flutter-clean-architecture-with-bloc-or-impeller-migration-article-1gdm</guid>
      <description>&lt;h2&gt;
  
  
  How to decouple presentation, domain invariants, and data sources in high-throughput mobile products without bloated state files.
&lt;/h2&gt;

&lt;h2&gt;
  
  
  1.
&lt;/h2&gt;

&lt;p&gt;Why Standard Flutter Architecture Crumbles at Scale&lt;br&gt;
Most Flutter tutorials model architecture around simple CRUD flows: a screen directly triggers an HTTP request, parses JSON in-line, and updates a local &lt;code&gt;setState()&lt;/code&gt; or a catch-all controller. In a multi-team production app with concurrent network requests, offline caching, and deep links, this pattern rapidly collapses.&lt;br&gt;
Business rules inevitably bleed into UI widgets. A change to a backend response contract forces rewrites across dozens of screen components. Worse, edge-case failure modes—such as token expiration mid-flow, cellular handovers, and rate-limited endpoints—get handled inconsistently.&lt;br&gt;
Clean Architecture, originally formulated by Robert C. Martin and adapted for Flutter by the community, solves this by enforcing the Dependency Inversion Principle: the core domain never depends on Flutter UI frameworks or third-party database packages.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h2&gt;
  
  
  &lt;strong&gt;Key Takeaway:&lt;/strong&gt; Domain rules (entities, use cases, business validation) must remain 100% pure Dart with zero imports from &lt;code&gt;package:flutter&lt;/code&gt;.
&lt;/h2&gt;
&lt;h2&gt;
  
  
  2. The Three-Layer Contract: Presentation, Domain, Data
&lt;/h2&gt;

&lt;p&gt;In production systems like the multi-vendor &lt;a href="https://syedwaleednawaz.com/projects/wasey-food-multi-vendor-app" rel="noopener noreferrer"&gt;Wasey Food Delivery platform&lt;/a&gt;, we separate every feature module into three distinct layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Domain Layer:&lt;/strong&gt; Contains purely immutable entities, value objects, and abstract repository contracts. It defines what the business does, not how data is transported.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Layer:&lt;/strong&gt; Implements domain repository contracts. It orchestrates local database drivers (Hive, SQLite) and remote REST/GraphQL clients, converting raw JSON DTOs into clean Domain Entities.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Presentation Layer:&lt;/strong&gt; BLoCs (Business Logic Components) and UI widgets. BLoCs consume Domain Use Cases and emit immutable UI states that widgets reactively rebuild upon.&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
dart
// Domain Layer: Abstract Repository Contract (Zero Flutter Dependencies)
abstract class OrderRepository {
  Future&amp;lt;Either&amp;lt;Failure, OrderEntity&amp;gt;&amp;gt; getOrderDetails(String orderId);
  Stream&amp;lt;OrderEntity&amp;gt; watchOrderLiveUpdates(String orderId);
}
// Data Layer: Concrete Implementation with Network Resilience
class OrderRepositoryImpl implements OrderRepository {
  final OrderRemoteDataSource remoteDataSource;
  final OrderLocalDataSource localDataSource;
  OrderRepositoryImpl({
    required this.remoteDataSource,
    required this.localDataSource,
  });
  @override
  Future&amp;lt;Either&amp;lt;Failure, OrderEntity&amp;gt;&amp;gt; getOrderDetails(String orderId) async {
    try {
      final model = await remoteDataSource.fetchOrder(orderId);
      await localDataSource.cacheOrder(model);
      return Right(model.toEntity());
    } on SocketException {
      // Graceful offline fallback from local disk
      final cached = await localDataSource.getLastCachedOrder(orderId);
      if (cached != null) return Right(cached.toEntity());
      return Left(NetworkFailure(message: 'No connectivity and no cache found.'));
    } on ServerException catch (e) {
      return Left(ServerFailure(message: e.message, statusCode: e.statusCode));
    }
  }
}

Data layer repository implementation mapping DTOs to Domain Entities with offline fallback.

3. Treating BLoC States as Finite State Machines
A common anti-pattern in Flutter BLoC is creating a single monolithic state with twenty optional nullable fields. This causes race conditions and unexpected widget rebuilds.

In production, every BLoC should operate as a strict Finite State Machine (FSM). For an order tracking screen, the state cannot simultaneously be 'loading' and 'completed'. Use sealed classes (or freezed unions) to declare exhaustive states: Initial, Loading, ActiveTracking, and Error.


dart
// Sealed class pattern in modern Dart 3+
sealed class OrderTrackingState {
  const OrderTrackingState();
}
class OrderTrackingInitial extends OrderTrackingState {
  const OrderTrackingInitial();
}
class OrderTrackingLoading extends OrderTrackingState {
  final String statusMessage;
  const OrderTrackingLoading({this.statusMessage = 'Connecting...'});
}
class OrderTrackingActive extends OrderTrackingState {
  final OrderEntity order;
  final LatLng driverLocation;
  final int etaMinutes;
  const OrderTrackingActive({
    required this.order,
    required this.driverLocation,
    required this.etaMinutes,
  });
}
class OrderTrackingFailure extends OrderTrackingState {
  final String errorMessage;
  final bool isRetryable;
  const OrderTrackingFailure({
    required this.errorMessage,
    this.isRetryable = true,
  });
}

Key Takeaway: When writing switch statements over sealed BLoC states, the Dart analyzer enforces compile-time handling of every possible edge case.

4. Real-World Lessons from Shipped Apps

Deploying Clean Architecture across multi-vendor logistics (Wasey) and enterprise clinical healthcare systems (Health Automated) provided three indispensable takeaways:

1. Avoid Pass-Through Use Cases: Do not create unnecessary Use Cases for trivial pass-throughs. If a repository method simply returns a stream without business transformation, exposing the repository interface directly to the BLoC avoids useless boilerplate classes.

2. Strictly Enforce DTO-to-Entity Transformations: Enforce transformation strictly at data layer boundaries. Never allow an auto-generated JSON model with string keys to leak into your UI widgets.

3. Test Business Logic in Isolation: Because our domain and BLoC layers have zero dependencies on Flutter rendering trees, we achieve 90%+ unit test coverage running in under 3 seconds in our GitHub Actions CI pipeline.

About the Author
I am Syed Waleed Nawaz, a Senior Flutter Developer &amp;amp; Mobile Backend Engineer with 5+ years shipping production applications across iOS, Android, and Web for US, UK, and UAE startups.

Portfolio: syedwaleednawaz.com
Need architecture consulting or Flutter app engineering? Explore my Flutter App Development Services or Contact Me
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>architecture</category>
      <category>cleancode</category>
    </item>
    <item>
      <title>Why I Replaced Firebase with Supabase in My Flutter Projects (And Never Looked Back)</title>
      <dc:creator>Syed Waleed Nawaz</dc:creator>
      <pubDate>Thu, 22 Oct 2026 18:40:04 +0000</pubDate>
      <link>https://dev.to/syedwaleednawaz/why-i-replaced-firebase-with-supabase-in-my-flutter-projects-and-never-looked-back-3jac</link>
      <guid>https://dev.to/syedwaleednawaz/why-i-replaced-firebase-with-supabase-in-my-flutter-projects-and-never-looked-back-3jac</guid>
      <description>&lt;p&gt;After shipping 100+ Flutter projects, I switched from Firebase to Supabase — and the difference was immediate. Real SQL queries, real-time that just works, Row Level Security baked in, and no more surprise bills. In this post I break down exactly why I made the switch, show real Flutter code side-by-side, and explain the one case where I still reach for Firebase.&lt;/p&gt;

</description>
      <category>flutter</category>
      <category>dart</category>
      <category>supabase</category>
      <category>firebase</category>
    </item>
  </channel>
</rss>
