fx_desktop 0.3.6 copy "fx_desktop: ^0.3.6" to clipboard
fx_desktop: ^0.3.6 copied to clipboard

Desktop-first Flutter components and CSS-like layout contracts for Xojo-style UI design.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart' show debugPaintSizeEnabled;
import 'package:fx_desktop/fx_desktop.dart';

void main() {
  runApp(const FxDesktopExampleApp());
}

class FxDesktopExampleApp extends StatelessWidget {
  const FxDesktopExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'FxDesktop Example',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xff2563eb)),
        useMaterial3: true,
        extensions: const [FxTheme()],
      ),
      home: const ExampleHomePage(),
    );
  }
}

class ExampleHomePage extends StatefulWidget {
  const ExampleHomePage({super.key});

  @override
  State<ExampleHomePage> createState() => _ExampleHomePageState();
}

class _ExampleHomePageState extends State<ExampleHomePage> {
  final ScrollController scrollController = ScrollController();
  final FxUndoController undoController = FxUndoController();
  Set<String> selectedRowIds = const {'order-1'};
  Set<({String rowId, String columnId})> selectedCells = const {};
  FxGridCellRange? selectedRange;
  List<FxListBoxRow> listBoxRows = [
    const FxListBoxRow(
      id: 'order-1',
      cells: {
        'number': '1001',
        'customer': 'Omega SA',
        'status': 'Open',
        'approved': true,
      },
    ),
    const FxListBoxRow(
      id: 'order-2',
      cells: {
        'number': '1002',
        'customer': 'Gepard',
        'status': 'Confirmed',
        'approved': false,
      },
    ),
    const FxListBoxRow(
      id: 'order-3',
      cells: {
        'number': '1003',
        'customer': 'Cindy Crawford',
        'status': 'Draft',
        'approved': false,
      },
      enabled: false,
    ),
  ];
  List<FxGridRow> gridRows = [
    const FxGridRow(
      id: 'layout',
      cells: {
        'field': 'Layout',
        'desktop': 'DesktopFlexLayoutManager',
        'web': 'WebFlexLayoutManager',
        'enabled': true,
      },
    ),
    const FxGridRow(
      id: 'table',
      cells: {
        'field': 'Table',
        'desktop': 'DesktopListBox',
        'web': 'WebListBox',
        'enabled': false,
      },
    ),
  ];
  String? sortedListBoxColumnId;
  bool sortedListBoxAscending = true;
  String? sortedGridColumnId;
  bool sortedGridAscending = true;
  Map<String, Map<String, String>> listBoxValidationErrors = {
    'order-2': {'customer': 'Must be at least 3 characters'},
  };
  Map<String, Map<String, String>> gridValidationErrors = {};

  bool active = true;
  bool inactive = false;
  bool? optional;
  String? popupStatus = 'Open';
  String comboValue = 'Bangkok';
  String? radioChoice = 'standard';
  String? radioGroupChoice = 'email';
  double priority = 40;
  DateTime? dueDate = DateTime(2026, 6, 4);
  DateTime? reminderTime = DateTime(2026, 6, 4, 9, 30);
  DateTime? appointment = DateTime(2026, 6, 4, 14, 15);
  int selectedTabIndex = 0;
  int selectedPageIndex = 0;
  int selectedCardIndex = 0;
  bool orderExpanded = true;
  bool auditExpanded = false;
  Color? accentColor = const Color(0xff2563eb);
  Color? optionalColor;
  bool undoApproved = false;
  String? undoStatus = 'Draft';
  String undoCustomer = 'Cindy Crawford';
  double undoPriority = 30;
  double undoPriorityDragStart = 30;
  int undoTabIndex = 0;
  bool _showLayoutBounds = false;

  @override
  void dispose() {
    undoController.dispose();
    scrollController.dispose();
    super.dispose();
  }

  void _commitUndoValue<T>({
    required String label,
    required T oldValue,
    required T newValue,
    required void Function(T nextValue) apply,
  }) {
    undoController.commitValue<T>(
      label,
      oldValue: oldValue,
      newValue: newValue,
      apply: (nextValue) => setState(() => apply(nextValue)),
    );
  }

  @override
  Widget build(BuildContext context) {
    return FxUndoScope(
      controller: undoController,
      child: Scaffold(
        backgroundColor: const Color(0xfff6f7f9),
        body: SafeArea(
          child: LayoutBuilder(
            builder: (context, constraints) {
              return Scrollbar(
                controller: scrollController,
                thumbVisibility: true,
                child: SingleChildScrollView(
                  controller: scrollController,
                  child: SizedBox(
                    width: constraints.maxWidth,
                    child: Padding(
                      padding: const EdgeInsets.fromLTRB(20, 20, 52, 20),
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.stretch,
                        children: [
                          Row(
                            children: [
                              Expanded(
                                child: Column(
                                  crossAxisAlignment: CrossAxisAlignment.start,
                                  mainAxisSize: MainAxisSize.min,
                                  children: [
                                    Text(
                                      'FxDesktop Component Harness',
                                      style: Theme.of(context)
                                          .textTheme
                                          .headlineSmall
                                          ?.copyWith(
                                            fontWeight: FontWeight.w700,
                                          ),
                                    ),
                                    const SizedBox(height: 4),
                                    Text(
                                      'One component per row, optimized for desktop visual checks.',
                                      style: Theme.of(
                                        context,
                                      ).textTheme.bodyMedium,
                                    ),
                                  ],
                                ),
                              ),
                              Row(
                                mainAxisSize: MainAxisSize.min,
                                children: [
                                  const Text('Show Layout Bounds'),
                                  const SizedBox(width: 8),
                                  Switch(
                                    value: _showLayoutBounds,
                                    onChanged: (value) {
                                      setState(() {
                                        _showLayoutBounds = value;
                                        debugPaintSizeEnabled = value;
                                      });
                                    },
                                  ),
                                ],
                              ),
                            ],
                          ),

                          const SizedBox(height: 18),
                          _ComponentRow(
                            name: 'FxTextField Phase 2.5 Constraints & Masks',
                            child: _StateSamples(
                              sampleWidth: 320,
                              children: const [
                                _StateSample(
                                  label: 'Required + count',
                                  child: FxTextField(
                                    label: 'Customer',
                                    value: 'Omega SA',
                                    requiredInput: true,
                                    reserveSupportingTextSpace: true,
                                    helpText: 'Required single-line input.',
                                    constraints: FxTextInputConstraints(
                                      minLength: 3,
                                      maxLength: 24,
                                      showCharacterCount: true,
                                    ),
                                  ),
                                ),
                                _StateSample(
                                  label: 'Numeric',
                                  child: FxTextField(
                                    label: 'Order No',
                                    value: '589434',
                                    hintText: 'Digits only',
                                    constraints: FxTextInputConstraints(
                                      kind: FxTextInputConstraintKind.numeric,
                                      maxLength: 8,
                                      showCharacterCount: true,
                                    ),
                                    reserveSupportingTextSpace: true,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Alphanumeric',
                                  child: FxTextField(
                                    label: 'Campaign Code',
                                    value: 'KW2507',
                                    hintText: 'Letters and digits only',
                                    constraints: FxTextInputConstraints(
                                      kind: FxTextInputConstraintKind
                                          .alphanumeric,
                                      maxLength: 12,
                                    ),
                                    reserveSupportingTextSpace: true,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Forbidden chars',
                                  child: FxTextField(
                                    label: 'Slug',
                                    value: 'omega-sa',
                                    helpText: 'Spaces and slashes are removed.',
                                    reserveSupportingTextSpace: true,
                                    constraints: FxTextInputConstraints(
                                      forbiddenCharacters: [' ', '/'],
                                    ),
                                  ),
                                ),
                                _StateSample(
                                  label: 'Phone mask',
                                  child: FxTextField(
                                    label: 'Phone',
                                    value: '9-1234-5678',
                                    hintText: '#-####-####',
                                    format: FxTextInputFormat.pattern(
                                      '#-####-####',
                                    ),
                                    reserveSupportingTextSpace: true,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Fixed decimal',
                                  child: FxTextField(
                                    label: 'Budget',
                                    value: '1,234.50',
                                    helpText: 'Groups on commit/blur.',
                                    format: FxTextInputFormat.number(
                                      decimalDigits: 2,
                                    ),
                                    reserveSupportingTextSpace: true,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxTextArea Phase 2.5 Constraints',
                            child: _StateSamples(
                              sampleWidth: 420,
                              children: const [
                                _StateSample(
                                  label: 'Required + count',
                                  child: FxTextArea(
                                    label: 'Notes',
                                    value:
                                        'Confirm campaign, venue, and delivery window.',
                                    requiredInput: true,
                                    helpText:
                                        'Character counter can be visible.',
                                    reserveSupportingTextSpace: true,
                                    constraints: FxTextInputConstraints(
                                      minLength: 10,
                                      maxLength: 120,
                                      showCharacterCount: true,
                                    ),
                                    minLines: 3,
                                    maxLines: 4,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Forbidden pattern',
                                  child: FxTextArea(
                                    label: 'Internal Comment',
                                    value:
                                        'Visible notes stay concise and reviewable.',
                                    helpText:
                                        'Rejects configured forbidden patterns.',
                                    reserveSupportingTextSpace: true,
                                    constraints: FxTextInputConstraints(
                                      forbiddenPattern: r'password|secret',
                                    ),
                                    minLines: 3,
                                    maxLines: 4,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Tab metadata',
                                  child: FxTextArea(
                                    label: 'Plain Text',
                                    value:
                                        'Pasted tab characters can be preserved when needed.',
                                    helpText:
                                        'Keyboard Tab still follows focus traversal.',
                                    reserveSupportingTextSpace: true,
                                    constraints: FxTextInputConstraints(
                                      allowTab: true,
                                      maxLength: 160,
                                    ),
                                    minLines: 3,
                                    maxLines: 4,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'Decorated Input Alignment',
                            child: _StateSamples(
                              sampleWidth: 320,
                              children: [
                                const _StateSample(
                                  label: 'Text field',
                                  child: FxTextField(
                                    label: 'Reference',
                                    value: 'A-589434',
                                    helpText: 'Has supporting text.',
                                    reserveSupportingTextSpace: true,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Date/time',
                                  child: FxDateTimePicker(
                                    label: 'Due Date',
                                    value: dueDate,
                                    reserveSupportingTextSpace: true,
                                    onChanged: (value) {
                                      setState(() => dueDate = value);
                                    },
                                  ),
                                ),
                                _StateSample(
                                  label: 'Popup menu',
                                  child: FxPopupMenu(
                                    label: 'Status',
                                    selectedValue: popupStatus,
                                    options: const [
                                      'Open',
                                      'Pending',
                                      'Closed',
                                    ],
                                    reserveSupportingTextSpace: true,
                                    onChanged: (value) {
                                      setState(() => popupStatus = value);
                                    },
                                  ),
                                ),
                                _StateSample(
                                  label: 'Combo box',
                                  child: FxComboBox(
                                    label: 'City',
                                    value: comboValue,
                                    options: const [
                                      'Bangkok',
                                      'Boston',
                                      'Berlin',
                                      'Zurich',
                                    ],
                                    reserveSupportingTextSpace: true,
                                    onChanged: (value) {
                                      setState(() => comboValue = value);
                                    },
                                    onOptionSelected: (value) {
                                      setState(() => comboValue = value);
                                    },
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxTextField Phase 2.4 Depth',
                            child: _StateSamples(
                              sampleWidth: 420,
                              children: const [
                                _StateSample(
                                  label: 'Helper',
                                  child: FxTextField(
                                    label: 'Customer',
                                    hintText: 'Company or person name',
                                    helpText:
                                        'Comparable to DesktopTextField/WebTextField.',
                                    reserveSupportingTextSpace: true,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Validation',
                                  child: FxTextField(
                                    label: 'Order No',
                                    hintText: 'Required order number',
                                    errorText: 'Order number is required.',
                                    reserveSupportingTextSpace: true,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Read only',
                                  child: FxTextField(
                                    label: 'Contract',
                                    value: 'A 589434',
                                    readOnly: true,
                                    reserveSupportingTextSpace: true,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Password / icons',
                                  child: FxTextField(
                                    label: 'Password',
                                    value: 'temporary',
                                    obscureText: true,
                                    prefixIcon: Icons.lock,
                                    suffixIcon: Icons.visibility,
                                    helpText: 'Obscured single-line input.',
                                    reserveSupportingTextSpace: true,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxTextArea Phase 2.4 Depth',
                            child: _StateSamples(
                              sampleWidth: 420,
                              children: const [
                                _StateSample(
                                  label: 'Helper',
                                  child: FxTextArea(
                                    label: 'Notes',
                                    hintText: 'Enter multiple lines',
                                    helpText: 'Visible to the operations team.',
                                    reserveSupportingTextSpace: true,
                                    minLines: 3,
                                    maxLines: 4,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Validation',
                                  child: FxTextArea(
                                    label: 'Notes',
                                    hintText: 'Enter notes',
                                    errorText: 'Notes are required.',
                                    reserveSupportingTextSpace: true,
                                    minLines: 3,
                                    maxLines: 4,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Read only',
                                  child: FxTextArea(
                                    label: 'Audit',
                                    value:
                                        'Created by order workflow.\nReviewed by operations.',
                                    readOnly: true,
                                    reserveSupportingTextSpace: true,
                                    minLines: 3,
                                    maxLines: 4,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Multiline scroll',
                                  child: FxTextArea(
                                    label: 'History',
                                    value:
                                        'Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6',
                                    reserveSupportingTextSpace: true,
                                    minLines: 3,
                                    maxLines: 3,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxUndoController / FxUndoScope',
                            child: _StateSamples(
                              sampleWidth: 360,
                              children: [
                                _StateSample(
                                  label: 'Committed value changes',
                                  child: Column(
                                    crossAxisAlignment:
                                        CrossAxisAlignment.stretch,
                                    mainAxisSize: MainAxisSize.min,
                                    children: [
                                      FxCheckBox(
                                        label: 'Approved',
                                        value: undoApproved,
                                        onChanged: (value) {
                                          _commitUndoValue<bool>(
                                            label: 'Change approval',
                                            oldValue: undoApproved,
                                            newValue: value ?? false,
                                            apply: (nextValue) =>
                                                undoApproved = nextValue,
                                          );
                                        },
                                      ),
                                      const SizedBox(height: 8),
                                      FxPopupMenu(
                                        label: 'Status',
                                        options: const [
                                          'Draft',
                                          'Pending',
                                          'Approved',
                                        ],
                                        selectedValue: undoStatus,
                                        onChanged: (value) {
                                          _commitUndoValue<String?>(
                                            label: 'Change status',
                                            oldValue: undoStatus,
                                            newValue: value,
                                            apply: (nextValue) =>
                                                undoStatus = nextValue,
                                          );
                                        },
                                      ),
                                      const SizedBox(height: 10),
                                      Text(
                                        'Approved: $undoApproved, Status: $undoStatus',
                                      ),
                                    ],
                                  ),
                                ),
                                _StateSample(
                                  label: 'Text commit and slider drag end',
                                  child: Column(
                                    crossAxisAlignment:
                                        CrossAxisAlignment.stretch,
                                    mainAxisSize: MainAxisSize.min,
                                    children: [
                                      FxTextField(
                                        label: 'Customer',
                                        value: undoCustomer,
                                        helpText: 'Commits on Enter or blur.',
                                        onCommit: (value) {
                                          _commitUndoValue<String>(
                                            label: 'Change customer',
                                            oldValue: undoCustomer,
                                            newValue: value,
                                            apply: (nextValue) =>
                                                undoCustomer = nextValue,
                                          );
                                        },
                                      ),
                                      const SizedBox(height: 8),
                                      FxSlider(
                                        value: undoPriority,
                                        min: 0,
                                        max: 100,
                                        divisions: 10,
                                        valueLabel: '${undoPriority.round()}%',
                                        onChangeStart: (value) =>
                                            undoPriorityDragStart = value,
                                        onChanged: (value) {
                                          setState(() => undoPriority = value);
                                        },
                                        onChangeEnd: (value) {
                                          _commitUndoValue<double>(
                                            label: 'Change priority',
                                            oldValue: undoPriorityDragStart,
                                            newValue: value,
                                            apply: (nextValue) =>
                                                undoPriority = nextValue,
                                          );
                                        },
                                      ),
                                    ],
                                  ),
                                ),
                                _StateSample(
                                  label: 'Indexed navigation state',
                                  child: Column(
                                    crossAxisAlignment:
                                        CrossAxisAlignment.stretch,
                                    mainAxisSize: MainAxisSize.min,
                                    children: [
                                      FxTabPanel(
                                        tabs: const [
                                          'Summary',
                                          'Pricing',
                                          'Audit',
                                        ],
                                        selectedIndex: undoTabIndex,
                                        onChanged: (index) {
                                          _commitUndoValue<int>(
                                            label: 'Change undo sample tab',
                                            oldValue: undoTabIndex,
                                            newValue: index,
                                            apply: (nextValue) =>
                                                undoTabIndex = nextValue,
                                          );
                                        },
                                        children: const [
                                          _NavigationContentPanel(
                                            title: 'Summary',
                                            body: 'Undo sample summary.',
                                            accentColor: Color(0xff2563eb),
                                          ),
                                          _NavigationContentPanel(
                                            title: 'Pricing',
                                            body: 'Undo sample pricing.',
                                            accentColor: Color(0xff16a34a),
                                          ),
                                          _NavigationContentPanel(
                                            title: 'Audit',
                                            body: 'Undo sample audit log.',
                                            accentColor: Color(0xffb45309),
                                          ),
                                        ],
                                      ),
                                      const SizedBox(height: 8),
                                      _UndoCommandBar(
                                        controller: undoController,
                                      ),
                                    ],
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxButton',
                            child: _StateSamples(
                              children: [
                                _StateSample(
                                  label: 'Primary enabled',
                                  child: FxButton(
                                    label: 'Primary',
                                    icon: Icons.play_arrow,
                                    prominence: FxButtonProminence.primary,
                                    onPressed: () {},
                                  ),
                                ),
                                const _StateSample(
                                  label: 'Primary disabled',
                                  child: FxButton(
                                    label: 'Primary',
                                    icon: Icons.play_arrow,
                                    prominence: FxButtonProminence.primary,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Normal',
                                  child: FxButton(
                                    label: 'Normal',
                                    onPressed: () {},
                                  ),
                                ),
                                _StateSample(
                                  label: 'Quiet',
                                  child: FxButton(
                                    label: 'Quiet',
                                    prominence: FxButtonProminence.quiet,
                                    onPressed: () {},
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxTextField',
                            child: _StateSamples(
                              sampleWidth: 420,
                              children: const [
                                _StateSample(
                                  label: 'Helper',
                                  child: FxTextField(
                                    label: 'Customer',
                                    hintText: 'Company or person name',
                                    helpText:
                                        'Comparable to DesktopTextField/WebTextField.',
                                    reserveSupportingTextSpace: true,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Validation',
                                  child: FxTextField(
                                    label: 'Order No',
                                    hintText: 'Required order number',
                                    errorText: 'Order number is required.',
                                    reserveSupportingTextSpace: true,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Read only',
                                  child: FxTextField(
                                    label: 'Contract',
                                    value: 'A 589434',
                                    readOnly: true,
                                    reserveSupportingTextSpace: true,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Password / icons',
                                  child: FxTextField(
                                    label: 'Password',
                                    value: 'temporary',
                                    obscureText: true,
                                    prefixIcon: Icons.lock,
                                    suffixIcon: Icons.visibility,
                                    helpText: 'Obscured single-line input.',
                                    reserveSupportingTextSpace: true,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Disabled',
                                  child: FxTextField(
                                    label: 'Customer',
                                    hintText: 'Disabled field',
                                    enabled: false,
                                    reserveSupportingTextSpace: true,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxTextArea',
                            child: _StateSamples(
                              sampleWidth: 420,
                              children: const [
                                _StateSample(
                                  label: 'Helper',
                                  child: FxTextArea(
                                    label: 'Notes',
                                    hintText: 'Enter multiple lines',
                                    helpText: 'Visible to the operations team.',
                                    reserveSupportingTextSpace: true,
                                    minLines: 3,
                                    maxLines: 4,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Validation',
                                  child: FxTextArea(
                                    label: 'Notes',
                                    hintText: 'Enter notes',
                                    errorText: 'Notes are required.',
                                    reserveSupportingTextSpace: true,
                                    minLines: 3,
                                    maxLines: 4,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Read only',
                                  child: FxTextArea(
                                    label: 'Audit',
                                    value:
                                        'Created by order workflow.\nReviewed by operations.',
                                    readOnly: true,
                                    reserveSupportingTextSpace: true,
                                    minLines: 3,
                                    maxLines: 4,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Multiline scroll',
                                  child: FxTextArea(
                                    label: 'History',
                                    value:
                                        'Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6',
                                    reserveSupportingTextSpace: true,
                                    minLines: 3,
                                    maxLines: 3,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Disabled',
                                  child: FxTextArea(
                                    label: 'Notes',
                                    hintText: 'Disabled text area',
                                    reserveSupportingTextSpace: true,
                                    minLines: 3,
                                    maxLines: 4,
                                    enabled: false,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxLabel',
                            child: _StateSamples(
                              sampleWidth: 340,
                              children: const [
                                _StateSample(
                                  label: 'Enabled',
                                  child: FxLabel(
                                    text:
                                        'Order summary label with wrapping enabled.',
                                  ),
                                ),
                                _StateSample(
                                  label: 'Centered',
                                  child: FxLabel(
                                    text: 'Centered label',
                                    alignment: TextAlign.center,
                                    style: TextStyle(
                                      fontWeight: FontWeight.w700,
                                    ),
                                  ),
                                ),
                                _StateSample(
                                  label: 'Disabled',
                                  child: FxLabel(
                                    text: 'Disabled label',
                                    enabled: false,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxPopupMenu',
                            child: _StateSamples(
                              sampleWidth: 320,
                              children: [
                                _StateSample(
                                  label: 'Selected fixed choice',
                                  child: FxPopupMenu(
                                    label: 'Status',
                                    options: const [
                                      'Open',
                                      'Pending',
                                      'Closed',
                                    ],
                                    selectedValue: popupStatus,
                                    reserveSupportingTextSpace: true,
                                    onChanged: (value) {
                                      setState(() => popupStatus = value);
                                    },
                                  ),
                                ),
                                const _StateSample(
                                  label: 'Empty options',
                                  child: FxPopupMenu(
                                    label: 'Status',
                                    options: [],
                                    reserveSupportingTextSpace: true,
                                    emptyText: 'No statuses',
                                  ),
                                ),
                                const _StateSample(
                                  label: 'Disabled',
                                  child: FxPopupMenu(
                                    label: 'Status',
                                    options: ['Open', 'Pending', 'Closed'],
                                    selectedValue: 'Pending',
                                    enabled: false,
                                    reserveSupportingTextSpace: true,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxComboBox',
                            child: _StateSamples(
                              sampleWidth: 360,
                              children: [
                                _StateSample(
                                  label: 'Editable with autocomplete',
                                  child: FxComboBox(
                                    label: 'City',
                                    value: comboValue,
                                    options: const [
                                      'Bangkok',
                                      'Boston',
                                      'Berlin',
                                      'Zurich',
                                    ],
                                    reserveSupportingTextSpace: true,
                                    onChanged: (value) {
                                      setState(() => comboValue = value);
                                    },
                                    onOptionSelected: (value) {
                                      setState(() => comboValue = value);
                                    },
                                  ),
                                ),
                                const _StateSample(
                                  label: 'Disabled',
                                  child: FxComboBox(
                                    label: 'City',
                                    value: 'Zurich',
                                    options: ['Bangkok', 'Boston', 'Berlin'],
                                    enabled: false,
                                    reserveSupportingTextSpace: true,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxCheckBox',
                            child: _StateSamples(
                              sampleWidth: 220,
                              children: [
                                _StateSample(
                                  label: 'Checked',
                                  child: FxCheckBox(
                                    label: 'Active',
                                    value: active,
                                    onChanged: (value) {
                                      setState(() => active = value ?? false);
                                    },
                                  ),
                                ),
                                _StateSample(
                                  label: 'Unchecked',
                                  child: FxCheckBox(
                                    label: 'Inactive',
                                    value: inactive,
                                    onChanged: (value) {
                                      setState(() => inactive = value ?? false);
                                    },
                                  ),
                                ),
                                _StateSample(
                                  label: 'Indeterminate',
                                  child: FxCheckBox(
                                    label: 'Optional',
                                    value: optional,
                                    tristate: true,
                                    onChanged: (value) {
                                      setState(() => optional = value);
                                    },
                                  ),
                                ),
                                const _StateSample(
                                  label: 'Disabled',
                                  child: FxCheckBox(
                                    label: 'Disabled',
                                    value: true,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxRadioButton',
                            child: _StateSamples(
                              sampleWidth: 240,
                              children: [
                                _StateSample(
                                  label: 'Selected',
                                  child: FxRadioButton<String>(
                                    label: 'Standard',
                                    value: 'standard',
                                    groupValue: radioChoice,
                                    onChanged: (value) {
                                      setState(() => radioChoice = value);
                                    },
                                  ),
                                ),
                                _StateSample(
                                  label: 'Unselected',
                                  child: FxRadioButton<String>(
                                    label: 'Express',
                                    value: 'express',
                                    groupValue: radioChoice,
                                    onChanged: (value) {
                                      setState(() => radioChoice = value);
                                    },
                                  ),
                                ),
                                const _StateSample(
                                  label: 'Disabled',
                                  child: FxRadioButton<String>(
                                    label: 'Disabled',
                                    value: 'disabled',
                                    selected: false,
                                    enabled: false,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxRadioGroup',
                            child: _StateSamples(
                              sampleWidth: 420,
                              children: [
                                _StateSample(
                                  label: 'Vertical group',
                                  child: FxRadioGroup<String>(
                                    value: radioGroupChoice,
                                    options: const [
                                      FxRadioOption(
                                        value: 'email',
                                        label: 'Email',
                                      ),
                                      FxRadioOption(
                                        value: 'phone',
                                        label: 'Phone',
                                      ),
                                      FxRadioOption(
                                        value: 'letter',
                                        label: 'Letter',
                                        enabled: false,
                                      ),
                                    ],
                                    onChanged: (value) {
                                      setState(() => radioGroupChoice = value);
                                    },
                                  ),
                                ),
                                _StateSample(
                                  label: 'Horizontal group',
                                  child: FxRadioGroup<String>(
                                    value: radioGroupChoice,
                                    orientation: FxChoiceOrientation.horizontal,
                                    options: const [
                                      FxRadioOption(
                                        value: 'email',
                                        label: 'Email',
                                      ),
                                      FxRadioOption(
                                        value: 'phone',
                                        label: 'Phone',
                                      ),
                                    ],
                                    onChanged: (value) {
                                      setState(() => radioGroupChoice = value);
                                    },
                                  ),
                                ),
                                const _StateSample(
                                  label: 'Disabled group',
                                  child: FxRadioGroup<String>(
                                    value: 'email',
                                    enabled: false,
                                    options: [
                                      FxRadioOption(
                                        value: 'email',
                                        label: 'Email',
                                      ),
                                      FxRadioOption(
                                        value: 'phone',
                                        label: 'Phone',
                                      ),
                                    ],
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxDateTimePicker',
                            child: _StateSamples(
                              sampleWidth: 340,
                              children: [
                                _StateSample(
                                  label: 'Date',
                                  child: FxDateTimePicker(
                                    label: 'Due Date',
                                    value: dueDate,
                                    reserveSupportingTextSpace: true,
                                    onChanged: (value) {
                                      setState(() => dueDate = value);
                                    },
                                  ),
                                ),
                                _StateSample(
                                  label: 'Time',
                                  child: FxDateTimePicker(
                                    label: 'Reminder',
                                    mode: FxDateTimePickerMode.time,
                                    value: reminderTime,
                                    reserveSupportingTextSpace: true,
                                    onChanged: (value) {
                                      setState(() => reminderTime = value);
                                    },
                                  ),
                                ),
                                _StateSample(
                                  label: 'Date and time',
                                  child: FxDateTimePicker(
                                    label: 'Appointment',
                                    mode: FxDateTimePickerMode.dateTime,
                                    value: appointment,
                                    reserveSupportingTextSpace: true,
                                    onChanged: (value) {
                                      setState(() => appointment = value);
                                    },
                                  ),
                                ),
                                const _StateSample(
                                  label: 'Disabled',
                                  child: FxDateTimePicker(
                                    label: 'Disabled Date',
                                    value: null,
                                    enabled: false,
                                    reserveSupportingTextSpace: true,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxSlider',
                            child: _StateSamples(
                              sampleWidth: 420,
                              children: [
                                _StateSample(
                                  label: 'Range with divisions',
                                  child: FxSlider(
                                    value: priority,
                                    min: 0,
                                    max: 100,
                                    divisions: 10,
                                    valueLabel: '${priority.round()}%',
                                    onChanged: (value) {
                                      setState(() => priority = value);
                                    },
                                  ),
                                ),
                                const _StateSample(
                                  label: 'Disabled',
                                  child: FxSlider(
                                    value: 25,
                                    min: 0,
                                    max: 100,
                                    divisions: 4,
                                    valueLabel: '25%',
                                    enabled: false,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name:
                                'FxTabPanel / FxPagePanel / FxSegmentedButton + FxCardContainer',
                            child: _StateSamples(
                              sampleWidth: 340,
                              children: [
                                _StateSample(
                                  label: 'Visible tabs',
                                  child: FxTabPanel(
                                    tabs: const [
                                      'Overview',
                                      'Pricing',
                                      'History',
                                    ],
                                    selectedIndex: selectedTabIndex,
                                    onChanged: (index) {
                                      setState(() => selectedTabIndex = index);
                                    },
                                    children: const [
                                      _NavigationContentPanel(
                                        title: 'Overview',
                                        body: 'Three open orders need review.',
                                        accentColor: Color(0xff2563eb),
                                      ),
                                      _NavigationContentPanel(
                                        title: 'Pricing',
                                        body: 'Discount cap is set to 12%.',
                                        accentColor: Color(0xff16a34a),
                                      ),
                                      _NavigationContentPanel(
                                        title: 'History',
                                        body:
                                            'Last edited by Cindy on Tuesday.',
                                        accentColor: Color(0xffb45309),
                                      ),
                                    ],
                                  ),
                                ),
                                _StateSample(
                                  label: 'Headless indexed pages',
                                  child: Column(
                                    crossAxisAlignment:
                                        CrossAxisAlignment.stretch,
                                    mainAxisSize: MainAxisSize.min,
                                    children: [
                                      FxSegmentedButton<int>(
                                        value: selectedPageIndex,
                                        options: const [
                                          FxSegmentedOption(
                                            value: 0,
                                            label: 'Form',
                                          ),
                                          FxSegmentedOption(
                                            value: 1,
                                            label: 'Rows',
                                          ),
                                          FxSegmentedOption(
                                            value: 2,
                                            label: 'Notes',
                                          ),
                                        ],
                                        onChanged: (index) {
                                          setState(
                                            () => selectedPageIndex = index,
                                          );
                                        },
                                      ),
                                      const SizedBox(height: 8),
                                      FxPagePanel(
                                        selectedIndex: selectedPageIndex,
                                        children: const [
                                          _NavigationContentPanel(
                                            title: 'Form Page',
                                            body:
                                                'Customer fields are shown here.',
                                            accentColor: Color(0xff7c3aed),
                                          ),
                                          _NavigationContentPanel(
                                            title: 'Table Page',
                                            body:
                                                'Line item rows replace the form.',
                                            accentColor: Color(0xff0f766e),
                                          ),
                                          _NavigationContentPanel(
                                            title: 'Notes Page',
                                            body:
                                                'Internal comments use this page.',
                                            accentColor: Color(0xffbe123c),
                                          ),
                                        ],
                                      ),
                                    ],
                                  ),
                                ),
                                _StateSample(
                                  label: 'Segmented cards',
                                  child: Column(
                                    crossAxisAlignment:
                                        CrossAxisAlignment.stretch,
                                    mainAxisSize: MainAxisSize.min,
                                    children: [
                                      FxSegmentedButton<int>(
                                        value: selectedCardIndex,
                                        options: const [
                                          FxSegmentedOption(
                                            value: 0,
                                            label: 'Summary',
                                          ),
                                          FxSegmentedOption(
                                            value: 1,
                                            label: 'Files',
                                          ),
                                          FxSegmentedOption(
                                            value: 2,
                                            label: 'Tasks',
                                          ),
                                        ],
                                        onChanged: (index) {
                                          setState(
                                            () => selectedCardIndex = index,
                                          );
                                        },
                                      ),
                                      const SizedBox(height: 8),
                                      FxCardContainer(
                                        selectedIndex: selectedCardIndex,
                                        children: const [
                                          _NavigationContentPanel(
                                            title: 'Summary Card',
                                            body:
                                                'Current campaign is confirmed.',
                                            accentColor: Color(0xff0891b2),
                                          ),
                                          _NavigationContentPanel(
                                            title: 'Files Card',
                                            body: 'Four proofs are attached.',
                                            accentColor: Color(0xff4f46e5),
                                          ),
                                          _NavigationContentPanel(
                                            title: 'Tasks Card',
                                            body: 'Two approvals remain open.',
                                            accentColor: Color(0xffca8a04),
                                          ),
                                        ],
                                      ),
                                    ],
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxDisclosureTriangle',
                            child: _StateSamples(
                              sampleWidth: 340,
                              children: [
                                _StateSample(
                                  label: 'Expanded',
                                  child: FxDisclosureTriangle(
                                    expanded: orderExpanded,
                                    title: 'Order details',
                                    onChanged: (value) {
                                      setState(() => orderExpanded = value);
                                    },
                                    child: const _NavigationContentPanel(
                                      title: 'Visible Details',
                                      body:
                                          'Billing and delivery fields are open.',
                                      accentColor: Color(0xff2563eb),
                                    ),
                                  ),
                                ),
                                _StateSample(
                                  label: 'Collapsed',
                                  child: FxDisclosureTriangle(
                                    expanded: auditExpanded,
                                    title: 'Audit trail',
                                    onChanged: (value) {
                                      setState(() => auditExpanded = value);
                                    },
                                    child: const _NavigationContentPanel(
                                      title: 'Audit Entries',
                                      body:
                                          'Three changes are ready to inspect.',
                                      accentColor: Color(0xff16a34a),
                                    ),
                                  ),
                                ),
                                const _StateSample(
                                  label: 'Disabled',
                                  child: FxDisclosureTriangle(
                                    expanded: false,
                                    title: 'Disabled section',
                                    enabled: false,
                                    child: _NavigationContentPanel(
                                      title: 'Disabled Content',
                                      body: 'This content stays hidden.',
                                      accentColor: Color(0xff6b7280),
                                    ),
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxColorPicker',
                            child: _StateSamples(
                              sampleWidth: 300,
                              children: [
                                _StateSample(
                                  label: 'HSV / hex picker',
                                  child: FxColorPicker(
                                    label: 'Accent',
                                    value: accentColor,
                                    onChanged: (value) {
                                      setState(() => accentColor = value);
                                    },
                                  ),
                                ),
                                _StateSample(
                                  label: 'No color',
                                  child: FxColorPicker(
                                    label: 'Optional',
                                    value: optionalColor,
                                    onChanged: (value) {
                                      setState(() => optionalColor = value);
                                    },
                                  ),
                                ),
                                const _StateSample(
                                  label: 'Disabled',
                                  child: FxColorPicker(
                                    label: 'Disabled',
                                    value: Color(0xff64748b),
                                    enabled: false,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxProgressBar / FxProgressWheel',
                            child: _StateSamples(
                              sampleWidth: 300,
                              children: const [
                                _StateSample(
                                  label: 'Minimum',
                                  child: FxProgressBar(value: 0),
                                ),
                                _StateSample(
                                  label: 'Partial',
                                  child: FxProgressBar(value: 62),
                                ),
                                _StateSample(
                                  label: 'Complete',
                                  child: FxProgressBar(value: 100),
                                ),
                                _StateSample(
                                  label: 'Disabled progress',
                                  child: FxProgressBar(
                                    value: 40,
                                    enabled: false,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Loading wheel',
                                  child: FxProgressWheel(size: 28),
                                ),
                                _StateSample(
                                  label: 'Disabled wheel',
                                  child: FxProgressWheel(
                                    enabled: false,
                                    size: 28,
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxSeparator',
                            child: _StateSamples(
                              sampleWidth: 360,
                              children: const [
                                _StateSample(
                                  label: 'Horizontal rule',
                                  child: _SeparatorHorizontalSample(),
                                ),
                                _StateSample(
                                  label: 'Vertical rule',
                                  child: _SeparatorVerticalSample(),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxStyledLabel',
                            child: _StateSamples(
                              sampleWidth: 420,
                              children: const [
                                _StateSample(
                                  label: 'Rich label',
                                  child: FxStyledLabel(
                                    text: 'Status: priority order is overdue',
                                    spans: [
                                      FxStyledTextSpan(text: 'Status: '),
                                      FxStyledTextSpan(
                                        text: 'priority order',
                                        style: TextStyle(
                                          color: Color(0xffbe123c),
                                          fontWeight: FontWeight.w700,
                                        ),
                                      ),
                                      FxStyledTextSpan(text: ' is overdue'),
                                    ],
                                  ),
                                ),
                                _StateSample(
                                  label: 'Wrapped help text',
                                  child: FxStyledLabel(
                                    text:
                                        'Use styled labels for compact help text with emphasized terms.',
                                    spans: [
                                      FxStyledTextSpan(
                                        text:
                                            'Use styled labels for compact help text with ',
                                      ),
                                      FxStyledTextSpan(
                                        text: 'emphasized terms.',
                                        style: TextStyle(
                                          fontStyle: FontStyle.italic,
                                          color: Color(0xff0f766e),
                                        ),
                                      ),
                                    ],
                                    softWrap: true,
                                  ),
                                ),
                                _StateSample(
                                  label: 'Disabled',
                                  child: FxStyledLabel(
                                    text: 'Disabled styled label',
                                    enabled: false,
                                    spans: [
                                      FxStyledTextSpan(text: 'Disabled '),
                                      FxStyledTextSpan(
                                        text: 'styled label',
                                        style: TextStyle(
                                          fontWeight: FontWeight.w700,
                                        ),
                                      ),
                                    ],
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxGroupBox',
                            child: _StateSamples(
                              sampleWidth: 420,
                              children: [
                                _StateSample(
                                  label: 'Enabled child controls',
                                  child: FxGroupBox(
                                    title: 'Order Options',
                                    child: Row(
                                      children: [
                                        FxButton(
                                          label: 'Confirm',
                                          onPressed: () {},
                                        ),
                                        const SizedBox(width: 8),
                                        FxButton(
                                          label: 'Cancel',
                                          prominence: FxButtonProminence.quiet,
                                          onPressed: () {},
                                        ),
                                      ],
                                    ),
                                  ),
                                ),
                                const _StateSample(
                                  label: 'Disabled child controls',
                                  child: FxGroupBox(
                                    title: 'Disabled Options',
                                    child: Row(
                                      children: [
                                        FxButton(label: 'Confirm'),
                                        SizedBox(width: 8),
                                        FxButton(
                                          label: 'Cancel',
                                          prominence: FxButtonProminence.quiet,
                                        ),
                                      ],
                                    ),
                                  ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxFlexLayout',
                            child: SizedBox(
                              height: 74,
                              child: FxFlexLayout(
                                align: FxAlignItems.center,
                                gap: 10,
                                padding: const EdgeInsets.all(10),
                                children: [
                                  const FxFlexItem(
                                    width: 150,
                                    height: 44,
                                    child: _DemoBox('Fixed width'),
                                  ),
                                  const FxFlexItem(
                                    grow: 1,
                                    height: 44,
                                    child: _DemoBox('Grow = 1'),
                                  ),
                                  const FxFlexItem(
                                    width: 130,
                                    height: 44,
                                    child: _DemoBox('Fixed'),
                                  ),
                                ],
                              ),
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxGridLayout',
                            child: SizedBox(
                              height: 120,
                              child: FxGridLayout(
                                columns: const [
                                  FxTrackSize.fixed(160),
                                  FxTrackSize.flex(1),
                                ],
                                rows: const [
                                  FxTrackSize.fixed(44),
                                  FxTrackSize.fixed(58),
                                ],
                                rowGap: 8,
                                columnGap: 8,
                                areas: '''
label field
label action
''',
                                children: [
                                  const FxGridArea(
                                    'label',
                                  ).containing(_DemoBox('Named area')),
                                  const FxGridArea('field').containing(
                                    FxTextField(label: 'Grid field'),
                                  ),
                                  FxGridArea('action').containing(
                                    Align(
                                      alignment: Alignment.centerLeft,
                                      child: FxButton(
                                        label: 'Grid Action',
                                        onPressed: () {},
                                      ),
                                    ),
                                  ),
                                ],
                              ),
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxListBox',
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.stretch,
                              children: [
                                FxListBox(
                                  height: 180,
                                  selectedRowIds: selectedRowIds,
                                  onSelectionChanged: (rowIds) {
                                    setState(() => selectedRowIds = rowIds);
                                  },
                                  sortedColumnId: sortedListBoxColumnId,
                                  sortAscending: sortedListBoxAscending,
                                  onSortChanged: (columnId, ascending) {
                                    setState(() {
                                      sortedListBoxColumnId = columnId;
                                      sortedListBoxAscending = ascending;
                                      listBoxRows.sort((a, b) {
                                        final valA =
                                            a.cells[columnId]?.toString() ?? '';
                                        final valB =
                                            b.cells[columnId]?.toString() ?? '';
                                        final cmp = valA.compareTo(valB);
                                        return ascending ? cmp : -cmp;
                                      });
                                    });
                                  },
                                  validationErrors: listBoxValidationErrors,
                                  onCellEdited: (rowId, columnId, newValue) {
                                    setState(() {
                                      final rowIndex = listBoxRows.indexWhere(
                                        (r) => r.id == rowId,
                                      );
                                      if (rowIndex != -1) {
                                        final oldRow = listBoxRows[rowIndex];
                                        final nextCells =
                                            Map<String, Object?>.from(
                                              oldRow.cells,
                                            );
                                        nextCells[columnId] = newValue;
                                        listBoxRows[rowIndex] = FxListBoxRow(
                                          id: oldRow.id,
                                          cells: nextCells,
                                          enabled: oldRow.enabled,
                                          height: oldRow.height,
                                          rowTag: oldRow.rowTag,
                                        );

                                        // Dynamic validation
                                        final errors =
                                            Map<
                                              String,
                                              Map<String, String>
                                            >.from(listBoxValidationErrors);
                                        if (columnId == 'customer') {
                                          final strVal =
                                              newValue?.toString() ?? '';
                                          if (strVal.isEmpty) {
                                            errors.putIfAbsent(
                                                  rowId,
                                                  () => {},
                                                )[columnId] =
                                                'Customer name cannot be empty';
                                          } else if (strVal.length < 3) {
                                            errors.putIfAbsent(
                                                  rowId,
                                                  () => {},
                                                )[columnId] =
                                                'Must be at least 3 characters';
                                          } else {
                                            errors[rowId]?.remove(columnId);
                                          }
                                        }
                                        listBoxValidationErrors = errors;
                                      }
                                    });
                                  },
                                  columns: const [
                                    FxListBoxColumn(
                                      id: 'number',
                                      caption: 'Order',
                                      width: FxColumnWidth.fixed(110),
                                      sortable: true,
                                      editable: true,
                                    ),
                                    FxListBoxColumn(
                                      id: 'customer',
                                      caption: 'Customer',
                                      width: FxColumnWidth.fixed(180),
                                      sortable: true,
                                      editable: true,
                                    ),
                                    FxListBoxColumn(
                                      id: 'status',
                                      caption: 'Status',
                                      width: FxColumnWidth.fixed(130),
                                      sortable: true,
                                      editable: true,
                                      type: FxCellType.choice([
                                        'Draft',
                                        'Open',
                                        'Confirmed',
                                        'Closed',
                                      ]),
                                    ),
                                    FxListBoxColumn(
                                      id: 'approved',
                                      caption: 'Approved',
                                      width: FxColumnWidth.fixed(100),
                                      sortable: true,
                                      editable: true,
                                      type: FxCellType.boolean(),
                                    ),
                                  ],
                                  rows: listBoxRows,
                                ),
                                const SizedBox(height: 8),
                                Text(
                                  'Tip: Use double-click or Enter to edit cells. Use Ctrl+C/Cmd+C to copy selected rows, and Ctrl+V/Cmd+V to paste TSV data. Edits are tracked in the Undo history.',
                                  style: Theme.of(context).textTheme.bodySmall
                                      ?.copyWith(
                                        color: Theme.of(
                                          context,
                                        ).colorScheme.onSurfaceVariant,
                                        fontStyle: FontStyle.italic,
                                      ),
                                ),
                              ],
                            ),
                          ),
                          _ComponentRow(
                            name: 'FxGrid',
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.stretch,
                              children: [
                                FxGrid(
                                  height: 180,
                                  selectedCells: selectedCells,
                                  selectedRange: selectedRange,
                                  selectionMode: FxGridSelectionMode.range,
                                  onCellsSelected: (cells) {
                                    setState(() {
                                      selectedCells = cells;
                                    });
                                  },
                                  onRangeSelected: (range) {
                                    setState(() {
                                      selectedRange = range;
                                    });
                                  },
                                  sortedColumnId: sortedGridColumnId,
                                  sortAscending: sortedGridAscending,
                                  onSortChanged: (columnId, ascending) {
                                    setState(() {
                                      sortedGridColumnId = columnId;
                                      sortedGridAscending = ascending;
                                      gridRows.sort((a, b) {
                                        final valA =
                                            a.cells[columnId]?.toString() ?? '';
                                        final valB =
                                            b.cells[columnId]?.toString() ?? '';
                                        final cmp = valA.compareTo(valB);
                                        return ascending ? cmp : -cmp;
                                      });
                                    });
                                  },
                                  validationErrors: gridValidationErrors,
                                  onCellEdited: (rowId, columnId, newValue) {
                                    setState(() {
                                      final rowIndex = gridRows.indexWhere(
                                        (r) => r.id == rowId,
                                      );
                                      if (rowIndex != -1) {
                                        final oldRow = gridRows[rowIndex];
                                        final nextCells =
                                            Map<String, Object?>.from(
                                              oldRow.cells,
                                            );
                                        nextCells[columnId] = newValue;
                                        gridRows[rowIndex] = FxGridRow(
                                          id: oldRow.id,
                                          cells: nextCells,
                                          enabled: oldRow.enabled,
                                          height: oldRow.height,
                                          rowTag: oldRow.rowTag,
                                          cellTags: oldRow.cellTags,
                                        );

                                        // Dynamic validation
                                        final errors =
                                            Map<
                                              String,
                                              Map<String, String>
                                            >.from(gridValidationErrors);
                                        if (columnId == 'desktop' ||
                                            columnId == 'web') {
                                          final strVal =
                                              newValue?.toString() ?? '';
                                          if (strVal.isEmpty) {
                                            errors.putIfAbsent(
                                                  rowId,
                                                  () => {},
                                                )[columnId] =
                                                'Value cannot be empty';
                                          } else {
                                            errors[rowId]?.remove(columnId);
                                          }
                                        }
                                        gridValidationErrors = errors;
                                      }
                                    });
                                  },
                                  columns: const [
                                    FxGridColumn(
                                      id: 'field',
                                      caption: 'Field',
                                      width: FxColumnWidth.fixed(140),
                                      sortable: true,
                                    ),
                                    FxGridColumn(
                                      id: 'desktop',
                                      caption: 'Xojo Desktop',
                                      width: FxColumnWidth.fixed(230),
                                      sortable: true,
                                      editable: true,
                                    ),
                                    FxGridColumn(
                                      id: 'web',
                                      caption: 'Xojo Web',
                                      width: FxColumnWidth.fixed(190),
                                      sortable: true,
                                      editable: true,
                                    ),
                                    FxGridColumn(
                                      id: 'enabled',
                                      caption: 'Enabled',
                                      width: FxColumnWidth.fixed(100),
                                      sortable: true,
                                      editable: true,
                                      type: FxCellType.boolean(),
                                    ),
                                  ],
                                  rows: gridRows,
                                ),
                                const SizedBox(height: 8),
                                Text(
                                  'Tip: Click and drag to select a range of cells. Use Ctrl+C/Cmd+C to copy selected cells, and Ctrl+V/Cmd+V to paste TSV data. Edits are tracked in the Undo history.',
                                  style: Theme.of(context).textTheme.bodySmall
                                      ?.copyWith(
                                        color: Theme.of(
                                          context,
                                        ).colorScheme.onSurfaceVariant,
                                        fontStyle: FontStyle.italic,
                                      ),
                                ),
                              ],
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                ),
              );
            },
          ),
        ),
      ),
    );
  }
}

class _StateSamples extends StatelessWidget {
  const _StateSamples({required this.children, this.sampleWidth});

  final List<Widget> children;
  final double? sampleWidth;

  @override
  Widget build(BuildContext context) {
    return Wrap(
      spacing: 16,
      runSpacing: 14,
      crossAxisAlignment: WrapCrossAlignment.start,
      children: [
        for (final child in children)
          SizedBox(width: sampleWidth, child: child),
      ],
    );
  }
}

class _StateSample extends StatelessWidget {
  const _StateSample({required this.label, required this.child});

  final String label;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      mainAxisSize: MainAxisSize.min,
      children: [
        Text(
          label,
          style: Theme.of(context).textTheme.labelMedium?.copyWith(
            color: Theme.of(context).colorScheme.onSurfaceVariant,
            fontWeight: FontWeight.w600,
          ),
        ),
        const SizedBox(height: 8),
        child,
      ],
    );
  }
}

class _ComponentRow extends StatelessWidget {
  const _ComponentRow({required this.name, required this.child});

  final String name;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return Container(
      margin: const EdgeInsets.only(bottom: 12),
      padding: const EdgeInsets.all(14),
      decoration: BoxDecoration(
        color: Theme.of(context).colorScheme.surface,
        border: Border.all(color: const Color(0xffd9dde5)),
        borderRadius: BorderRadius.circular(6),
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          Text(
            name,
            style: Theme.of(
              context,
            ).textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w700),
          ),
          const SizedBox(height: 10),
          child,
        ],
      ),
    );
  }
}

class _UndoCommandBar extends StatelessWidget {
  const _UndoCommandBar({required this.controller});

  final FxUndoController controller;

  @override
  Widget build(BuildContext context) {
    return ListenableBuilder(
      listenable: controller,
      builder: (context, _) {
        return Wrap(
          spacing: 8,
          runSpacing: 8,
          crossAxisAlignment: WrapCrossAlignment.center,
          children: [
            FxButton(
              label: controller.undoLabel == null
                  ? 'Undo'
                  : 'Undo ${controller.undoLabel}',
              icon: Icons.undo,
              onPressed: controller.canUndo ? controller.undo : null,
            ),
            FxButton(
              label: controller.redoLabel == null
                  ? 'Redo'
                  : 'Redo ${controller.redoLabel}',
              icon: Icons.redo,
              onPressed: controller.canRedo ? controller.redo : null,
            ),
            Text(
              'Depth: ${controller.undoDepth} undo / '
              '${controller.redoDepth} redo',
              style: Theme.of(context).textTheme.bodySmall,
            ),
          ],
        );
      },
    );
  }
}

class _DemoBox extends StatelessWidget {
  const _DemoBox(this.text);

  final String text;

  @override
  Widget build(BuildContext context) {
    return DecoratedBox(
      decoration: BoxDecoration(
        color: const Color(0xffeef2ff),
        border: Border.all(color: const Color(0xffc7d2fe)),
        borderRadius: BorderRadius.circular(4),
      ),
      child: Center(
        child: Text(text, style: const TextStyle(fontWeight: FontWeight.w600)),
      ),
    );
  }
}

class _NavigationContentPanel extends StatelessWidget {
  const _NavigationContentPanel({
    required this.title,
    required this.body,
    required this.accentColor,
  });

  final String title;
  final String body;
  final Color accentColor;

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);

    return Container(
      constraints: const BoxConstraints(minHeight: 96),
      padding: const EdgeInsets.all(12),
      decoration: BoxDecoration(
        color: accentColor.withValues(alpha: 0.08),
        border: Border.all(color: accentColor.withValues(alpha: 0.42)),
        borderRadius: BorderRadius.circular(4),
      ),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            width: 4,
            height: 46,
            decoration: BoxDecoration(
              color: accentColor,
              borderRadius: BorderRadius.circular(2),
            ),
          ),
          const SizedBox(width: 10),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              mainAxisSize: MainAxisSize.min,
              children: [
                Text(
                  title,
                  style: theme.textTheme.labelLarge?.copyWith(
                    fontWeight: FontWeight.w700,
                  ),
                ),
                const SizedBox(height: 4),
                Text(body, style: theme.textTheme.bodySmall),
              ],
            ),
          ),
        ],
      ),
    );
  }
}

class _SeparatorHorizontalSample extends StatelessWidget {
  const _SeparatorHorizontalSample();

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 86,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        mainAxisAlignment: MainAxisAlignment.center,
        children: const [
          Text('Billing'),
          SizedBox(height: 10),
          FxSeparator(),
          SizedBox(height: 10),
          Text('Delivery'),
        ],
      ),
    );
  }
}

class _SeparatorVerticalSample extends StatelessWidget {
  const _SeparatorVerticalSample();

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 86,
      child: Row(
        children: const [
          Expanded(child: Text('Left pane')),
          SizedBox(width: 12),
          FxSeparator(orientation: FxSeparatorOrientation.vertical),
          SizedBox(width: 12),
          Expanded(child: Text('Right pane')),
        ],
      ),
    );
  }
}
0
likes
0
points
30
downloads

Publisher

unverified uploader

Weekly Downloads

Desktop-first Flutter components and CSS-like layout contracts for Xojo-style UI design.

Repository (GitHub)
View/report issues

Topics

#desktop #flutter #layout #xojo

License

unknown (license)

Dependencies

flexiblebox, flutter, flutter_layout_grid, two_dimensional_scrollables

More

Packages that depend on fx_desktop