Question #65MediumFlutter Basics

How Flutter Apps are converted to native apps ?

#flutter#native

Answer

Overview

Flutter apps are not converted to native apps in the traditional sense. Instead, Flutter compiles to native ARM/x86 machine code that runs directly on the device. The Flutter framework itself handles rendering through its own engine.


How Flutter Apps Work

Architecture Layers

text
┌─────────────────────────────────┐
│      Flutter Framework          │  ← Dart code (widgets, logic)
│         (Dart)                  │
├─────────────────────────────────┤
│      Flutter Engine             │  ← Skia graphics, Dart runtime
│    (C++, Skia, Dart VM)        │
├─────────────────────────────────┤
│     Platform Embedder           │  ← Android/iOS native wrapper
│  (Android SDK / iOS UIKit)     │
├─────────────────────────────────┤
│   Operating System (Android/iOS)│  ← Native platform
└─────────────────────────────────┘

Compilation Process

Debug Mode (Development)

text
Dart Source Code
  JIT Compilation (Just-In-Time)
  Dart VM executes bytecode
  Hot Reload enabled (fast iteration)

Characteristics:

  • Code compiled on-the-fly
  • Slower performance
  • Enables hot reload

Release Mode (Production)

text
Dart Source Code
  AOT Compilation (Ahead-Of-Time)
  Native ARM/x86 machine code
  Runs directly on device (no interpreter)

Characteristics:

  • Compiled to native machine code
  • Maximum performance (near-native)
  • No hot reload

Platform-Specific Build Process

Android

bash
flutter build apk --release

What happens:

text
1. Dart code → AOT compiled to ARM/x86 machine code
2. Flutter engine (C++ libraries) → bundled
3. Android wrapper (MainActivity.java/.kt) → created
4. Resources (assets, images) → packaged
5. All combined into APK/App Bundle

Output structure:

text
app-release.apk
├── lib/
│   ├── arm64-v8a/          # ARM 64-bit native code
│   ├── armeabi-v7a/        # ARM 32-bit native code
│   └── x86_64/             # x86 64-bit (emulators)
├── assets/
│   └── flutter_assets/     # Images, fonts, etc.
├── META-INF/
└── AndroidManifest.xml

iOS

bash
flutter build ios --release

What happens:

text
1. Dart code → AOT compiled to ARM machine code
2. Flutter engine → embedded as framework
3. iOS wrapper (AppDelegate.swift) → created
4. Compiled into .app bundle
5. Signed and packaged as .ipa

Output structure:

text
Runner.app
├── Frameworks/
│   ├── Flutter.framework   # Flutter engine
│   └── App.framework        # Compiled Dart code
├── Assets.car               # Asset catalog
└── Info.plist

Key Components in Built App

1. Flutter Engine (C++)

What it includes:

  • Skia graphics library (rendering)
  • Dart runtime (VM)
  • Text rendering
  • Platform channels

Size: ~4-5 MB

text
Android: libflutter.so
iOS:     Flutter.framework

2. Compiled Dart Code

Your app's business logic and UI code compiled to native machine code.

dart
// Your Dart code
void main() {
  runApp(MyApp());
}

// Compiled to →
// Native ARM assembly code (machine code)

3. Platform Embedder

Minimal native wrapper that hosts the Flutter engine.

Android:

kotlin
// MainActivity.kt
import io.flutter.embedding.android.FlutterActivity

class MainActivity: FlutterActivity() {
    // Flutter engine runs here
}

iOS:

swift
// AppDelegate.swift
import UIKit
import Flutter

@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    // Flutter engine runs here
}

How Rendering Works

Traditional Native Apps

text
Dart/Java/Swift Code
  Platform Widgets (UIButton, TextView)
  Platform Rendering (iOS UIKit, Android Canvas)
  GPU

Flutter Apps

text
Dart Code (Widgets)
  Flutter Framework (builds widget tree)
  Skia Engine (renders to canvas)
  GPU (direct rendering)

Key difference: Flutter bypasses platform widgets and renders everything itself using Skia.


Platform Channels (Native Integration)

Flutter can call native code when needed:

dart
// Dart side
import 'package:flutter/services.dart';

static const platform = MethodChannel('com.example/battery');

Future<int> getBatteryLevel() async {
  final int result = await platform.invokeMethod('getBatteryLevel');
  return result;
}
kotlin
// Android side (Kotlin)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL)
  .setMethodCallHandler { call, result ->
    if (call.method == "getBatteryLevel") {
      val batteryLevel = getBatteryLevel()
      result.success(batteryLevel)
    }
  }

Flow:

text
Flutter (Dart)
    ↓ Platform Channel
Native Code (Kotlin/Swift)
Platform APIs (Battery, Camera, etc.)
    ↓ Response
Flutter (Dart)

Build Size Breakdown

Minimal Flutter App (Release)

text
Android APK:
├── Flutter engine:     4-5 MB
├── Compiled Dart code: 1-2 MB
├── Assets/fonts:       1-3 MB
├── Android wrapper:    0.5 MB
└── Total:              ~7-10 MB

iOS IPA:
├── Flutter.framework:  4-5 MB
├── App.framework:      1-2 MB
├── Assets:             1-3 MB
├── iOS wrapper:        0.5 MB
└── Total:              ~7-10 MB

Why Flutter is Fast

1. AOT Compilation

text
Native app:    Source → Native code
Flutter app:   Dart → AOT → Native ARM code (same as native!)

No interpretation at runtime = maximum performance.


2. Direct GPU Rendering

text
Flutter → Skia → GPU
(No platform widget overhead)

3. Efficient Widget Tree

dart
// Only rebuilds what changed
setState(() {
  _counter++; // Only this widget rebuilds
});

Debugging the Build

View APK contents (Android)

bash
# Extract APK
unzip app-release.apk -d extracted

# View native libraries
ls extracted/lib/arm64-v8a/
# Output: libflutter.so, libapp.so

View IPA contents (iOS)

bash
# Extract IPA
unzip Runner.ipa -d extracted

# View frameworks
ls extracted/Payload/Runner.app/Frameworks/
# Output: Flutter.framework, App.framework

Comparison with Other Frameworks

FrameworkCompilationRendering
NativeNative codePlatform widgets
FlutterAOT → Native codeSkia engine (custom)
React NativeJavaScript (interpreted)Native widgets via bridge
IonicJavaScript (WebView)HTML/CSS (slow)

Key advantage: Flutter compiles to native code like native apps, but renders using its own engine.


Key Differences from React Native

AspectFlutterReact Native
LanguageDart (compiled to native)JavaScript (interpreted)
BridgeNo bridge (direct code)JavaScript bridge (slow)
RenderingSkia engineNative widgets
PerformanceNear-native (95-98%)Good (70-80%)

Why faster: No JavaScript bridge, direct native code.


Platform-Specific Features

Flutter can access platform features via:

1. Platform Channels (Custom)

dart
// Call native iOS/Android code
final result = await platform.invokeMethod('nativeMethod');

2. Plugins (Pre-built)

yaml
dependencies:
  camera: ^0.10.0      # Camera access
  location: ^4.4.0     # GPS
  firebase_core: ^2.4.0 # Firebase

Plugins use platform channels internally.


Build Commands

Android

bash
# Debug (JIT)
flutter run

# Release (AOT)
flutter build apk --release         # APK
flutter build appbundle --release   # App Bundle (Play Store)

# Split per ABI (reduce size)
flutter build apk --split-per-abi

iOS

bash
# Debug
flutter run

# Release (AOT)
flutter build ios --release
flutter build ipa --release  # For App Store

Key Takeaways

Not converted: Flutter doesn't convert to native code—it compiles directly to native ARM/x86 machine code.

Rendering: Flutter uses its own Skia engine, not platform widgets.

Performance: Near-native (95-98%) because it's real compiled native code.

Platform integration: Access native features via platform channels.


Resources