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.
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-frameworkbuilds 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:
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:
Dart source is compiled by
frontend_serverinto an intermediate
app.dillbinary, which contains merged Dart bytecode. Hot Reload works by compiling changed code into
app.dill.incremental.dilland sending it to the Dart VM.
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_assetsfrom the package and upload images to CDN.
Integrate
cached_network_imagefor disk caching.
Call
precacheImageduring 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_pathsetting during VM startup.
Flutter.framework/Flutter (Engine)
Engine size dominates the package. Two optimization avenues are:
Compilation optimization : change the iOS build flag from
-Osto
-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:
gclientto fetch source and dependencies.
gnto generate Ninja build files.
ninjato perform the actual compilation.
See Flutter Wiki: Setting up the Engine development environment.
Steps:
Create a
.gclientfile to specify the engine repo and dependency commits.
Run
gclient syncto download dependencies.
Use
gnto generate configuration for the target platform/architecture, then run
ninjato compile.
Replace the Engine in the local Flutter SDK with the custom build.
The final trimmed product architecture is illustrated below:
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:
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_assetsto a cloud server.
Customize
FlutterLoader.javainside
flutter.jarto 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 包体积".
QQ Music Frontend Team
QQ Music Web Frontend Team
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.