3weeks Dev Log #8 — From UIKit to Flutter: What the Tech Stack Switch Actually Looks Like

Same Features, Different Code
The rebuild used the existing app as a reference — run the old version, look at each screen, rebuild it in Flutter. The same method I used for SMART LOTTO.
The difference this time: the Swift source code still existed. The rebuild was a port as much as a redesign.
Notifications Are the Core Feature
In 3weeks, notifications are the most important piece.
If the daily reminder doesn't fire reliably, the app has no reason to exist. Habit tracking only works if users remember to check in.
Flutter local notifications use the flutter_local_notifications package. iOS requires UNUserNotificationCenter configuration; Android requires notification channel setup. Cross-platform doesn't mean identical code — each platform has its own setup requirements.
Android battery optimization settings were the most complex part. Without explicitly requesting an exception, Android may suppress background notifications on some devices and OS versions.
Local DB: Realm to Hive
The existing app used Realm Swift. The Flutter version uses Hive.
Hive is a lightweight local database optimized for Flutter. The API is simple, and for a straightforward data model like 3weeks, it's a good fit.
The data model hasn't changed:
- Habit: name, description, start date, notification time, reward text
- Check record: habit ID + date + completion boolean
Migrating the structure from Realm concepts to Hive was faster than anticipated.
UI Redesign
The 2019 UI looked its age. Flutter's strength in custom rendering made rebuilding it with a modern design feel natural.
Design decisions by screen:
Home screen: Today's status for all active habits, immediately visible. Complete or not-complete should be readable at a glance.
Habit detail: Visual representation of 21-day progress. Calendar showing which days were completed.
Completion screen: The 21-day achievement screen needed to feel significant. Added a proper celebration animation.
Voice Memo Implementation
The voice recording feature was ported to Flutter using the record package for recording and just_audio for playback. Recorded files are stored in the app's local storage directory.
iOS requires microphone permission; Android requires recording permission. Both need careful permission handling — missing a permission check can cause silent failures or crashes, and the error messages aren't always informative.
Migrating Existing User Data
Existing iOS users had data stored in Realm that needed to survive the update to the Flutter version.
Reading the original Realm database directly from Flutter isn't possible. The solution: a migration layer in the iOS update that reads existing Realm records and converts them to Hive format before the new app takes over.
It was not a trivial task, but protecting existing user data wasn't optional.