dynamic_ui_engine 1.0.0
dynamic_ui_engine: ^1.0.0 copied to clipboard
A JSON-driven dynamic UI engine for Flutter that enables server-driven UI, navigation, and API workflows at runtime.
import 'package:flutter/material.dart';
import 'package:dynamic_ui_engine/dynamic_ui_engine.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Dynamic UI Engine Example',
theme: ThemeData(useMaterial3: true),
initialRoute: '/',
routes: {
'/': (context) => const DynamicUiScreen(),
'/home': (context) => const Scaffold(
body: Center(child: Text('Home screen')),
),
'/profile': (context) {
final args = ModalRoute.of(context)?.settings.arguments as Map<String, dynamic>?;
final userId = args?['userId'] ?? '?';
return Scaffold(
body: Center(child: Text('Profile – userId: $userId')),
);
},
},
);
}
}
/// Sample JSON: column with text (with binding) and button (sequential api_call + navigate).
const sampleJson = '''
{
"schemaVersion": "1.0",
"ui": {
"type": "column",
"children": [
{
"type": "text",
"data": {
"value": "Welcome {{user.name}}",
"style": { "fontSize": 18, "color": "#333333" }
}
},
{
"type": "button",
"data": {
"label": "Load user then go Home",
"onClick": {
"type": "action_group",
"execution": "sequential",
"actions": [
{
"type": "api_call",
"url": "/user",
"bindTo": "user"
},
{
"type": "navigate",
"route": "/home"
}
]
}
}
},
{
"type": "button",
"data": {
"label": "Go to Profile (userId: 42)",
"onClick": {
"type": "navigate",
"route": "/profile",
"arguments": { "userId": 42 }
}
}
}
]
}
}
''';
class DynamicUiScreen extends StatefulWidget {
const DynamicUiScreen({super.key});
@override
State<DynamicUiScreen> createState() => _DynamicUiScreenState();
}
class _DynamicUiScreenState extends State<DynamicUiScreen> {
Map<String, dynamic> bindingContext = {};
void _updateBindingContext(Map<String, dynamic> updates) {
setState(() {
for (final e in updates.entries) {
bindingContext[e.key] = e.value;
}
});
}
@override
Widget build(BuildContext context) {
final actionHandler = _ExampleActionHandler(
context: context,
onStateUpdate: _updateBindingContext,
);
final widget = DynamicWidgetBuilder.buildFromString(
sampleJson,
actionHandler: actionHandler,
bindingContext: bindingContext.isEmpty ? null : bindingContext,
);
return Scaffold(
appBar: AppBar(title: const Text('Dynamic UI Engine')),
body: SafeArea(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: widget,
),
),
);
}
}
class _ExampleActionHandler implements ActionHandler {
_ExampleActionHandler({
required this.context,
required this.onStateUpdate,
});
final BuildContext context;
final void Function(Map<String, dynamic> updates) onStateUpdate;
@override
Future<void> handle(DynamicAction action) async {
switch (action.type) {
case ActionType.navigate:
final route = action.route ?? '/';
final args = action.arguments;
if (context.mounted) {
Navigator.of(context).pushNamed(route, arguments: args);
}
break;
case ActionType.apiCall:
// Mock API: simulate fetching user and bind to state
await Future.delayed(const Duration(milliseconds: 300));
final bindTo = action.bindTo;
if (bindTo != null) {
onStateUpdate({
bindTo: {'name': 'Alice', 'id': 1},
});
}
break;
case ActionType.actionGroup:
// Handled by ActionEngine; handler receives only single actions
break;
}
}
}