Flutter Code Push Idea

Ufuk Sahin
3 min readMay 29, 2024

--

I wanted to share with you an example idea that came to mind when I was thinking about how to do CodePush with Flutter.

First of all, let’s create a json for ourselves. This json will allow us to create a dynamic UI.

{
"title": "Dynamic Page",
"widgets": [
{
"type": "text",
"data": "Hello, this is dynamic text!",
"style": {
"fontSize": 20,
"color": "#FF0000"
}
},
{
"type": "button",
"data": "Click me",
"action": "show_alert",
"alert_message": "You clicked the button!",
"style": {
"backgroundColor": "#00FF00",
"textColor": "#FFFFFF"
}
},
{
"type": "image",
"url": "https://example.com/image.png",
"style": {
"width": 100,
"height": 100
}
}
],
"layout": {
"type": "column",
"alignment": "center"
}
}

Our page will be fed from this json, we can shape and change the style of our page according to the information in the json.

import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;

void main() {
runApp(MyApp());
}

class MyApp extends StatefulWidget {
@override
_MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
Map<String, dynamic>? pageConfig;

@override
void initState() {
super.initState();
fetchPageConfig();
}

Future<void> fetchPageConfig() async {
final response = await http.get(Uri.parse('https://yourserver.com/page_config.json'));
if (response.statusCode == 200) {
setState(() {
pageConfig = json.decode(response.body);
});
} else {
throw Exception('Failed to load page config');
}
}

@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text(pageConfig?['title'] ?? 'Loading...'),
),
body: pageConfig == null
? Center(child: CircularProgressIndicator())
: DynamicPage(pageConfig: pageConfig!),
),
);
}
}

class DynamicPage extends StatelessWidget {
final Map<String, dynamic> pageConfig;

DynamicPage({required this.pageConfig});

@override
Widget build(BuildContext context) {
return buildLayout(
pageConfig['layout'] ?? {'type': 'column'},
pageConfig['widgets'],
context,
);
}

Widget buildLayout(Map<String, dynamic> layoutConfig, List<dynamic> widgetsConfig, BuildContext context) {
List<Widget> widgets = [
for (var widgetConfig in widgetsConfig) buildWidget(widgetConfig, context)
];

switch (layoutConfig['type']) {
case 'column':
return Column(
mainAxisAlignment: getMainAxisAlignment(layoutConfig['alignment']),
children: widgets,
);
case 'row':
return Row(
mainAxisAlignment: getMainAxisAlignment(layoutConfig['alignment']),
children: widgets,
);
default:
return Column(
children: widgets,
);
}
}

MainAxisAlignment getMainAxisAlignment(String? alignment) {
switch (alignment) {
case 'center':
return MainAxisAlignment.center;
case 'start':
return MainAxisAlignment.start;
case 'end':
return MainAxisAlignment.end;
default:
return MainAxisAlignment.start;
}
}

Widget buildWidget(Map<String, dynamic> widgetConfig, BuildContext context) {
switch (widgetConfig['type']) {
case 'text':
return Padding(
padding: const EdgeInsets.all(8.0),
child: Text(
widgetConfig['data'],
style: TextStyle(
fontSize: widgetConfig['style']?['fontSize']?.toDouble(),
color: widgetConfig['style']?['color'] != null
? Color(int.parse(widgetConfig['style']!['color'].substring(1, 7), radix: 16) + 0xFF000000)
: null,
),
),
);
case 'button':
return Padding(
padding: const EdgeInsets.all(8.0),
child: ElevatedButton(
onPressed: () => handleAction(widgetConfig, context),
style: ButtonStyle(
backgroundColor: widgetConfig['style']?['backgroundColor'] != null
? MaterialStateProperty.all<Color>(
Color(int.parse(widgetConfig['style']!['backgroundColor'].substring(1, 7), radix: 16) + 0xFF000000))
: null,
),
child: Text(
widgetConfig['data'],
style: TextStyle(
color: widgetConfig['style']?['textColor'] != null
? Color(int.parse(widgetConfig['style']!['textColor'].substring(1, 7), radix: 16) + 0xFF000000)
: null,
),
),
),
);
case 'image':
return Padding(
padding: const EdgeInsets.all(8.0),
child: Image.network(
widgetConfig['url'],
width: widgetConfig['style']?['width']?.toDouble(),
height: widgetConfig['style']?['height']?.toDouble(),
),
);
default:
return Container();
}
}

void handleAction(Map<String, dynamic> widgetConfig, BuildContext context) {
switch (widgetConfig['action']) {
case 'show_alert':
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text('Alert'),
content: Text(widgetConfig['alert_message']),
actions: [
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text('OK'),
),
],
),
);
break;
// Add other actions here
default:
break;
}
}
}

The above code creates ui by processing the incoming json. This example is for beginner level only, for more complex designs a more complex json should be prepared and this json can be created visually by a panel (like flutterflow). If you have a more logical suggestion, please leave it in the comments.

--

--