Flutter to Native Code Migration: Setup | 2/5
Planning Your Migration: Architecture and Project Setup

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
Introduction
Migrating a Flutter app to native Android (Kotlin + Jetpack Compose) and iOS (Swift + SwiftUI) with MongoDB with custom backend as the backend can unlock native capabilities while retaining your scalable backend. Planning your architecture and project setup properly ensures a smooth transition.
Project Setup
a) Android
Start by creating a new Android project in Android Studio. Once your project is initialized, follow these steps to configure dependancies:
- In your
root/build.gradle.kts, ensure you have:
plugins {
alias(libs.plugins.android.application) apply false // Android application plugin
alias(libs.plugins.kotlin.android) apply false // Kotlin support for Android
alias(libs.plugins.kotlin.compose) apply false // Jetpack Compose compiler plugin
alias(libs.plugins.dagger.hilt) apply false // Hilt for dependency injection
alias(libs.plugins.devtools.ksp) apply false // Kotlin Symbol Processing (used by Room and Hilt packages)
}
- In your
app/build.gradle.kts, ensure you have:
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.dagger.hilt)
alias(libs.plugins.devtools.ksp)
kotlin("plugin.serialization") version "2.1.21"
}
at the top and in the depencies you may want to organize them this way
dependencies {
// Core AndroidX & Lifecycle
implementation(libs.androidx.core.ktx) // Kotlin extensions for core Android APIs
implementation(libs.androidx.lifecycle.runtime.ktx) // Lifecycle-aware components
implementation(libs.androidx.core.splashscreen) // Splash screen API
// Jetpack Compose - BOM
implementation(platform(libs.androidx.compose.bom)) // Compose Bill of Materials (BOM)
// Jetpack Compose - Core UI
implementation(libs.androidx.activity.compose) // Activity support for Compose
implementation(libs.androidx.ui) // Core Compose UI elements
implementation(libs.androidx.ui.graphics) // Compose graphics primitives
// Jetpack Compose - Material Design
implementation(libs.androidx.material3) // Material 3 UI components
implementation(libs.androidx.compose.material) // Material 2 components (legacy support)
implementation(libs.androidx.icons.extended) // Extended material icons
// Jetpack Compose - Navigation & State
implementation(libs.compose.navigation) // Navigation support in Compose
implementation(libs.compose.hilt.navigation) // Hilt + Navigation integration
implementation(libs.androidx.compose.livedata) // LiveData support in Compose
// Jetpack Compose - Tooling & Preview
implementation(libs.androidx.ui.tooling.preview) // Compose UI preview support
debugImplementation(libs.androidx.ui.tooling) // Compose UI tools (debug only)
debugImplementation(libs.androidx.ui.test.manifest) // Manifest for Compose UI tests (debug)
// Navigation (Non-Compose)
implementation(libs.androidx.navigation) // AndroidX Navigation components
// Room Database
implementation(libs.androidx.room.runtime) // Room runtime
ksp(libs.androidx.room.compiler) // Room annotation processor (KSP)
annotationProcessor(libs.androidx.room.compiler) // Annotation processor (for fallback/tests)
// Hilt for Dependency Injection
implementation(libs.hilt.android) // Hilt core
ksp(libs.hilt.compiler) // Hilt code generation
kspAndroidTest(libs.hilt.android.compiler) // Hilt codegen for instrumentation tests
// Networking (Retrofit + OkHttp)
implementation(libs.squareup.retrofit) // Retrofit for networking
implementation(libs.squareup.retrofit.gson) // Gson converter for Retrofit
implementation(libs.squareup.okhttp3.logging) // OkHttp logging interceptor
// Testing - Unit Tests
testImplementation(libs.junit) // JUnit for unit testing
// Testing - Android Instrumentation Tests
androidTestImplementation(libs.androidx.junit) // AndroidX JUnit extensions
androidTestImplementation(libs.androidx.espresso.core) // Espresso UI test framework
androidTestImplementation(platform(libs.androidx.compose.bom)) // Compose BOM for tests
androidTestImplementation(libs.androidx.ui.test.junit4) // Compose JUnit4 test support
}
- Now sync your project in readiness for the next setup of your project
b) iOS
Start by creating a new iOS project in Xcode using Swift + SwiftUI. Once your project is initialized, follow these steps to configure dependencies:
Add Dependencies via the Swift Package Manager by going to:
File → Add Packages
Add Swinject by entering:
https://github.com/Swinject/Swinject.gitand select the latest stable version. All other packages are added that way.
Project Architecture
After setting up dependencies and basic wiring, it is important to have a clean architecture that can scale and remain testable across Android, and iOS.
I worked maintaining similar layering that was already using in Flutter:
Maintaining similar layering across Android and iOS
Core – Dependency injection, constants, environment configurations, helpers, utilities.
Data – Data models, data sources (local and remote), mappers, DTOs.
Domain – Entities, repositories (interfaces and implementations).
Presentation – Screens/views, view models/BLoCs, UI components, themes.
This was my flutter setup
lib
├── core
│ ├── di
│ ├── injectible.dart
│ ├── utils
├── data
│ ├── models
│ ├── song.dart
│ ├── sources
│ ├── local
│ ├── daos (Domain Access Objects)
│ ├── app_database.dart
│ ├── remote
├── domain
│ ├── repository
│ ├── song_repository.dart
├── presentation
│ ├── blocs
│ ├── selection
│ ├── home
│ ├── screens
│ ├── selection
│ ├── home
│ ├── widgets
├── app.dart (boostrapper)
└── main.dart (app launcher/initializer)
Uses BLoC for state management.
Local data (Floor) and remote data (backend client).
Dependency injection via Injectable and GetIt.
Keeps UI logic in screens, while reusable pieces of code in widgets.
Environment and constants in core/utils/env.
a) Android (Kotlin, Jetpack Compose)
app
├── core
│ ├── di
│ ├── ├── AppModule.kt
│ ├── ├── NetworkModule.kt
│ ├── utils
├── data
│ ├── models
│ ├── Song.kt
│ ├── samples
│ ├── sources
│ ├── local
│ ├── AppDaos.kt (Data Access Objects)
│ ├── AppDatabase.kt
│ ├── remote
│ ├── ApiService.kt
├── domain
│ ├── entity
│ ├── UiState.kt
│ ├── repository
│ ├── SongRepository.kt
├── presentation
│ ├── components
│ ├── screens
│ ├── selection
│ ├── home
│ ├── theme
│ ├── viewmodels
│ ├── SelectionViewModel.kt
│ ├── HomeViewModel.kt
├── MainActivity.kt
└── SongLibApp.kt
Uses ViewModel + StateFlow for state management.
Dependency Injection via Koin/Hilt.
Room for local caching, Retrofit for remote.
UI uses Jetpack Compose with components for reusable UI blocks.
b) iOS (SwiftUI)
MyNewApp
├── Core
│ ├── Di
│ ├── DependencyMap.swift
│ ├── DiContainer.swift
│ ├── Utils
├── Data
│ ├── Models
│ ├── Song.swift
│ ├── Samples
│ ├── Sources
│ ├── Local
│ ├── CoreDataManager.swift
│ ├── SongDataManager.swift
│ ├── MyNewApp.xcdatamodelId
│ ├── Remote
│ ├── ApiService.swift
│ ├── EndPoint.swift
├── Domain
│ ├── Entity
│ ├── Repository
│ ├── SongRepository.swift
├── Presentation
│ ├── Components
│ ├── Views
│ ├── Selection
│ ├── Home
│ ├── Theme
│ ├── ViewModels
│ ├── SelectionViewModel.swift
│ ├── HomeViewModel.swift
├── Assets.xcassets
├── ContentView.swift
└── SongLibApp.swift
Uses ObservableObject + @Published or Combine for state management.
Dependency injection via lightweight DependencyMap and DiContainer.
Uses Core Data for local, ApiService.swift for remote.
SwiftUI structures with Views and reusable Components.
DTO and Core Data entity mapping handled explicitly in
Mappers.
Strengths in a clean structure:
Consistent Layering: Core, Data, Domain, Presentation clearly separate concerns hence its easy to understand and maintain.
Alignment Across Platforms: The same mental model applies for Flutter (BLoC), Android (ViewModel + StateFlow), and iOS (ObservableObject/Combine). DTOs and entity mappers handled explicitly, ensuring testability and separation of network vs. app models.
Testability & Modularity: Dependency Injection handled cleanly (
Injectable,Koin/Hilt,DependencyMap/Container). Local storage handled (Floor, Room, Core Data) while ApiService remains the source of truth.UI Organization: Presentation is split into screens/views, view models/BLoCs, and reusable components/widgets, keeping screens clean.
Bootstrap & Entry Separation:
main.dart/MyNewApp.kt/MyNewApp.swiftas app launchers.app.dart/ContentView.swiftas bootstrap entry, further isolating setup.Mappers: You explicitly handle DTO ↔ Entity ↔ Local mappings in dedicated files across all platforms. This prevents leaky abstractions and future issues when the API or local storage changes.
Sample Data: The
samplesfolder is clean for seeding/testing, consider documenting its purpose in your repo for contributors.Future Expansion: Consider having a
usecasessubfolder insidedomainif you add business logic later, but for a small iOS project.
In the next article, we will cover Data Handling and Mapping strategies, including DTOs, entities, and why mapping layers matter when migrating.



