Building the Same App in SwiftUI, Kotlin Multiplatform, and Flutter — What 281 vs. 75 Lines of Code Teaches Us
Article Summary
Dr. Simon Bogutzky built the same bill-splitting app three times. The line count difference? 75 vs. 281.
This isn't another framework comparison with benchmarks and feature lists. It's a hands-on experiment implementing identical functionality in SwiftUI, Kotlin Multiplatform with Compose, and Flutter to reveal what those code differences actually mean for your team.
Key Takeaways
- SwiftUI needs one-third the code of Flutter, one-quarter of KMP/Compose
- KMP's extra 200 lines buy proper architecture and platform abstraction
- SwiftUI handles state and navigation automatically; others require explicit setup
- Flutter's hot reload wins developer experience, but manual cleanup risks leaks
- All three perform well at small scale; differences emerge in complex apps
SwiftUI wins on elegance for iOS-only apps, KMP/Compose invests in scalable architecture for cross-platform, and Flutter balances pragmatic speed with mature tooling.
About This Article
Dr. Simon Bogutzky wanted to understand why state management works so differently across frameworks. SwiftUI's @State property wrappers handle reactivity automatically. KMP/Compose requires explicit derivedStateOf with manual dependency tracking. Flutter demands manual TextEditingController lifecycle management with dispose() cleanup.
He looked at how each framework approaches state. SwiftUI uses implicit two-way bindings with $ syntax. KMP/Compose provides fine-grained control through remember and mutableStateOf with explicit dependencies. Flutter relies on imperative setState() calls paired with controller disposal in lifecycle methods.
The comparison showed some real differences. SwiftUI's automatic dependency tracking prevents memory leaks without developer intervention. KMP/Compose's explicit dependencies enable optimization for expensive computations in complex apps. Flutter's manual approach gives developers control but introduces leak risks if they forget dispose() calls.