Build or update the BlueBubbles external channel plugin for Moltbot (extension package, REST...
npx skills add MADTeacher/mad-agents-skills --skill "flutter-duit-bdui"
Install specific skill from multi-skill repository
# Description
Integrate Duit framework into Flutter applications including setup, driver configuration, HTTP/WebSocket transports, custom widgets, and themes. Use when integrating backend-driven UI, configuring Duit, or adding Duit to Flutter applications.
# SKILL.md
name: flutter-duit-bdui
description: Integrate Duit framework into Flutter applications including setup, driver configuration, HTTP/WebSocket transports, custom widgets, and themes. Use when integrating backend-driven UI, configuring Duit, or adding Duit to Flutter applications.
Fluttter Duit Backend-driven UI
Overview
Duit enables backend-driven UI in Flutter applications. The server controls both data and layout via JSON, allowing UI updates without app releases.
Quick Start
- Add dependency to pubspec.yaml
- Initialize DuitRegistry (optional: with themes/custom widgets)
- Create XDriver (HTTP, WebSocket, or static)
- Wrap UI in DuitViewHost
- Server sends JSON layouts β Duit renders them
Prerequisites
SDK Requirements
- Dart SDK: >=3.4.4 <4.0.0
- Flutter: >=3.24.0
Add Dependency
flutter pub add flutter_duit
Install:
flutter pub get
Basic Integration
Minimal Setup
import 'package:flutter/material.dart';
import 'package:flutter_duit/flutter_duit.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: DuitViewHost.withDriver(
driver: XDriver.static({
"type": "Text",
"id": "1",
"attributes": {"data": "Hello, World!"},
}),
),
),
);
}
}
Driver Lifecycle Management
Always dispose drivers to prevent memory leaks:
class MyWidgetState extends State<MyWidget> {
late final XDriver driver;
@override
void initState() {
super.initState();
driver = XDriver.static(/* ... */);
}
@override
void dispose() {
driver.dispose();
super.dispose();
}
}
Transport Configuration
HTTP Transport
Fetch layouts from REST API endpoints:
final driver = XDriver(
transportManager: HttpTransportManager(
options: HttpTransportOptions(
baseUrl: 'https://api.example.com/view',
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
),
),
);
WebSocket Transport
Real-time bidirectional communication:
final driver = XDriver(
transportManager: WSTransportManager(
options: WSTransportOptions(
url: 'wss://api.example.com/ws',
headers: {
'Authorization': 'Bearer $token',
},
reconnectInterval: Duration(seconds: 5),
heartbeatInterval: Duration(seconds: 30),
),
),
);
Static/Stub Transport
For testing or local layouts:
final driver = XDriver.static(
layoutJson,
);
Custom Decoder/Encoder
import 'dart:convert';
import 'dart:typed_data';
class CustomDecoder extends Converter<Uint8List, Map<String, dynamic>> {
@override
Map<String, dynamic> convert(Uint8List input) {
// Custom decode logic
return jsonDecode(utf8.decode(input));
}
}
final driver = XDriver(
transportManager: HttpTransportManager(
options: HttpTransportOptions(
baseUrl: 'https://api.example.com',
decoder: CustomDecoder(),
),
),
);
Custom Transport
Create your own transport implementation if needed:
class MyCustomTransportManager with TransportCapabilityDelegate {
@override
void linkDriver(UIDriver driver) {
// Implement linkDriver method
}
@override
Stream<Map<String, dynamic>> connect({
Map<String, dynamic>? initialRequestData,
Map<String, dynamic>? staticContent,
}) async* {
// Implement connect method
}
@override
Future<Map<String, dynamic>?> executeRemoteAction(
ServerAction action,
Map<String, dynamic> payload,
) async {
//Implement executeRemoteAction method
}
@override
Future<Map<String, dynamic>?> request(
String url,
Map<String, dynamic> meta,
Map<String, dynamic> body,
) async {
//Implement request method
}
@override
void releaseResources() {
// Implement linkDriver method
}
}
Custom Widgets
Create and register Custom Widget
import 'package:flutter_duit/flutter_duit.dart';
// 1. Define custom widget
class MyCustomWidget extends StatelessWidget {
final ViewAttribute attributes;
const MyCustomWidget({
required this.attributes,
super.key,
});
@override
Widget build(BuildContext context) {
final attrs = attributes.payload;
return Container(
child: Text(attrs.getString(key: "message")),
);
}
}
// 2. Create build factory fn for widget
Widget myCustomBuildFactory(ElementPropertyView model) {
if (model.isControlled) {
return MyCustomWidget(
attributes: model.attributes,
);
} else {
return const SizedBox.shrink();
}
}
// 3. Register build-fn
void main() async {
WidgetsFlutterBinding.ensureInitialized();
DuitRegistry.register(
"MyCustomWidget",
buildFactory: myCustomBuildFactory,
);
runApp(const MyApp());
}
Components
Components registration
Components allow you to create reusable UI templates that can be referenced by a tag and populated with dynamic data.
import 'package:flutter_duit/flutter_duit.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Define component template
final cardComponent = {
"tag": "CardComponent",
"layoutRoot": {
"type": "Container",
"id": "cardContainer",
"controlled": false,
"attributes": {
"padding": {"all": 16},
"margin": {"all": 8},
"decoration": {
"borderRadius": 12,
"color": "#FFFFFF",
"boxShadow": [
{
"color": "#00000033",
"blurRadius": 6,
"offset": {"dx": 0, "dy": 2},
},
],
},
},
"children": [
{
"type": "Text",
"id": "cardTitle",
"controlled": false,
"attributes": {
"data": {
"style": {
"fontSize": 18,
"fontWeight": "w600",
"color": "#333333",
},
},
"refs": [
{
"objectKey": "title",
"attributeKey": "data",
},
],
},
},
{
"type": "Text",
"id": "cardDescription",
"controlled": false,
"attributes": {
"data": {
"style": {
"fontSize": 14,
"color": "#666666",
},
},
"refs": [
{
"objectKey": "description",
"attributeKey": "data",
},
],
},
},
],
},
};
// Register the component
await DuitRegistry.registerComponents([cardComponent]);
runApp(const MyApp());
}
// Usage in JSON layout from server:
// {
// "type": "Component",
// "id": "card1",
// "tag": "CardComponent",
// "data": {
// "title": "Hello World",
// "description": "This is a card component"
// }
// }
Key concepts:
- tag: Unique identifier for the component
- layoutRoot: Root element of the component template
- refs: References to dynamic data passed via the
datafield - objectKey: Key in the
dataobject - attributeKey: Attribute in the widget to bind to
- defaultValue: Optional default value if data key is missing
You can register multiple components at once:
await DuitRegistry.registerComponents([
cardComponent,
buttonComponent,
listItemComponent,
]);
When to Use This Skill
Use this skill when:
- Integration flutter_duit library into project
- Custom widet creation
- Components registration
- Basic framework behavior overriding via capabilities implementation
- Need help with the framework API
Resources
Reference Documentation
- capabilities.md β Notes about capability-based design and core framework parts overriding
- troubleshooting.md - Notes about common issues in framework integration
- environvent_vars.md β Notes about avalilable env variables and its usage
- public_api.md β Notes about driver public API
- https://duit.pro/docs/en β official documentation site
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.