Skip to content

Example: Dashboard Config

A complete dashboard chart widget configuration demonstrating UnionProperty for data source selection, EnumProperty for chart type, and conditional display options.

Domain Model

FieldTypeDescription
titleStringWidget display title
chartTypeEnumbar / line / pie / area
dataSourceUnionAPI endpoint / static data / entity query
dateRangeEnumlast7d / last30d / last90d / custom
customStartDateDateTimeStart date (only when custom range)
customEndDateDateTimeEnd date (only when custom range)
groupByEnumday / week / month / quarter
showLegendBoolDisplay legend
showGridBoolDisplay grid lines (bar/line/area only)
showLabelsBoolDisplay data labels
refreshIntervalIntAuto-refresh seconds (0 = disabled)

Enums

dart
enum ChartType { bar, line, pie, area }
enum DateRange { last7d, last30d, last90d, custom }
enum GroupBy { day, week, month, quarter }

Full Implementation

dart
import 'package:vyuh_property_system/vyuh_property_system.dart';

PropertyCollection buildDashboardChartConfig() {
  final b = PropertyCollectionBuilder();

  // ── General ────────────────────────────────────────────────
  b.group('general', 'General');

  b.string('title', 'Widget Title',
    required: true,
    defaultValue: 'Untitled Chart',
    validators: [PropertyValidators.maxLength(100)],
  );

  b.enumeration<ChartType>('chartType', 'Chart Type',
    options: [
      EnumOption(ChartType.bar, 'Bar Chart'),
      EnumOption(ChartType.line, 'Line Chart'),
      EnumOption(ChartType.pie, 'Pie Chart'),
      EnumOption(ChartType.area, 'Area Chart'),
    ],
    defaultValue: ChartType.bar,
  );

  // ── Date Range ─────────────────────────────────────────────
  b.group('dateRange', 'Date Range');

  b.enumeration<DateRange>('dateRange', 'Date Range',
    options: [
      EnumOption(DateRange.last7d, 'Last 7 Days'),
      EnumOption(DateRange.last30d, 'Last 30 Days'),
      EnumOption(DateRange.last90d, 'Last 90 Days'),
      EnumOption(DateRange.custom, 'Custom Range'),
    ],
    defaultValue: DateRange.last30d,
  );

  b.dateTime('customStartDate', 'Start Date',
    visibleCondition: PropertyCollectionBuilder.when<DateRange>(
      'dateRange', DateRange.custom,
    ),
  );

  b.dateTime('customEndDate', 'End Date',
    visibleCondition: PropertyCollectionBuilder.when<DateRange>(
      'dateRange', DateRange.custom,
    ),
  );

  b.enumeration<GroupBy>('groupBy', 'Group By',
    options: [
      EnumOption(GroupBy.day, 'Day'),
      EnumOption(GroupBy.week, 'Week'),
      EnumOption(GroupBy.month, 'Month'),
      EnumOption(GroupBy.quarter, 'Quarter'),
    ],
    defaultValue: GroupBy.day,
  );

  // ── Display Options ────────────────────────────────────────
  b.group('display', 'Display Options');

  b.boolean('showLegend', 'Show Legend', defaultValue: true);

  // Grid only makes sense for bar, line, and area charts
  b.boolean('showGrid', 'Show Grid Lines',
    defaultValue: true,
    visibleCondition: PropertyCollectionBuilder.whenIn<ChartType>(
      'chartType', {ChartType.bar, ChartType.line, ChartType.area},
    ),
  );

  b.boolean('showLabels', 'Show Data Labels', defaultValue: false);

  // ── Refresh ────────────────────────────────────────────────
  b.group('refresh', 'Auto-Refresh', initiallyCollapsed: true);

  b.integer('refreshInterval', 'Refresh Interval (sec)',
    defaultValue: 0,
    help: 'Set to 0 to disable auto-refresh',
    validators: [PropertyValidators.min(0), PropertyValidators.max(3600)],
  );

  return b.buildCollection();
}

Adding Data Source with UnionProperty

The data source is best modeled as a UnionProperty because each source type has different sub-properties:

dart
PropertyCollection buildFullDashboardChartConfig() {
  final collection = buildDashboardChartConfig();

  final dataSource = UnionProperty(
    key: 'dataSource',
    label: 'Data Source',
    group: 'general',
    defaultSelectedKey: 'api',
    options: [
      UnionOption(
        key: 'api',
        title: 'API Endpoint',
        description: 'Fetch data from a REST API',
        property: StringProperty(
          key: 'apiUrl',
          label: 'API URL',
          required: true,
          validators: [PropertyValidators.pattern(
            r'^https?://',
            message: 'Must start with http:// or https://',
          )],
          help: 'Full URL including query parameters',
        ),
      ),
      UnionOption(
        key: 'static',
        title: 'Static Data',
        description: 'Use manually entered data points',
        property: StringProperty(
          key: 'staticJson',
          label: 'JSON Data',
          required: true,
          help: 'Enter data as a JSON array',
        ),
      ),
      UnionOption(
        key: 'entityQuery',
        title: 'Entity Query',
        description: 'Query data from entity system',
        property: StringProperty(
          key: 'queryExpression',
          label: 'Query Expression',
          required: true,
          help: 'Entity query in the format: entity.field:aggregation',
        ),
      ),
    ],
  );

  collection.addProperty(dataSource);
  return collection;
}

Widget Usage

dart
class ChartConfigPanel extends StatefulWidget {
  final Map<String, dynamic>? savedConfig;
  final ValueChanged<Map<String, dynamic>> onSave;

  const ChartConfigPanel({
    super.key,
    this.savedConfig,
    required this.onSave,
  });

  @override
  State<ChartConfigPanel> createState() => _ChartConfigPanelState();
}

class _ChartConfigPanelState extends State<ChartConfigPanel> {
  late final PropertyCollection _config;

  @override
  void initState() {
    super.initState();
    _config = buildFullDashboardChartConfig();

    if (widget.savedConfig != null) {
      _config.fromJson(widget.savedConfig!);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Expanded(
          child: PropertyCollectionEditor(
            collection: _config,
            showGroups: true,
            disposeCollection: false,
          ),
        ),
        Padding(
          padding: const EdgeInsets.all(16),
          child: FilledButton(
            onPressed: () {
              if (_config.isValid) {
                widget.onSave(_config.toJson());
              }
            },
            child: const Text('Apply Configuration'),
          ),
        ),
      ],
    );
  }

  @override
  void dispose() {
    _config.dispose();
    super.dispose();
  }
}

Sample JSON Output

json
{
  "title": "Monthly Revenue",
  "chartType": "line",
  "dataSource": {
    "selectedKey": "api",
    "selectedValue": "https://api.example.com/revenue"
  },
  "dateRange": "last30d",
  "groupBy": "day",
  "showLegend": true,
  "showGrid": true,
  "showLabels": false,
  "refreshInterval": 60
}

With custom date range:

json
{
  "title": "Enrollment Trends",
  "chartType": "area",
  "dataSource": {
    "selectedKey": "entityQuery",
    "selectedValue": "enrollment.count:sum"
  },
  "dateRange": "custom",
  "customStartDate": "2026-01-01T00:00:00.000",
  "customEndDate": "2026-03-31T23:59:59.000",
  "groupBy": "week",
  "showLegend": true,
  "showGrid": true,
  "showLabels": true,
  "refreshInterval": 300
}

Condition Summary

Next Steps