sdui_kit 0.0.1
sdui_kit: ^0.0.1 copied to clipboard
A Server-Driven UI (SDUI) Flutter plugin that renders dynamic widgets from JSON. Supports text, title, description, table, grid, array, section, action, docs, html, and details components — each with [...]
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:sdui_kit/sdui_kit.dart';
void main() => runApp(const SduiExampleApp());
class SduiExampleApp extends StatelessWidget {
const SduiExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'sdui_kit Example',
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF1565C0)),
useMaterial3: true,
),
home: const SduiDemoScreen(),
);
}
}
// ---------------------------------------------------------------------------
// Demo Screen — uses the exact same JSON structure as test_service.dart
// ---------------------------------------------------------------------------
class SduiDemoScreen extends StatefulWidget {
const SduiDemoScreen({super.key});
@override
State<SduiDemoScreen> createState() => _SduiDemoScreenState();
}
class _SduiDemoScreenState extends State<SduiDemoScreen> {
// This mirrors the exact payload from test_service.dart
static final _serverResponse = {
"id": "0111",
"name": "View data documentation",
"viewData": [
// ── TEXT ────────────────────────────────────────────────────────────
{
"type": "text",
"label": "Text (Default)",
"data": "This is a default text item.",
"style": "default"
},
{
"type": "text",
"label": "Text (Style 1)",
"data": "This is text with style1.",
"style": "style1"
},
{
"type": "text",
"label": "Text (Style 2)",
"data": "This is text with style2.",
"style": "style2"
},
{
"type": "text",
"label": "Text (Style 3)",
"data": "This is text with style3.",
"style": "style3"
},
{
"type": "text",
"label": "Text (Value Align Center)",
"data": "Centered Text",
"style": "default",
"valueAlign": "center"
},
// ── TITLE ───────────────────────────────────────────────────────────
{"type": "title", "data": "Title (Default)", "style": "default"},
{"type": "title", "data": "Title (Style 1)", "style": "style1"},
// ── DESCRIPTION ─────────────────────────────────────────────────────
{
"type": "description",
"data":
"This is a default description. It usually contains longer text that explains something in detail.",
"style": "default"
},
{
"type": "description",
"data": "This is a description with style1.",
"style": "style1"
},
// ── DETAILS ─────────────────────────────────────────────────────────
{
"type": "details",
"label": "Details Section",
"style": "default",
"data": {
"title": "Detail Title",
"description": "Detail Description content goes here.",
"style": "default"
}
},
// ── SECTION ─────────────────────────────────────────────────────────
{
"type": "section",
"label": "Section (Default)",
"style": "default",
"data": [
{
"type": "text",
"label": "Inside Section",
"data": "Nested text item",
"style": "default"
},
]
},
{
"type": "section",
"label": "Section (Style 1)",
"style": "style1",
"data": [
{"type": "title", "data": "Section Title", "style": "default"},
]
},
// ── TABLE ───────────────────────────────────────────────────────────
{
"type": "table",
"label": "Table (Default)",
"style": "default",
"data": [
{"Col1": "Val1", "Col2": "Val2"},
{"Col1": "Val3", "Col2": "Val4"}
]
},
{
"type": "table",
"label": "Table (Style 1)",
"style": "style1",
"data": [
{"Header1": "Data1", "Header2": "Data2"}
]
},
{
"type": "table",
"label": "Table (Style 2)",
"style": "style2",
"data": [
{"Key": "Value", "Status": "Active"}
]
},
// ── GRID ────────────────────────────────────────────────────────────
{
"type": "grid",
"label": "Grid (Main & Sub)",
"style": "default",
"column": 2,
"data": [
{
"label": "Main Item",
"type": "main",
"value": "100",
"status": "active",
"colorOne": "0xFFE0E0E0",
"colorTwo": "0xFFFFFFFF"
},
{
"label": "Sub Item",
"type": "sub",
"value": "50",
"status": "pending",
"badge": "New"
},
{
"label": "Another Main",
"type": "main",
"value": "200",
"color": "0xFF00FF00"
}
]
},
{
"type": "grid",
"label": "Grid (Style 1)",
"style": "style1",
"column": 3,
"data": [
{"label": "Grid Item 1", "type": "main", "value": "A"},
{"label": "Grid Item 2", "type": "main", "value": "B"}
]
},
{
"type": "grid",
"label": "Grid (Style 2)",
"style": "style2",
"column": 2,
"data": [
{"label": "Grid Item 1", "type": "main", "value": "X"},
{"label": "Grid Item 2", "type": "main", "value": "Y"}
]
},
// ── DOCS ────────────────────────────────────────────────────────────
{
"type": "docs",
"label": "Documents (Default)",
"style": "default",
"data": [
{
"key": "doc1",
"title": "Document 1",
"url":
"https://www.princexml.com/samples/invoice-colorful/invoicesample.pdf",
"mime": "application/pdf",
"style": "default"
},
{
"key": "img1",
"title": "Image 1",
"url": "https://picsum.photos/200/300",
"mime": "image/png"
}
]
},
{
"type": "docs",
"label": "Documents (Style 2)",
"style": "style2",
"data": [
{
"key": "doc2",
"title": "Document 2",
"url":
"https://www.princexml.com/samples/invoice-colorful/invoicesample.pdf",
"mime": "application/pdf"
}
]
},
// ── ACTION ──────────────────────────────────────────────────────────
{
"type": "action",
"label": "Action Button (Default)",
"style": "default",
"data": {
"route": "/some/route",
"id": "123",
"code": "ACT001",
"type": "primary",
"baseApi": "/api/v1"
}
},
{
"type": "action",
"label": "Action Button (Style 1)",
"style": "action1",
"data": {
"route": "/style1/route",
"id": "456",
"type": "secondary"
}
},
{
"type": "action",
"label": "Action Button (Style 2)",
"style": "action2",
"data": {
"route": "/style2/route",
"id": "789",
"type": "tertiary"
}
},
// ── ARRAY ───────────────────────────────────────────────────────────
{
"type": "array",
"label": "Array (Default)",
"style": "default",
"data": ["Item 1", "Item 2", "Item 3", "Item A", "Item B", "Item C"]
},
{
"type": "array",
"label": "Array (Style 1)",
"style": "style1",
"data": ["Item A", "Item B", "Item C"]
},
{
"type": "array",
"label": "Array (Style 2)",
"style": "style2",
"data": [
"Iloooooooovovovovovovo",
"Item A",
"Item B",
"Item C",
2332 // integers are cast to String automatically
]
},
// ── HTML ────────────────────────────────────────────────────────────
{
"type": "html",
"label": "Html data",
"style": "default",
"data":
"<h1>Hello</h1><p>World</p><p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>"
},
// ── YOUTUBE VIDEO (not yet supported — shown as "Unsupported type") ─
{
"type": "ytVideo",
"label": "Youtube Video sample label",
"style": "default",
"data": "https://www.youtube.com/watch?v=SoiQPKQnMkg"
},
{
"type": "ytVideo",
"label": "Youtube Video shorts sample label",
"style": "default",
"data": "https://www.youtube.com/shorts/1NeG6xq5gVI"
},
],
};
late final List<SduiItem> _items;
String? _lastAction;
@override
void initState() {
super.initState();
_items = sduiParser(_serverResponse['viewData']);
}
@override
Widget build(BuildContext context) {
final name = _serverResponse['name'] as String;
return Scaffold(
backgroundColor: const Color(0xFFF4F6FA),
appBar: AppBar(
title: Text(name),
backgroundColor: const Color(0xFF1565C0),
foregroundColor: Colors.white,
elevation: 0,
actions: [
Padding(
padding: const EdgeInsets.only(right: 12),
child: Chip(
label: Text(
'${_items.length} items',
style: const TextStyle(fontSize: 12, color: Colors.white),
),
backgroundColor: Colors.white.withValues(alpha: 0.2),
side: BorderSide.none,
),
),
],
),
body: Column(
children: [
// Action feedback banner
if (_lastAction != null)
MaterialBanner(
backgroundColor: Colors.green.shade50,
content: Text(
'Action tapped → route: $_lastAction',
style: TextStyle(
color: Colors.green.shade800, fontWeight: FontWeight.w600),
),
actions: [
TextButton(
onPressed: () => setState(() => _lastAction = null),
child: const Text('DISMISS'),
),
],
),
Expanded(
child: SingleChildScrollView(
padding: const EdgeInsets.all(16),
child: SduiView(
items: _items,
primaryColor: const Color(0xFF1565C0),
onAction: (action) {
setState(() => _lastAction = action.route);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'route: ${action.route} | id: ${action.id} | type: ${action.type}',
),
behavior: SnackBarBehavior.floating,
duration: const Duration(seconds: 3),
),
);
},
),
),
),
],
),
);
}
}