LingCode reads your Flutter repo as a design spec — not source to translate — and writes idiomatic SwiftUI, Jetpack Compose, and macOS SwiftUI from scratch. No Dart in the output. No "Flutter-flavored" native code. Plan it in five phases so LingCode doesn't drift halfway through.
The naïve approach is to map each Flutter widget to a native equivalent one-to-one and hope it composes. It doesn't. Three things break:
Provider becomes @Observable+@Environment on iOS but ViewModel+StateFlow on Android. Translating literally produces awkward hybrids.Container with padding, alignment, decoration, and a child is one widget in Flutter — three modifiers in SwiftUI and a Box with modifiers in Compose. Translating widget-by-widget yields nested noise.MethodChannel is a hand-written native hook on each platform. Faking them produces silent runtime breakage.Reading the Flutter source for intent — what screens exist, how they navigate, what data flows where — and then writing the native version fresh produces code a native developer would actually want to maintain.
pubspec.yaml at the root.ios/, android/, and macos/ next to (not inside) your Flutter source. Keeps the Flutter app intact while you migrate.Open the chat panel and ask LingCode to scan the Flutter repo and write a conversion plan. A prompt that works:
Read this Flutter repo and write CONVERSION_PLAN.md in
../native-output/. Summarize:
- pubspec.yaml: deps, Flutter SDK constraint, assets, fonts.
- lib/ tree: every screen/route and its purpose.
- State management: Provider / Riverpod / Bloc / GetX / setState.
- Data layer: HTTP client, local DB (sqflite/Hive/Isar), shared prefs.
- Platform channels and native plugins, listed one by one.
- Theme, typography, color tokens, l10n strings.
- Gotchas: BackdropFilter, CustomPainter, ShaderMask, FFI plugins,
native ads SDKs, Firebase Crashlytics — anything without a clean
native mapping.
Don't start porting yet. Stop after the plan.
LingCode reads the repo, builds a todo list as it scans, and writes CONVERSION_PLAN.md. Read it before approving any code generation. The "gotchas" section is the one that decides whether the project is a two-week job or a two-month job.
For each target platform, LingCode needs to commit to architectural choices before generating code. Ask it to append a per-target architecture section to the plan:
Append to CONVERSION_PLAN.md a section for each of iOS, Android,
macOS that picks:
- Navigation pattern: NavigationStack on Apple, NavHost on Android.
- DI / state container: @Observable+@Environment, or ViewModel+
StateFlow, etc. Match the original Flutter state idiom.
- Networking layer: URLSession + async/await (Apple), OkHttp/Retrofit
(Android), or shared via Ktor if there's a reason to.
- Persistence: SwiftData / Core Data / GRDB (Apple), Room (Android).
- Asset pipeline: where pubspec assets land in each native project.
Where the screen makes sense on macOS, share SwiftUI views between
iOS and macOS — don't duplicate.
This is the last cheap step. Approving the wrong navigation pattern here means rewriting every screen later. Read the architecture section like a code review.
Now ask LingCode to create the empty projects:
Scaffold the iOS, Android, and macOS projects in ../native-output/.
Use Xcode's project format for iOS and macOS (don't hand-write
.xcodeproj XML). Use Gradle for Android (don't hand-write
build.gradle). Bundle IDs:
- iOS: com.yourcompany.yourapp
- Android: com.yourcompany.yourapp
- macOS: com.yourcompany.yourapp.mac
LingCode uses its native scaffolders for this — same shape Xcode and Android Studio produce on "New Project." You should be able to open ios/YourApp.xcodeproj in Xcode and android/ in Android Studio and have both build the empty template.
This is the long phase. Ask LingCode to port one screen at a time, using a todo list to track progress:
Port screens from lib/screens/ to the native projects, one screen
per todo item. For each screen:
1. Read the Flutter widget for intent — screen layout, state flow,
navigation in and out.
2. Write the SwiftUI view (iOS + macOS where it makes sense).
3. Write the Compose composable (Android).
4. Wire navigation in both NavigationStack and NavHost.
5. Wire state into the chosen container.
For widgets with no clean native mapping, stub with
// TODO(flutter-to-native): ... and log it in CONVERSION_PLAN.md
under "deferred." Don't fake behavior.
Start with the home screen. Stop after each screen for me to review.
The "stop after each screen" instruction matters. Letting LingCode run all 20 screens without checkpoints means re-reviewing all 20 at the end, which is the worst time to catch architectural drift.
If you want to spot-check LingCode's translations, here's the mapping it follows:
Container/Padding/Center → VStack/HStack/ZStack with .padding modifiersRow / Column → HStack / VStackListView → ListGridView → LazyVGridNavigator.push → NavigationStack(path:)FutureBuilder → .task + @StateStreamBuilder → AsyncSequence + .taskTextField → TextField + @FocusStateGestureDetector → .onTapGesture / .gestureAnimatedContainer → .animationHero → matched-geometry effectSafeArea → .safeAreaInset / .ignoresSafeAreaContainer → BoxRow / Column → Row / ColumnListView → LazyColumnGridView → LazyVerticalGridNavigator → NavHost + NavControllerFutureBuilder → collectAsStateWithLifecycleTextField → TextField + FocusRequesterAnimatedContainer → animate*AsStateHero → Modifier.sharedElementSafeArea → WindowInsets@Observable + @Environment (Apple) / ViewModel + StateFlow (Android)ViewModel with sealed event/state typessetState → @State (Apple) / remember (Compose)Some Flutter features don't have an honest native mapping. LingCode stubs them and logs them in CONVERSION_PLAN.md rather than guessing — you decide what to do with each:
MethodChannel is a hand-written native hook on iOS and Android — Swift methods and Kotlin functions you write yourself. LingCode stubs the call sites.AppDelegate (iOS) and Application (Android), not the shared layer.onKeyPress; Android has onKeyEvent — usable, but not a literal mapping.Don't let LingCode invent behavior for these. The honest stub is better than confidently wrong code.
After the screen-by-screen phase, ask LingCode to verify each target:
For each target, run the build and report pass/fail:
- iOS: xcodebuild -project ios/YourApp.xcodeproj
-scheme YourApp -destination 'generic/platform=iOS Simulator'
build
- Android: cd android && ./gradlew assembleDebug
- macOS: xcodebuild -project macos/YourApp.xcodeproj
-scheme YourApp build
If a target fails, diagnose and fix before moving on. Don't claim
done until every requested platform builds clean.
This is where you'd run LingCode's parallel agents — one per target — if you want to compress wall time. Each target is independent; failures in iOS won't block Android.
The native projects build, but the real test is parity. Open both the original Flutter app and the new iOS build in simulators, then walk through every screen comparing behavior — navigation, animations, error states, edge cases like empty lists and offline behavior.
LingCode runs the iOS simulator and Android emulator from the Run Destination Picker (⌘R) — you don't need a separate Xcode or Android Studio window for casual testing. Use Android Studio when you need ADB tooling, profilers, or AVD management.
flutter embed, no "add-to-app." If you wanted that, you didn't need this tutorial.pub get, no flutter build, no edits — LingCode treats it as a read-only spec.This entire workflow is packaged as a skill — drop it into your LingCode skills folder and ask LingCode for "flutter to native" to invoke it automatically:
---
name: flutter-to-native
description: Convert a Flutter app to native iOS (SwiftUI), Android (Jetpack Compose), and/or macOS (SwiftUI). Reads the Flutter repo as a design spec and writes fresh idiomatic native code — zero Dart in the output. Triggers: 'migrate from Flutter', 'rewrite Flutter app in native', 'flutter to swiftui', 'flutter to compose', 'flutter to native', existing pubspec.yaml, lib/main.dart. Actions: inventory Flutter app, propose native architecture per target, scaffold ios/ android/ macos/, port screen-by-screen, verify each build clean. Anti-pattern: Flutter embed, add-to-app, faked platform channels. Five phases with checkpoints.
---
Convert a Flutter app to native. Treat the Flutter repo as a design
spec, not source to translate. Read it to understand intent
(screens, navigation, state, data), then write idiomatic native
code from scratch.
Phase 1 — Inventory: scan pubspec, lib/, state mgmt, data layer,
platform channels, theme, gotchas. Write CONVERSION_PLAN.md.
Phase 2 — Architecture per target: navigation, DI, networking,
persistence, asset pipeline.
Phase 3 — Scaffold ios/, android/, macos/.
Phase 4 — Port screen by screen, one todo per screen.
Phase 5 — Verify each target builds clean.
Stop after each phase for user review. Stub deferred items with
// TODO(flutter-to-native): ... rather than faking behavior.
Save as ~/.lingcode/skills/flutter-to-native/SKILL.md — see Install a skill for the exact location and how skills get discovered.