Flutter to Native Code Migration: Summary | 5/5

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:
- 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
andSwinject
via Swift Package Manager.
- Access Credentials
Flutter:
- Use
--dart-define
(secure, CI-friendly) or.env
(dev-friendly) to injectSUPABASE_URL
andSUPABASE_ANON_KEY
.
- Use
Android:
- Store credentials in
local.properties
and expose viaBuildConfig
usingbuildConfigField
.
- Store credentials in
iOS:
- Store credentials securely in
Secrets.plist
(excluded from version control) and load at runtime.
- Store credentials securely in
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.
Supabase Client Initialization
Flutter: Initialize in
main.dart
withSupabase.initialize(...)
and access globally viaSupabase.instance.client
.Android: Manually create
SupabaseClient
in aSupabaseModule
using Hilt, and inject it where needed.iOS: Manually create and manage
SupabaseClient
within aSupabaseService
injected via Swinject.
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
Data Mapping (DTO → Entity)
Flutter: No explicit mapping needed, use
Fruit.fromJson(item)
directly.Android: Use mappers to convert
FruitDto
(network layer) toFruit
(domain layer) for type safety and clean separation.iOS: Use mappers to convert
FruitDTO
toFruit
, 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.
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