When I started building Let’s Discover Germany, I expected Flutter’s write once, run anywhere promise to make things straightforward. It largely delivered — but there were enough sharp edges to fill a blog post.

State Management: Don’t Over-Engineer Early

The most common mistake I see Flutter newcomers make is reaching straight for complex state solutions like BLoC or Riverpod before understanding the problem. For a mid-sized app, Provider with ChangeNotifier was more than sufficient. I only migrated to Riverpod later once the app grew, and that migration was painless because the logic was already well-encapsulated.

Rule of thumb: start with setState for local UI state, Provider for shared state, and only escalate when you feel genuine pain.

Firebase Real-Time Database vs Firestore

I initially chose Firebase Realtime Database because of its simplicity. The flat JSON structure works fine for small datasets, but as soon as I introduced nested data (places → reviews → users), queries became awkward and data denormalization became unavoidable.

If I were starting today, I’d go straight to Firestore. The document/collection model maps cleanly to how you think about your data, and compound queries save a lot of client-side filtering work.

Platform-Specific UI Pitfalls

Flutter renders its own widgets — it doesn’t use native components. This is its biggest strength and its biggest frustration:

  • Fonts: The system font on Android and iOS differ. Loading a custom Google Font early sidesteps this.
  • Navigation gestures: iOS users expect swipe-back; Flutter’s CupertinoPageRoute handles this, but mixing MaterialPageRoute and CupertinoPageRoute in the same app leads to inconsistent behavior.
  • Keyboard overlaps: On Android, resizeToAvoidBottomInset: true is your friend. On iOS, wrapping content in a SingleChildScrollView is usually the better fix.

Hot Reload Is Genuinely Transformative

Coming from native Android/iOS development, Flutter’s hot reload is not a gimmick — it changes how you build UI. Being able to iterate on layouts and see results in under a second fundamentally shifts your workflow from “write → build → test” to something much closer to visual design tooling.

What I’d Do Differently

  1. Define the data model before writing a single widget.
  2. Use flutter_gen for type-safe asset references from day one.
  3. Write widget tests earlier — they’re far cheaper to write during development than retroactively.

Overall, Flutter remains my go-to for cross-platform mobile. The DX is excellent, the community has matured enormously, and Dart is an underrated language.