← Back to blog
Building Production Flutter Apps: Lessons from 40+ Shipped Projects
Engineering Team·
After shipping over 40 Flutter applications to production, we've learned what works — and what doesn't. Here's our playbook.
Architecture
We use a clean architecture pattern with three layers:
- Data layer — Repositories that abstract data sources (API, local DB, Firebase)
- Domain layer — Business logic with Riverpod providers
- Presentation layer — Widgets that consume providers
This separation means you can swap Firebase for a custom backend without touching your UI code.
State Management
We chose Riverpod over Bloc for most projects. The reasons:
- Simpler boilerplate — no separate Event/State classes
- Compile-time safety — providers are resolved at compile time
- Testability — providers can be overridden in tests
- Code generation — riverpod_generator handles the repetitive parts
CI/CD
Every project ships with a Codemagic pipeline that:
- Runs static analysis (dart analyze)
- Runs unit and widget tests
- Builds iOS and Android binaries
- Deploys to TestFlight and Internal Testing
- Notifies the team on Slack
Testing
We target 80%+ code coverage with three types of tests:
- Unit tests — Provider and repository logic
- Widget tests — Individual widget behavior
- Integration tests — Full user flows
Key Lessons
- Start with CI/CD — The pipeline should be set up before the first feature is built
- Design system first — Build your theme, typography, and component library before screens
- Error handling is a feature — Every API call needs loading, error, and empty states
- Performance from day one — Profile on real devices, not simulators
- Automate everything — Code generation, localization, asset management