Hodgepodge #1: Optimization tips
These small optimizations add up and significantly impact the overall user experience.
In my culture, we have a very traditional soup called solyanka. The thing about this soup, is that it consists out of a variety of ingredients, and can be made in many various ways. This versatility is likely why the name "solyanka" became an idiom meaning "a random mix of stuff." Interestingly, when I tried to translate this concept to English, I discovered there's a perfect equivalent that also comes from a dish (though different from our solyanka) — the hodgepodge. Since I don't have one particular topic to discuss today, but rather bits and pieces of different ones, let's call this type of newsletter issue a hodgepodge. This word sounds so silly, to be honest, I love it!
I initially planned to post this newsletter last year (did anyone order stale jokes?), but plans have a life of their own. Plus, solyanka is a known hangover cure for 1st of January, so I think the timing is perfect 😂
As you probably already know, I'm currently indie building a set of apps. My initial goal was to release MVPs of these apps, and now that they're out, I'm focused on turning them into more robust products. While polishing some things, I made several optimizations in different parts of the apps—some obvious, some less so. Though these improvements are unrelated and somewhat random, I wanted to share them with you in case you encounter similar challenges.
Optimization tip #1: Optimistic anonymous login
No users, no problems, right? Well, users are definitely nice problems to have, and I need a way to identify them. Authentication is important, but I want my users to get into the app as quickly as possible, while still being able to link and store their data in their account. For this, I use Firebase Anonymous Authentication. Despite its limitations, it authenticates users with just a single line of code FirebaseAuth.instance.signInAnonymously();
, provides their ID, and can later be linked with any of their accounts—whether unique to your app or single sign-on like Google, Apple, Facebook, etc. I use Firebase services extensively in my apps, so this works like a charm.
App Check is a relatively recent Firebase service that protects your apps from unauthorized backend access, including Firebase services like Cloud Firestore, Cloud Functions, Vertex AI, and more (I've talked about it in more detail here). It works through attestation providers (Play Integrity on Android, Device Check & App Attest on iOS, etc.), generating and caching a token that's sent with each request to Firebase services to verify authorization. From what I've observed, the first token generation can take some time and happens with the first Firebase service call. In my case, this first call is typically FirebaseAuth.instance.signInAnonymously();
, which runs when users press "Continue" (or similar) during onboarding.
This consistently took 2-3 seconds—enough to frustrate any user in 2024 (let alone in 2025!). So I moved the login call to happen as soon as the user opens the onboarding flow (for example, from button tap handler to initState
). By the time they're ready to continue, they're already logged in. A small but nice fix!
Optimization tip #2: Prefer PNGs to SVGs
When it comes to icons, the general tip is to use SVGs because they're scalable, customizable, and lighter than other formats. So without thinking, I made this screen with icon selection in my Trigger Journal app filled with SVGs:
But during app testing, I noticed significant frame drops while scrolling—even in release builds. The culprit? The numerous complex SVGs being rendered simultaneously. Converting these icons to PNGs dramatically improved performance. Since these icons have fixed dimensions and don't need scaling, PNGs work perfectly well. Consider this your friendly reminder that no one size fits all, pun intended. Now I’m wondering if RepaintBoundary
would have helped, but that’s a question for another day.
But what if images are fetched from the network?
Optimization tip #3: Download images asynchronously
One exciting feature in NativePal is the creation of custom characters. To support 50+ languages (wait for the next post for challenges on implementing THAT), we decided to let users create their own characters instead of hardcoding a set for each language. Each custom character can have interests, characteristics, a name, and an avatar. All this data is stored remotely, allowing us to add options to the database that become available to users without app updates.
However, opening this page on a simulator consistently took 8-10 seconds—making it completely unusable. Here's what I discovered during inspection. Each image is a data class containing textual information about the character's appearance, plus paths to both avatar and full-body images stored in Firebase Storage. The paths are relative because getting an actual download URL requires calling a Firebase method (which fetches required tokens and checks authorization) FirebaseStorage.instance.ref().child(path).getDownloadURL()
. Initially, I was synchronously fetching each profile's avatar and full-body image, like this:
I realized that since these network calls are independent, we don't need to wait for one profile to download before starting another. So I refactored the synchronous fetch to an asynchronous one:
Yes, there's more code now, but on the simulator I saw a consistent improvement from 10 seconds to 1.4 seconds!!! (forgive my informal benchmarking, but it works for this use case), so to say I’m happy is to say nothing 😅 This optimization became particularly noticeable when loading around 40 profiles, though it also gave a nice performance boost even when loading just 6 profiles.
These small optimizations add up and significantly impact the overall user experience. While some seem obvious in hindsight and others less so, I hope you've found something useful to take away from this!
I initially planned to also tell you about my self-inflicted challenges of supporting 50+ languages in a language learning app (yes, it matters that it's a language learning app—see next issue to find out why 😝). However, this would further delay this already-delayed newsletter (though fitting its hodgepodge theme) and probably deserves its own discussion. Since consistency in the newsletter and content creation is my resolution for 2025 (something I wasn't great at in 2024), let's keep these posts smaller but more frequent ☺️️
Talk to you on Twitter and Bluesky! And check out my apps here :)
-Daria 💙
P.S. The e-book version of my “Flutter Design Patterns and Best Practices” is now on sale for just $9.99 at Packt’s website, so if you haven’t yet, it’s a perfect opportunity to grab your copy!
Great real-world practical tips Daria, thank you for sharing!