Otukan protects all three layers of a Flutter app: the Dart code compiled ahead of time into libapp.so on Android and App.framework on iOS, the native code your plugins and platform channels bring in, and the assets bundled in flutter_assets. It keeps Flutter’s own obfuscation in place and adds what that flag leaves out: string protection, integrity checks and runtime detection. Your Dart, Android and iOS crash symbols stay usable. Because the same Dart code ships in different containers on each platform, Otukan protects and validates the Android and iOS builds separately, and your policy can respond differently on each.
Code layers
Dart AOT code
In release mode, Flutter compiles Dart ahead of time into native machine code. On Android the snapshot is in libapp.so, one copy per ABI, next to the engine in libflutter.so. On iOS it is in App.framework, next to Flutter.framework. It is compiled code, not source, but it still carries symbol names and string literals such as endpoint URLs and error messages.
Flutter has its own obfuscation option, set at build time, and Otukan works alongside it:
flutter build appbundle --obfuscate --split-debug-info=build/symbols
flutter build ipa --obfuscate --split-debug-info=build/symbols
The flag renames symbols and moves debug information into the symbols directory. It does not hide strings, check integrity or detect a hostile runtime. Otukan adds those layers to the same snapshot, so an analyst faces protected strings, integrity checks and runtime detection, not only renamed symbols.
Native plugins and platform channels
Most Flutter apps depend on plugins with native code: Kotlin or Java on Android, Swift or Objective-C on iOS. Payment, identity and analytics SDKs usually arrive this way. Platform channels carry messages between Dart and this native code, and an attacker who hooks the process could observe or alter those messages. Otukan protects your own plugin code, detects hooking of the process at runtime, and tunes its checks so that third-party SDKs which inspect the app do not trip your policy. Native code that receives channel messages should still validate its inputs.
Packaged assets
Images, JSON files, fonts and other assets declared in pubspec.yaml are stored as ordinary files in flutter_assets: inside the app bundle or APK on Android, and inside App.framework on iOS. Otukan’s integrity checks cover them, so a modified asset is detected and handled by your policy.
| Layer | Where it lives | How Otukan protects it |
|---|---|---|
| Dart code | libapp.so on Android, App.framework on iOS | Hardens the snapshot and its strings, alongside Dart obfuscation |
| Flutter engine | libflutter.so on Android, Flutter.framework on iOS | Covered by integrity checks and revalidated at each Flutter upgrade |
| Native plugins | Platform libraries and frameworks | Protects your plugin code and tunes runtime checks for vendor SDKs |
| Assets | flutter_assets in the package | Integrity-checked, so modifications are detected |
Platform scope
The Dart source is shared, but the build output is not. Otukan treats the Android and iOS builds as two protected releases.
| Topic | Android | iOS |
|---|---|---|
| Container | App bundle or APK with per-ABI native libraries | App bundle with embedded frameworks |
| Signing | Your upload key; with Play App Signing, Google re-signs with the app signing key | Apple-issued certificate; App Store and TestFlight builds are re-signed by Apple |
| Native symbols | Native debug symbols uploaded to Play Console or your crash tool | dSYM files matched by UUID |
| Runtime signals from Otukan | Root, hooking, debugging, emulators, repackaged copies | Jailbreak, hooking and swizzling, debugging, re-signed copies |
| Attestation | Play Integrity API | App Attest and DeviceCheck |
Platform detail is on Android app security and iOS app security. How protection fits a pipeline is covered on mobile app security integration.
Release testing
Debugging symbols and crash reporting
A protected Flutter release has three kinds of symbols: Dart symbols from --split-debug-info, native symbols for Android, and dSYMs for iOS. Otukan keeps all three valid, so your crash reporting tool reads protected releases just as it reads unprotected ones. Confirm it by forcing a crash in a protected build on each platform. Obfuscated Dart stack traces can also be read locally with flutter symbolize and the matching symbols directory.
Framework updates
Each Flutter SDK upgrade changes the engine and can change how the snapshot is laid out. Otukan revalidates protection against the new version on both platforms, and your release check stays short:
- build baseline and protected releases from the same commit
- run install, update, sign-in and your most sensitive flow on physical Android and iOS devices
- confirm that crash reports symbolicate on both platforms
- compare start-up and size against the previous release
Measurement guidance is on mobile app protection performance.
For how Flutter fits the wider protection model, return to mobile app security. If your app is already published, a free App Exposure Check reviews the public store release.