Crafting Personalization with Efficiency: Client-Side Message Templating with Flutter
Let’s give some relief to the backend
I was working on a new feature this week where we had to display a customized greeting on the screen — which we were obviously getting from the backend. I quickly opened up Dartpad as a thought crossed my mind...
What if we could utilize the data already present in the app’s storage and let the backend send templates only?
The idea was pretty simple:
- The backend sends me “Hello %name%, how has your day been?”
- The app recognizes the %name% variable and patches it with the available local data
- The app displays “Hello Chahat, how has your day been?”
I proposed this idea to my manager. He told me that he had been using this approach previously for customizing push notifications without loading the server when thousands of push notifications had to be sent at once.
Brilliant! Now we have two use cases.
After doing some research I found the key advantages of this approach, some of which I am writing down:
Advantages:
- Faster API responses. The server doesn’t need to fetch the user data just for sending personalized messages.
- Lesser load on the server while sending bulk push notifications. The difference this small development can make in this use case is incomparable.
- Reduced database queries. With this approach, the backend doesn’t need to fetch and process individual user data to generate personalized messages.
- Simplified code on the backend. Keeping the message templates and customization logic on the client side simplifies the code on the server.
Without wasting any more time, this is the code I came up with:
#1 — Create a Storage Helper class if you don’t have one already
PS: please don't draw swords yet.. this is just a demo implementation. You can have your storage helper your way.
class AppStorage {
AppStorage._();
static AppStorage? _instance;
static AppStorage get instance {
_instance ??= AppStorage._();
return _instance!;
}
String getUserName() {
// your logic to get user's name
}
String getUserId() {
// your logic to get user's ID
}
}
#2 — Create a Template Resolver
We will use this to keep a map / dict of all variables that the app is expecting along with a replacePlaceholders
function that will actually translate our string templates to customized greetings.
👉🏼 Notice that we haven’t mapped real data to the keys in the _values
map. Instead, we have mapped the keys with their respective string functions. This way all the functions won’t be called at once when the _values
map is initialized. This definitely depends on your coding preferences and you can modify it your way.
class StringTemplateResolver {
static final Map<String, String Function()> _values = {
'user_name': AppStorage.instance.getUserName,
'user_id': AppStorage.instance.getUserName
};
static resolve(String message) {
return replacePlaceholders(message);
}
static String replacePlaceholders(String message) {
String result = message;
_values.forEach((key, value) {
final placeholder = '%$key%';
if (result.contains(placeholder)) {
result = result.replaceAll(placeholder, value.call());
}
});
return result;
}
}
#3 — Create an extension function for easy access
’cause why not?
extension DynamicString on String {
String display() {
return StringTemplateResolver.resolve(this);
}
}
#4 — Access using the extension
message.display()
💡 Bonus Tip: You may also create an extension or wrapper on the Text widget class that will automatically translate all of your input strings.
Future development scope
We can do a couple of things to make this even better. We may introduce default data to our functions in case we do not have the user’s name or ID yet. We can optionally code this platform-specific instead of in Dart to have easier access on the native side while dealing with push notifications. Not just on the UI, we can also use this to manipulate other data too. A push notification that redirects to https://yourapp.com/%user_id%/scoreboard
can do wonders.
Pitfalls and Limitations
Although this is a brilliant approach to getting the most out of backend-frontend collaboration, it may come with some negatives too.
- If the backend sends a template that is not recognized by the app, this logic may fail. This can happen with continuous development when older versions of the app are left behind.
- It is important to make limited use of this approach and use only very common data chunks such as user ID or name. Making it too defined may cause trouble in code maintenance.
- Testing complexities — all the variables must be tested to ensure an error-free output. As it is on the UI and visible, minor errors may lead to major embarrassment.
Conclusion
This isn’t a new concept. Many tech companies already use it. It was just something that naturally came into my mind and I thought it was worth giving a shot. Everything has its perks and evils and this one has too. Although the server load that you can take off with this is very impressive. Let me know your thoughts and outcomes and let’s meet in another blog. Happy coding!
🚀 Let’s connect!
Connect with me on LinkedIn for professional insights and networking.
Explore my contributions on GitHub and Stack Overflow to collaborate on something amazing! ✨
Consider clapping 👏🏼 if you found this article insightful.