Flutter to Native Code Migration: Summary | 5/5

Siro DavesSiro Daves
3 min read

Table of contents

This is a continuation of my articles on migrating from Flutter to Native code and on this particle article I summarise everyting I have covered in this series and I hope you will learn from it.


Introduction

Migrating your Flutter app to native Android (Kotlin + Jetpack Compose) and iOS (Swift + SwiftUI) while using Supabase requires:


  1. Dependency Setup
  • Flutter: Use supabase_flutter (built-in client, no manual client creation).

  • Android: Add Supabase, Ktor, Room, and Hilt dependencies in build.gradle.kts.

  • iOS: Add supabase-swift and Swinject via Swift Package Manager.

  1. Access Credentials
  • Flutter:

    • Use --dart-define (secure, CI-friendly) or .env (dev-friendly) to inject SUPABASE_URL and SUPABASE_ANON_KEY.
  • Android:

    • Store credentials in local.properties and expose via BuildConfig using buildConfigField.
  • iOS:

    • Store credentials securely in Secrets.plist (excluded from version control) and load at runtime.
  1. Project Architecture Consistency: maintain the same layered architecture:

    • Core: DI, utils, configs.

    • Data: DTOs, models, local/remote sources.

    • Domain: Entities, repositories.

    • Presentation: Screens/views, view models (or BLoCs), reusable components.

This keeps your mental model consistent across Flutter, Android, and iOS, easing onboarding and maintenance.

  1. Supabase Client Initialization

    • Flutter: Initialize in main.dart with Supabase.initialize(...) and access globally via Supabase.instance.client.

    • Android: Manually create SupabaseClient in a SupabaseModule using Hilt, and inject it where needed.

    • iOS: Manually create and manage SupabaseClient within a SupabaseService injected via Swinject.

  2. Invoking Supabase in your code

    Flutter:

     final result = await Supabase.instance.client.from('fruits').select();
    

    Android:

     val result = supabase["fruits"].select().decodeList<FruitDto>()
    

    iOS:

     let results: [FruitDTO] = try await supabase.client.from("fruits").select().execute().value
    
  3. Data Mapping (DTO → Entity)

  • Flutter: No explicit mapping needed, use Fruit.fromJson(item) directly.

  • Android: Use mappers to convert FruitDto (network layer) to Fruit (domain layer) for type safety and clean separation.

  • iOS: Use mappers to convert FruitDTO to Fruit, handling optionals and ensuring SwiftUI views get clean, non-optional data.

Key Benefits of This Approach:

  • Consistency: Maintain the same structure across platforms, simplifying maintenance and future feature expansion.

  • Testability: Clear separation of concerns with DI and explicit layers allows unit testing and easy mocking.

  • Scalability: Supports future growth (adding new features or endpoints) without architectural rewrites.

  • Supabase-centric simplicity: Leverage Supabase’s powerful Postgres + Auth while retaining native platform strengths.

Final Thought:

Migrating a Flutter app to native platforms with Supabase is straightforward if you:

  • Plan your architecture,

  • Handle credentials securely,

  • Use clear dependency management,

  • And maintain consistency across platforms.


This lets you fully leverage native platform capabilities while maintaining your backend could be Supabase or any other you are using, enabling you to build scalable, maintainable, and testable native apps with a clean developer experience.

0
Subscribe to my newsletter

Read articles from Siro Daves directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Siro Daves
Siro Daves

Software engineer and a Technical Writer, Best at Flutter mobile app development, full stack development with Mern. Other areas are like Android, Kotlin, .Net and Qt