flutter-duit-bdui
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFluttter Duit Backend-driven UI
Flutter Duit 后端驱动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.
Duit可在Flutter应用中实现后端驱动UI。服务器通过JSON控制数据与布局,无需发布应用即可更新UI。
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
- 向pubspec.yaml添加依赖
- 初始化DuitRegistry(可选:配置主题/自定义组件)
- 创建XDriver(HTTP、WebSocket或静态类型)
- 用DuitViewHost包裹UI
- 服务器发送JSON布局 → Duit渲染界面
Prerequisites
前置要求
SDK Requirements
SDK要求
yaml
- Dart SDK: >=3.4.4 <4.0.0
- Flutter: >=3.24.0yaml
- Dart SDK: >=3.4.4 <4.0.0
- Flutter: >=3.24.0Add Dependency
添加依赖
bash
flutter pub add flutter_duitInstall:
bash
flutter pub getbash
flutter pub add flutter_duit安装依赖:
bash
flutter pub getBasic 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});
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: DuitViewHost.withDriver(
driver: XDriver.static({
"type": "Text",
"id": "1",
"attributes": {"data": "Hello, World!"},
}),
),
),
);
}
}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});
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
Driver生命周期管理
Always dispose drivers to prevent memory leaks:
dart
class MyWidgetState extends State<MyWidget> {
late final XDriver driver;
void initState() {
super.initState();
driver = XDriver.static(/* ... */);
}
void dispose() {
driver.dispose();
super.dispose();
}
}务必销毁Driver以避免内存泄漏:
dart
class MyWidgetState extends State<MyWidget> {
late final XDriver driver;
void initState() {
super.initState();
driver = XDriver.static(/* ... */);
}
void dispose() {
driver.dispose();
super.dispose();
}
}Transport Configuration
传输配置
HTTP Transport
HTTP传输
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',
},
),
),
);从REST API端点获取布局:
dart
final driver = XDriver(
transportManager: HttpTransportManager(
options: HttpTransportOptions(
baseUrl: 'https://api.example.com/view',
headers: {
'Authorization': 'Bearer $token',
'Content-Type': 'application/json',
},
),
),
);WebSocket Transport
WebSocket传输
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),
),
),
);实时双向通信:
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,
);用于测试或本地布局:
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>> {
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(),
),
),
);dart
import 'dart:convert';
import 'dart:typed_data';
class CustomDecoder extends Converter<Uint8List, Map<String, dynamic>> {
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 {
void linkDriver(UIDriver driver) {
// Implement linkDriver method
}
Stream<Map<String, dynamic>> connect({
Map<String, dynamic>? initialRequestData,
Map<String, dynamic>? staticContent,
}) async* {
// Implement connect method
}
Future<Map<String, dynamic>?> executeRemoteAction(
ServerAction action,
Map<String, dynamic> payload,
) async {
//Implement executeRemoteAction method
}
Future<Map<String, dynamic>?> request(
String url,
Map<String, dynamic> meta,
Map<String, dynamic> body,
) async {
//Implement request method
}
void releaseResources() {
// Implement linkDriver method
}
}如有需要,可创建自己的传输实现:
dart
class MyCustomTransportManager with TransportCapabilityDelegate {
void linkDriver(UIDriver driver) {
// Implement linkDriver method
}
Stream<Map<String, dynamic>> connect({
Map<String, dynamic>? initialRequestData,
Map<String, dynamic>? staticContent,
}) async* {
// Implement connect method
}
Future<Map<String, dynamic>?> executeRemoteAction(
ServerAction action,
Map<String, dynamic> payload,
) async {
//Implement executeRemoteAction method
}
Future<Map<String, dynamic>?> request(
String url,
Map<String, dynamic> meta,
Map<String, dynamic> body,
) async {
//Implement request method
}
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,
});
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());
}dart
import 'package:flutter_duit/flutter_duit.dart';
// 1. 定义自定义组件
class MyCustomWidget extends StatelessWidget {
final ViewAttribute attributes;
const MyCustomWidget({
required this.attributes,
super.key,
});
Widget build(BuildContext context) {
final attrs = attributes.payload;
return Container(
child: Text(attrs.getString(key: "message")),
);
}
}
// 2. 为组件创建构建工厂函数
Widget myCustomBuildFactory(ElementPropertyView model) {
if (model.isControlled) {
return MyCustomWidget(
attributes: model.attributes,
);
} else {
return const SizedBox.shrink();
}
}
// 3. 注册构建函数
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 field
data - objectKey: Key in the object
data - 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,
]);组件允许您创建可复用的UI模板,通过标签引用并填充动态数据。
dart
import 'package:flutter_duit/flutter_duit.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// 定义组件模板
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",
},
],
},
},
],
},
};
// 注册组件
await DuitRegistry.registerComponents([cardComponent]);
runApp(const MyApp());
}
// 在服务器返回的JSON布局中使用:
// {
// "type": "Component",
// "id": "card1",
// "tag": "CardComponent",
// "data": {
// "title": "Hello World",
// "description": "This is a card component"
// }
// }核心概念:
- tag: 组件的唯一标识符
- layoutRoot: 组件模板的根元素
- refs: 对通过字段传递的动态数据的引用
data - objectKey: 对象中的键
data - attributeKey: 要绑定的组件属性
- defaultValue: 可选,当数据键缺失时的默认值
您可以一次性注册多个组件:
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
在以下场景中使用本技能:
- 将flutter_duit库集成到项目中
- 创建自定义组件
- 注册组件
- 通过能力实现重写基础框架行为
- 需要框架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
- capabilities.md — 基于能力的设计及核心框架部分重写说明
- troubleshooting.md - 框架集成中的常见问题说明
- environvent_vars.md — 可用环境变量及其使用说明
- public_api.md — Driver公开API说明
- https://duit.pro/docs/en — 官方文档网站