flutter-duit-bdui
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.
Packaged view
This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.
Install command
npx @skill-hub/cli install madteacher-mad-agents-skills-flutter-duit-bdui
Repository
Skill path: flutter-duit-bdui
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.
Open repositoryBest for
Primary workflow: Ship Full Stack.
Technical facets: Full Stack, Frontend, Backend.
Target audience: everyone.
License: Unknown.
Original source
Catalog source: SkillHub Club.
Repository owner: MADTeacher.
This is still a mirrored public skill entry. Review the repository before installing into production workflows.
What it helps with
- Install flutter-duit-bdui into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
- Review https://github.com/MADTeacher/mad-agents-skills before adding flutter-duit-bdui to shared team environments
- Use flutter-duit-bdui for development workflows
Works across
Favorites: 0.
Sub-skills: 0.
Aggregator: No.
Original source / Raw 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
1. Add dependency to pubspec.yaml
2. Initialize DuitRegistry (optional: with themes/custom widgets)
3. Create XDriver (HTTP, WebSocket, or static)
4. Wrap UI in DuitViewHost
5. Server sends JSON layouts → Duit renders them
## Prerequisites
### SDK Requirements
```yaml
- Dart SDK: >=3.4.4 <4.0.0
- Flutter: >=3.24.0
```
### Add Dependency
```bash
flutter pub add flutter_duit
```
Install:
```bash
flutter pub get
```
## Basic Integration
### Minimal Setup
```dart
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:
```dart
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:
```dart
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:
```dart
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:
```dart
final driver = XDriver.static(
layoutJson,
);
```
### Custom Decoder/Encoder
```dart
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:
```dart
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
```dart
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.
```dart
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 `data` field
- **objectKey**: Key in the `data` object
- **attributeKey**: Attribute in the widget to bind to
- **defaultValue**: Optional default value if data key is missing
You can register multiple components at once:
```dart
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](./references/capabiliteis.md) — Notes about capability-based design and core framework parts overriding
- [troubleshooting.md](./references/troubleshooting.md) - Notes about common issues in framework integration
- [environvent_vars.md](./references//environment_vars.md) — Notes about avalilable env variables and its usage
- [public_api.md](./references/public_api.md) — Notes about driver public API
- <https://duit.pro/docs/en> — official documentation site
---
## Referenced Files
> The following files are referenced in this skill and included for context.
### references/capabiliteis.md
```markdown
# About Duit capability-based API design
Duit framework uses a capability-based architecture that allows developers to customize and extend core functionality through modular delegates. This design pattern separates concerns and provides flexibility in how different aspects of the framework are implemented.
## Available Capabilities
The flutter_duit package provides a concrete implementation for each delegate:
| Delegate | Purpose |
|----------|---------|
| `ViewModelCapabilityDelegate` | View model management, UI events, layout structure parsing |
| `TransportCapabilityDelegate` | Transport layer (HTTP, WebSocket, static content) |
| `ServerActionExecutionCapabilityDelegate` | Server action execution and event handling |
| `UIControllerCapabilityDelegate` | UI element controller management (TextField, Checkbox, etc.) |
| `FocusCapabilityDelegate` | Focus management and element navigation |
| `ScriptingCapabilityDelegate` | Embedded script execution |
| `LoggingCapabilityDelegate` | Logging with support for different levels |
| `NativeModuleCapabilityDelegate` | Native code interaction via MethodChannel |
## Creating Custom Implementations
To create a custom capability implementation, simply create a class with the corresponding mixin:
```dart
final class MyCustomFocusManager with FocusCapabilityDelegate {
late final UIDriver _driver;
@override
void linkDriver(UIDriver driver) => _driver = driver;
@override
void requestFocus(String nodeId) {
// Custom focus management logic
}
@override
void releaseResources() {
// Resource cleanup
}
// Implementation of remaining methods...
}
```
## Best Practices
1. **Always call `linkDriver()`** when implementing custom capabilities to enable communication with the driver
2. **Implement `releaseResources()`** to clean up resources (close connections, cancel streams, etc.)
3. **Use proper error handling** and log errors via `driver.logError()`
4. **Keep capabilities focused** on a single responsibility
5. **Document custom implementations** for maintainability
---
## Conclusion
Duit's capability-based API provides a powerful and flexible architecture for building backend-driven UI applications. By understanding and customizing capabilities, you can:
- Extend functionality without modifying core framework code
- Optimize for specific use cases (mobile, web, embedded)
- Integrate with existing systems (analytics, logging, authentication)
- Add platform-specific features (native modules, JS execution)
- Improve testing and debugging capabilities
The modular design makes Duit adaptable to various requirements while maintaining a clean separation of concerns.
```
### references/troubleshooting.md
```markdown
# Troubleshooting
## Common Issues
**Widget not rendering:**
- Check widget type is registered (for custom widgets)
- Verify JSON structure matches expected format
- Ensure `id` is unique across layout
**Driver initialization failing:**
- Check network connectivity
- Verify base URL and headers
- Check server is returning valid JSON
**Theme not applying:**
- Ensure DuitRegistry.initialize called before runApp
- Verify theme name in JSON matches registered theme
- Check overrideRule if attributes conflict with theme
**Memory leaks:**
- Always dispose XDriver in StatefulWidgets
- Dispose custom transport managers properly
```
### references//environment_vars.md
```markdown
# Environment variables
This page describes compile-time environment variables used across DUIT packages and how to enable/disable them in different workflows.
All variables below are compile-time constants resolved via bool.fromEnvironment, which means they are evaluated at build time and must be provided through the toolchain flags.
## Available variables
1) `duit:throw-on-unspecified-widget-type`
- Package: `flutter_duit`
- Type: `bool` (compile-time)
- Default: `true`
- Purpose: When an unspecified/unknown widget type is encountered, throw `ArgumentError` (when `true`) instead of returning a fallback empty widget (when `false`). Useful during development to surface schema/model issues early.
1) `duit:enable-warm-up`
- Package: `duit_kernel`
- Type: `bool` (compile-time)
- Default: `false`
- Purpose: Enables attribute warm-up routines. When enabled, the kernel may pre-initialize attribute-related structures to reduce first-use latency.
1) `duit:prefer-inline`
- Package: `duit_kernel`
- Type: `bool` (compile-time)
- Default: `true`
- Purpose: Favors inline function strategies in the kernel where supported. Intended for advanced performance tuning and experimentation.
1) `duit:allow-focus-node-override`
- Package: `flutter_duit`
- Type: `bool` (compile-time)
- Default: `false`
- Purpose: Favors inline function strategies in the kernel where supported. Intended for advanced performance tuning and experimentation.
- Назначение: Defines the behavior when binding a `FocusNode` to a driver if an attempt is made to bind a node with the same `nodeId` again.
## How to set
These are compile-time flags and should be passed to the build/test commands.
#### Flutter (run/build/test)
Use `--dart-define` for Flutter CLI commands.
Run the app:
```bash
flutter run -d macos \
--dart-define=duit:throw-on-unspecified-widget-type=false \
--dart-define=duit:enable-warm-up=true \
--dart-define=duit:prefer-inline=true
```
```
### references/public_api.md
```markdown
# Public Driver API
## Overview
The Public Driver API (`XDriver`) is the public interface for working with the Duit driver. It represents an **extension type wrapper** over the internal `UIDriver`, providing a convenient interface for initializing and managing a Duit application in various operation modes.
### Architecture
```
XDriver (extension type)
↓ wraps
UIDriver (internal)
↓ uses
DuitDriverCompat (implementation)
```
`XDriver` implements the `FocusCapabilityDelegate` interface to support focus management in the UI.
## Supported Operation Modes
### 1. Remote (Remote Mode)
- Connection to a remote server through the transport layer
- UI is dynamically loaded from the server
- Support for live updates through the transport
- Ideal for backend-driven UI
### 2. Static (Static Mode)
- Working with predefined JSON content without server requests
- Suitable for offline mode, testing, or applications with fixed UI
- Uses `StubTransportManager` by default
### 3. Native Module (Module Mode)
- Integration of Duit as a module into an existing Flutter application
- Uses `NativeTransportManager` by default for communication with the host application
- Allows embedding Duit UI into native applications
## Capability Delegates (Managers)
The driver supports multiple delegates to extend functionality:
| Delegate | Interface | Purpose |
|----------|-----------|---------|
| `transportManager` | `TransportCapabilityDelegate` | Transport layer management (HTTP/WebSocket/Native) |
| `nativeModuleManager` | `NativeModuleCapabilityDelegate` | Platform code invocation |
| `scriptingManager` | `ScriptingCapabilityDelegate` | Client-side script execution |
| `loggingManager` | `LoggingCapabilityDelegate` | Logging customization |
| `focusManager` | `FocusCapabilityDelegate` | UI element focus management |
| `actionManager` | `ServerActionExecutionCapabilityDelegate` | Server action execution |
| `controllerManager` | `UIControllerCapabilityDelegate` | Widget state management |
| `viewManager` | `ViewModelCapabilityDelegate` | View model management |
| `actionParser` | `Parser<ServerAction>` | Parsing server actions from JSON |
| `eventParser` | `Parser<ServerEvent>` | Parsing server events from JSON |
## Public Methods
### Factory Constructors
#### `XDriver.remote()`
Creates a driver for working with a remote Duit server.
**Required parameters:**
- `transportManager` - transport layer manager
**Optional parameters:**
- `initialRequestPayload` - data for the first request to the server
- All capability delegates
#### `XDriver.static()`
Creates a driver for working with static JSON content.
**Required parameters:**
- `content` - JSON structure describing the UI
**Optional parameters:**
- `initialRequestPayload` - initial data for initialization
- `transportManager` - if not specified, uses `StubTransportManager`
- All capability delegates
**Throws:**
- `StateError` if `content` is empty
#### `XDriver.nativeModule()`
Creates a driver for native module mode.
**Optional parameters:**
- `initialRequestPayload` - initial data from the host application
- `transportManager` - if not specified, uses `NativeTransportManager`
- All capability delegates
#### `XDriver.from()` (internal)
Creates an `XDriver` from an existing `UIDriver` instance.
**For internal library use only.** Marked as `@internal`.
### Lifecycle Methods
#### `init()`
Initializes the driver and prepares it for operation.
**Performs:**
- Transport layer initialization
- Loading of initial UI content (for remote mode)
- Setup of all registered delegates and managers
- Event system preparation
**Returns:** `Future<void>` - completes when the driver is fully ready
**Important:** Call only once. Repeated calls may lead to unpredictable behavior.
#### `dispose()`
Releases resources used by the driver.
**Performs:**
- Closes transport connections
- Cancels external event stream subscriptions
- Clears internal caches and state
- Releases resources of all registered managers
- Removes all event handlers
**Important:** After calling `dispose`, the driver becomes unusable. It is recommended to call this in the `dispose` method of the widget using the driver.
### Event Handling Methods
#### `attachExternalHandler()`
Registers an external event handler.
**Parameters:**
- `type` - event handler type (`UserDefinedHandlerKind`)
- `handle` - handler function (`UserDefinedEventHandler`)
**Example:**
```dart
driver.attachExternalHandler(
UserDefinedHandlerKind.custom('onButtonClick'),
(eventData) {
print('Button clicked: ${eventData['id']}');
},
);
```
Handlers are called synchronously when the corresponding event occurs in the UI. One type can have only one handler; repeated registration replaces the previous one.
#### `addExternalEventStream()`
Adds an external event stream for processing by the driver.
**Parameters:**
- `stream` - event stream in JSON format (`Stream<Map<String, dynamic>>`)
**Example:**
```dart
final websocketStream = WebSocketChannel.connect(
Uri.parse('ws://example.com'),
).stream.map((data) => jsonDecode(data));
driver.addExternalEventStream(websocketStream);
```
The driver automatically subscribes to the stream when added and unsubscribes when `dispose` is called. Multiple streams can be added; they will be processed in parallel.
**Important:** Ensure that the event structure matches the expected Duit format or is handled by registered external handlers.
### Internal Methods
#### `asInternalDriver`
Provides access to the internal `UIDriver` instance.
**For internal library use only.** Marked as `@internal`. Should not be used in user code. Direct use of `UIDriver` may break encapsulation and lead to unpredictable behavior.
## Usage Examples
### Remote Mode
```dart
final driver = XDriver.remote(
transportManager: HttpTransportManager(
baseUrl: 'https://api.example.com',
),
initialRequestPayload: {
'userId': '12345',
'theme': 'dark',
},
loggingManager: CustomLogger(),
);
await driver.init();
```
### Static Mode
```dart
final uiContent = {
'type': 'Column',
'children': [
{'type': 'Text', 'data': 'Hello World'},
],
};
final driver = XDriver.static(uiContent);
await driver.init();
```
### Native Module Mode
```dart
final driver = XDriver.nativeModule(
nativeModuleManager: MyNativeModuleManager(),
initialRequestPayload: {
'hostVersion': '1.0.0',
'features': ['analytics', 'payments'],
},
);
await driver.init();
```
### Lifecycle Management
```dart
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late final XDriver driver;
@override
void initState() {
super.initState();
driver = XDriver.remote(
transportManager: myTransport,
);
driver.init();
}
@override
void dispose() {
driver.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) => ...;
}
```
## Usage Recommendations
1. **Mode Selection:** Use the mode according to the application architecture:
- Remote - for backend-driven UI
- Static - for tests or offline mode
- NativeModule - for integration into existing applications
2. **Lifecycle:** Always call `init()` before using the driver and `dispose()` when finished.
3. **Capability Delegates:** Connect only necessary delegates to optimize performance.
4. **Error Handling:** Wrap `init()` in try-catch to handle initialization errors.
5. **Single Use:** Do not create multiple driver instances for the same session.
## Design Rationale
Using an **extension type** instead of a regular class allows:
- **Performance:** Extension types do not create additional objects at runtime, reducing overhead
- **Encapsulation:** Hides the internal implementation (`UIDriver`) behind a clean public API
- **Compatibility:** Easily integrates with existing code without type changes
- **Easy Extension:** Ability to add new methods without modifying the base implementation
This decision aligns with modern Dart 3.0+ practices and provides an optimal balance between performance and usability.
```