Ever wondered how some apps update their UI without you having to update them from Google play store or Apple app store? Don’t worry, we got you covered. Apps with such behavior use what is called Server Driven UI or SDUI in short and when it comes to scalable and dynamic apps, SDUI is not just an optional feature but a compulsory one. Server Driven UI SDUI In this article I am going to explain what SDUI is and how you can add it in your flutter app and also I will shed some light on useful tips to consider when using SDUI. Let’s first get our head around the concept of SDUI. What Is SDUI? SDUI or Server Driven UI is technique through which you can control your app’s UI from the sever without having to change your app’s code itself. In SDUI, server sends a pre-agreed json response and app side handles that json response in a specific way to render UI. Changing json results in UI changes on the app without the need of app update or code changes. Now let’s dive into practical example of doing it in a flutter app. In our example, we will be rendering 2 custom widgets, let’s call them TigerWidget and RhinoWIdget and they look something below: TigerWidget RhinoWIdget Our build function looks like this: @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text('SDUI Demo'), ), body: Column( children: [ const TigerWidget(), const RhinoWidget(), Text('My Amazing Screen!', style: TextStyle(fontSize: 50)), ], ), ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text('SDUI Demo'), ), body: Column( children: [ const TigerWidget(), const RhinoWidget(), Text('My Amazing Screen!', style: TextStyle(fontSize: 50)), ], ), ); } Now we are going to make TigerWidget and RhinoWidget dynamic so we can render them based on the json received from server but first, we need to design json for our dynamic widgets. TigerWidget RhinoWidget Designing The JSON We will use following demo json for our example (you can design custom json as per your needs): { "id": "widget_id", "image_url": "image_url", "title": "title", "description": "description" } { "id": "widget_id", "image_url": "image_url", "title": "title", "description": "description" } We have 2 widgets to render, therefor we will use id field to determine which widget to show on the UI. Our server will send us a list of these objects, server response will look like below: id [ { "id": "tiger_widget", "image_url": "https://images.unsplash.com/photo-1605092676920-8ac5ae40c7c8", "title": "Tiger", "description": "A beautiful Tiger" }, { "id": "rhino_widget", "image_url": "https://images.unsplash.com/photo-1430026996702-608b84ce9281", "title": "Rhino", "description": "A beautiful Rhino" } ] [ { "id": "tiger_widget", "image_url": "https://images.unsplash.com/photo-1605092676920-8ac5ae40c7c8", "title": "Tiger", "description": "A beautiful Tiger" }, { "id": "rhino_widget", "image_url": "https://images.unsplash.com/photo-1430026996702-608b84ce9281", "title": "Rhino", "description": "A beautiful Rhino" } ] JSON to UI Parser Now that we have designed our json, we will need a UI parser that can use this json to render our dynamic widgets. Our example parser looks something like below: class SduiParser extends StatelessWidget { final Map<String, dynamic> parsedJsonFromServer; const SduiParser({super.key, required this.parsedJsonFromServer}); @override Widget build(BuildContext context) { return Column( children: [ ...(parsedJsonFromServer['data'] as List).map((json) { switch (json['id']) { case 'tiger_widget': return TigerWidget(); case 'rhino_widget': return RhinoWidget(); default: return const SizedBox(); } }), ], ); } } class SduiParser extends StatelessWidget { final Map<String, dynamic> parsedJsonFromServer; const SduiParser({super.key, required this.parsedJsonFromServer}); @override Widget build(BuildContext context) { return Column( children: [ ...(parsedJsonFromServer['data'] as List).map((json) { switch (json['id']) { case 'tiger_widget': return TigerWidget(); case 'rhino_widget': return RhinoWidget(); default: return const SizedBox(); } }), ], ); } } Our SduiParser class takes our parsed json from server as a Map<String, dynamic> and renders our dynamics widget as per our json structure. Now Our build function looks something like below: SduiParser Map<String, dynamic> Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text('SDUI Demo'), ), body: SingleChildScrollView( child: Column( children: [ SduiParser(parsedJsonFromServer: _parsedSduiJson), Text('My Amazing Screen!', style: TextStyle(fontSize: 50)), ], ), ), ); } Widget build(BuildContext context) { return Scaffold( appBar: AppBar( backgroundColor: Theme.of(context).colorScheme.inversePrimary, title: Text('SDUI Demo'), ), body: SingleChildScrollView( child: Column( children: [ SduiParser(parsedJsonFromServer: _parsedSduiJson), Text('My Amazing Screen!', style: TextStyle(fontSize: 50)), ], ), ), ); } So now if we want only RhinoWidget to render on our screen and remove our TigerWidget, all we have to do is to change our json response from server to something like below: RhinoWidget TigerWidget [ { "id": "rhino_widget", "image_url": "https://images.unsplash.com/photo-1430026996702-608b84ce9281", "title": "Rhino", "description": "A beautiful Rhino" } ] [ { "id": "rhino_widget", "image_url": "https://images.unsplash.com/photo-1430026996702-608b84ce9281", "title": "Rhino", "description": "A beautiful Rhino" } ] We can now play around with our json by adding or removing multiple objects or by changing their positions and manipulate our UI from the server without having to release new build for the UI changes to take effect. Note that in this example we have designed only 2 UI components, the more UI components you design, the more UI control you have. Note that in this example we have designed only 2 UI components, the more UI components you design, the more UI control you have. Useful Tips Beware to keep consistent look at your UI parser, for large scale projects, it can get very messy very fast Always test your SDUI json in dev or test environment before applying in production as smallest key-value mistake can lead to app UI cracks Make sure to place a suitable placeholder for empty UI json response so not to have extra white space in your screen when app receives empty json response, either use a zero state widget or just return an empty Container or a SizedBox Make sure to take proper security measures in your app against packet spoofing as potential hacker might change SDUI json data against your will Debugging SDUI can be a challenge as smallest invalid change in SDUI can lead to broken UI, make sure to double check your SDUI json response Beware to keep consistent look at your UI parser, for large scale projects, it can get very messy very fast Always test your SDUI json in dev or test environment before applying in production as smallest key-value mistake can lead to app UI cracks Make sure to place a suitable placeholder for empty UI json response so not to have extra white space in your screen when app receives empty json response, either use a zero state widget or just return an empty Container or a SizedBox Container SizedBox Make sure to take proper security measures in your app against packet spoofing as potential hacker might change SDUI json data against your will Debugging SDUI can be a challenge as smallest invalid change in SDUI can lead to broken UI, make sure to double check your SDUI json response Final Thoughts Implementing Server-Driven UI in Flutter opens up exciting possibilities for building highly flexible and scalable applications that can adapt on the fly without the need for frequent app updates. While the approach comes with its own set of challenges, such as increased complexity in rendering and debugging but careful architectural planning and best practices can help you harness the full power of SDUI effectively. By adopting Server-Driven UI, you position your app to deliver personalized, dynamic experiences that can evolve rapidly with user needs and business goals. Complete project github repository github repository github repository github repository