Mobile Development 17 min read

Practical Guide to Trimming Flutter App Size on iOS & Android

This article examines the composition of Flutter iOS and Android build artifacts, then presents detailed strategies—such as removing unused components, relocating assets, and optimizing engine compilation—to significantly reduce package size, with concrete examples, code snippets, and measured results showing up to 50% shrinkage.

QQ Music Frontend Team
QQ Music Frontend Team
QQ Music Frontend Team
Practical Guide to Trimming Flutter App Size on iOS & Android

In hybrid development scenarios, the relatively large incremental size of Flutter packages is a common complaint. Google has confirmed that Flutter will not support dynamic loading and provides no official customization scheme, so developers must manually reduce package size.

iOS Section

Product Composition

Running

flutter build ios-framework

builds a Flutter module into a framework for iOS host integration. The resulting product consists of:

App.framework

App: AOT‑compiled Dart business code

flutter_assets: Flutter static resources

Flutter.framework

Flutter: compiled Flutter Engine

icudtl.dat: internationalization data file

The following diagram shows the iOS Flutter product structure:

iOS product structure
iOS product structure
Note: Finder shows size using a 1000‑based multiplier; use command‑line output for accurate values.
The Engine size shown uses profile mode (arm64+arm32). Flutter 1.17.1 release has a bug preventing bitcode compression, inflating size to 351.47 MB (see Flutter app size issue #45519).

Reduction Methods

The basic approaches are:

Delete unused parts of the product.

Relocate parts to remote delivery and modify loading logic for dynamic fetching.

App.framework/App

The App component is built from Dart source code. The build flow is:

App.framework build flow
App.framework build flow

Dart source is compiled by

frontend_server

into an intermediate

app.dill

binary, which contains merged Dart bytecode. Hot Reload works by compiling changed code into

app.dill.incremental.dill

and sending it to the Dart VM.

Hot Reload process
Hot Reload process

Two AOT snapshot libraries compose the App binary:

kDartIsolateSnapshotData : isolate initial heap state

kDartIsolateSnapshotInstructions : AOT instructions for the isolate

kDartVmSnapshotData : shared VM heap state

kDartVmSnapshotInstructions : shared VM AOT instructions

See the official Wiki for details: https://github.com/flutter/flutter/wiki/Flutter-engine-operation-in-AOT-Mode

Because App Store review rules forbid dynamic delivery of executable code, only the data segments (kDartIsolateSnapshotData and kDartVmSnapshotData) can be moved to a remote server; instruction segments must remain in the binary.

To relocate these snapshots, modify the settings path used during Dart VM startup to point to the remote location.

Delete Unused Parts

Using Flutter’s size analysis tool, two unused libraries were identified and removed. Additional reductions include:

Configure the linter to forbid unnecessary syntax (e.g., explicit type casts) that adds try‑catch blocks.

Obfuscate Dart code, saving 0.75 MB (2.5%).

Further symbol removal:

Disable stack trace symbols, saving 1.8 MB (6.2%).

Delete the dSYM symbol file, saving 5.8 MB (20%).

dSYM stores hexadecimal function address mappings for crash analysis.

Relocate Parts

The four AOT snapshot libraries can be split so that only data segments are delivered remotely. The loading path is changed in the VM settings.

For

flutter_assets

, which cannot be deleted, two relocation strategies are offered:

Modify the VM settings to load assets from a remote URL.

Use a CDN‑backed image solution with disk caching and preloading via

precacheImage

. Steps:

Wrap an Image widget to select local or CDN images based on build mode.

Adjust CI to strip

flutter_assets

from the package and upload images to CDN.

Integrate

cached_network_image

for disk caching.

Call

precacheImage

during module startup to preload CDN images.

Modifying the engine for remote asset loading is recommended over the CDN approach.

Flutter.framework/icudtl.dat

The internationalization data file should be relocated similarly by changing the

icu_data_path

setting during VM startup.

Flutter.framework/Flutter (Engine)

Engine size dominates the package. Two optimization avenues are:

Compilation optimization : change the iOS build flag from

-Os

to

-Oz

, reducing ~700 KB.

Engine trimming :

Remove unused Skia parameters, saving ~200 KB.

Exclude BoringSSL when using a client‑side proxy instead of Dart HttpClient, saving ~500 KB.

Another optimization mentioned in issue #40345 can reduce Dart‑compiled instruction count from 36 to 13, pending Google support.

Engine Compilation

Building a custom engine requires:

gclient

to fetch source and dependencies.

gn

to generate Ninja build files.

ninja

to perform the actual compilation.

See Flutter Wiki: Setting up the Engine development environment.

Steps:

Create a

.gclient

file to specify the engine repo and dependency commits.

Run

gclient sync

to download dependencies.

Use

gn

to generate configuration for the target platform/architecture, then run

ninja

to compile.

Replace the Engine in the local Flutter SDK with the custom build.

The final trimmed product architecture is illustrated below:

Final trimmed product architecture
Final trimmed product architecture

iOS Reduction Results

Package size can be measured in several ways. After uploading a blank host app to the App Store, the Install Size decreased from 18.7 MB to 11.8 MB , a reduction of roughly 37%.

When viewing the App Store via a web browser, the displayed size is the Download Size, not the Install Size.

Android Section

Product Composition

The Android Flutter module release consists of:

libapp.so
flutter.jar

(which contains

libflutter.so

,

icudtl.dat

, and Java bridge files)

Key artifacts are shown in the diagram below:

Android product composition
Android product composition

Reduction Methods

Because Android has no App Store binary encryption restriction, the entire Flutter payload can be delivered dynamically. The steps are:

Move

libapp.so

,

libflutter.so

, and

flutter_assets

to a cloud server.

Customize

FlutterLoader.java

inside

flutter.jar

to load libraries from the remote location.

Specific code examples are omitted.

Android Reduction Results

Using a blank host project, the 6.2 MB Flutter payload was completely removed from the final APK.

Reference articles: "Q 音直播 Flutter 包裁剪方案 (iOS)" and "如何缩减接近 50% 的 Flutter 包体积".
flutterMobile DevelopmentiOSAndroidApp size
QQ Music Frontend Team
Written by

QQ Music Frontend Team

QQ Music Web Frontend Team

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.