Have you ever asked yourself how may code generation works for a no-code app development?

Rex Cole
3 min readJul 18, 2024

--

This is a very short dart code generation example.

Under the hood…

  • User Interface Modeling: When you drag and drop components in the visual editor, the application models these components in a structured format, often JSON or a similar data structure. This model describes the hierarchy and properties of each UI component.
  • Component Mapping: Each visual component in the drag-and-drop interface is mapped to a corresponding Flutter widget. For instance, a button in the visual editor corresponds to a RaisedButton or ElevatedButton in Flutter.
  • Property Binding: The properties set in the visual editor (like colors, sizes, and actions) are bound to the respective properties of Flutter widgets. This involves translating the visual settings into Dart properties.
  • Code Generation Engine: The platform uses a code generation engine that reads the UI model and the component mappings to generate the Dart code. This engine iterates over the UI model and constructs the corresponding Flutter widget tree.
  • State Management and Logic: For dynamic behavior and state management, the visual editor provides ways to define logic and actions. These definitions are converted into Dart code, typically using Flutter’s state management solutions like setState, Provider, or Riverpod.
  • Integration Points: For features like data binding and API integration, the tool generates code to handle network requests, parse responses, and update the UI accordingly.

Coding…

Generated using PlantUML

PlantUML Code.

@startuml
class UIComponent {
- String type
- Map<String, dynamic> properties
+ UIComponent({required this.type, required this.properties})
+ factory UIComponent.fromJson(Map<String, dynamic> json)
}

interface UIComponentBuilder {
+ void setProperties(Map<String, dynamic> properties)
+ String build()
}

class ButtonBuilder {
- String label
- String color
- String action
+ void setProperties(Map<String, dynamic> properties)
+ String build()
+ String generateColorCode(String color)
+ String generateActionCode(String action)
}

ButtonBuilder --> UIComponentBuilder : implements

class UIDirector {
- UIComponentBuilder? builder
+ void setBuilder(UIComponentBuilder builder)
+ String construct(Map<String, dynamic> properties)
}

UIComponent <.. UIDirector : uses
ButtonBuilder <.. UIDirector : uses

@enduml

Dart Code

import 'dart:convert';

// Define the model for a UI component
class UIComponent {
final String type;
final Map<String, dynamic> properties;

UIComponent({required this.type, required this.properties});

factory UIComponent.fromJson(Map<String, dynamic> json) {
return UIComponent(
type: json['type'],
properties: json['properties'],
);
}
}

abstract class UIComponentBuilder {
void setProperties(Map<String, dynamic> properties);
String build();
}

class ButtonBuilder implements UIComponentBuilder {
late String label;
late String color;
late String action;

@override
void setProperties(Map<String, dynamic> properties) {
label = properties['label'] ?? 'Button';
color = properties['color'] ?? '#000000';
action = properties['action'] ?? '';
}

@override
String build() {
return '''
ElevatedButton(
onPressed: () {
${generateActionCode(action)}
},
child: Text('$label'),
style: ElevatedButton.styleFrom(
backgroundColor: ${generateColorCode(color)},
),
)
''';
}

String generateColorCode(String color) {
if (color.startsWith('#')) {
// Convert hex color to Color object
return 'Color(0xFF${color.substring(1)})';
} else {
// Assume the color is a named color (e.g., Colors.white)
return 'Colors.$color';
}
}

String generateActionCode(String action) {
if (action.startsWith('navigate_to_')) {
return '';
}
// Add more action handlers as needed
return '';
}
}

class UIDirector {
UIComponentBuilder? builder;

void setBuilder(UIComponentBuilder builder) {
this.builder = builder;
}

String construct(Map<String, dynamic> properties) {
builder?.setProperties(properties);
return builder?.build() ?? '';
}
}

void main() {
// Example model
String jsonString = '''
{
"type": "button",
"properties": {
"label": "Click Me",
"color": "white",
"action": "navigate_to_ScreenB"
}
}
''';

// Convert JSON string to UIComponent
Map<String, dynamic> json = jsonDecode(jsonString);
UIComponent component = UIComponent.fromJson(json);

// Create the director and builder
UIDirector director = UIDirector();
UIComponentBuilder builder;

// Choose the correct builder based on component type
switch (component.type) {
case 'button':
builder = ButtonBuilder();
break;
// Add more cases for other component types
default:
throw UnsupportedError('Unsupported component type: ${component.type}');
}

// Construct the UI component
director.setBuilder(builder);
String dartCode = director.construct(component.properties);
print(dartCode);
}

Tadaaa! Now you have successfully created a simple button code generator!

--

--

No responses yet