vroxaldesign 1.0.1 copy "vroxaldesign: ^1.0.1" to clipboard
vroxaldesign: ^1.0.1 copied to clipboard

Vroxal Design System for Flutter — design tokens (color, typography, spacing), a 1,598-glyph custom icon library (VdIcons), and 21 production-ready UI components with automatic light/dark mode support.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:vroxaldesign/vroxaldesign.dart';

void main() => runApp(const ShowcaseApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Vroxal Design Showcase',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(brightness: Brightness.light),
      darkTheme: ThemeData(brightness: Brightness.dark),
      themeMode: ThemeMode.system,
      home: const _RootNav(),
    );
  }
}

// ── Root navigation ───────────────────────────────────────────

class _RootNav extends StatefulWidget {
  const _RootNav();

  @override
  State<_RootNav> createState() => _RootNavState();
}

class _RootNavState extends State<_RootNav> {
  int _index = 0;

  static const _tabs = [
    (label: 'Actions', icon: Icons.touch_app),
    (label: 'Displays', icon: Icons.label),
    (label: 'Feedback', icon: Icons.notifications),
    (label: 'Forms', icon: Icons.edit_note),
    (label: 'Phase 3', icon: Icons.new_releases),
  ];

  static const _pages = <Widget>[
    _Phase1Screen(),
    _Phase2DisplaysScreen(),
    _Phase2FeedbacksScreen(),
    _Phase2FormsScreen(),
    _Phase3Screen(),
  ];

  @override
  Widget build(BuildContext context) {
    final scheme = VdColorScheme.of(context);
    return Scaffold(
      backgroundColor: scheme.backgroundDefaultBase,
      body: _pages[_index],
      bottomNavigationBar: NavigationBar(
        selectedIndex: _index,
        onDestinationSelected: (i) => setState(() => _index = i),
        destinations: [
          for (final t in _tabs)
            NavigationDestination(icon: Icon(t.icon), label: t.label),
        ],
      ),
    );
  }
}

// ── Shared helpers ────────────────────────────────────────────

Widget _section(BuildContext context, String title) {
  return Padding(
    padding: const EdgeInsets.only(top: VdSpacing.s800, bottom: VdSpacing.s400),
    child: Text(
      title,
      style: VdFont.labelSmall.textStyle.copyWith(
        color: VdColorScheme.of(context).contentDefaultTertiary,
      ),
    ),
  );
}

Widget _row(List<Widget> children) {
  return Wrap(
    spacing: VdSpacing.s300,
    runSpacing: VdSpacing.s300,
    crossAxisAlignment: WrapCrossAlignment.center,
    children: children,
  );
}

AppBar _appBar(BuildContext context, String title) {
  final scheme = VdColorScheme.of(context);
  return AppBar(
    backgroundColor: scheme.backgroundDefaultBase,
    elevation: 0,
    title: Text(
      title,
      style: VdFont.titleMedium.textStyle.copyWith(color: scheme.contentDefaultBase),
    ),
  );
}

// ─────────────────────────────────────────────────────────────
// Phase 1 — Actions
// ─────────────────────────────────────────────────────────────

class _Phase1Screen extends StatefulWidget {
  const _Phase1Screen();

  @override
  State<_Phase1Screen> createState() => _Phase1ScreenState();
}

class _Phase1ScreenState extends State<_Phase1Screen> {
  bool _chipSelected = false;
  bool _loading = false;

  @override
  Widget build(BuildContext context) {
    final scheme = VdColorScheme.of(context);

    return Scaffold(
      backgroundColor: scheme.backgroundDefaultBase,
      appBar: _appBar(context, 'Phase 1 — Actions'),
      body: ListView(
        padding: const EdgeInsets.all(VdSpacing.s600),
        children: [
          _section(context, 'VdButton — Styles'),
          _row([
            VdButton('Solid', onPressed: () {}),
            VdButton('Subtle', style: VdButtonStyle.subtle, onPressed: () {}),
            VdButton('Outlined', style: VdButtonStyle.outlined, onPressed: () {}),
            VdButton('Ghost', style: VdButtonStyle.transparent, onPressed: () {}),
          ]),
          _section(context, 'VdButton — Colors'),
          _row([
            VdButton('Primary', color: VdButtonColor.primary, onPressed: () {}),
            VdButton('Neutral', color: VdButtonColor.neutral, onPressed: () {}),
            VdButton('Error', color: VdButtonColor.error, onPressed: () {}),
            VdButton('Success', color: VdButtonColor.success, onPressed: () {}),
            VdButton('Warning', color: VdButtonColor.warning, onPressed: () {}),
            VdButton('Info', color: VdButtonColor.info, onPressed: () {}),
          ]),
          _section(context, 'VdButton — Sizes'),
          _row([
            VdButton('Small', size: VdButtonSize.small, onPressed: () {}),
            VdButton('Medium', onPressed: () {}),
            VdButton('Large', size: VdButtonSize.large, onPressed: () {}),
          ]),
          _section(context, 'VdButton — Modifiers'),
          _row([
            VdButton('Rounded', rounded: true, onPressed: () {}),
            VdButton('With Icon', leftIcon: const Icon(Icons.add), onPressed: () {}),
            VdButton(
              _loading ? 'Loading' : 'Toggle Load',
              isLoading: _loading,
              onPressed: () => setState(() => _loading = !_loading),
            ),
            VdButton('Disabled', isDisabled: true, onPressed: () {}),
          ]),
          _section(context, 'VdButton — Full width'),
          VdButton('Full Width', fullWidth: true, onPressed: () {}),
          const SizedBox(height: VdSpacing.s200),
          VdButton(
            'Full Width Outlined',
            fullWidth: true,
            style: VdButtonStyle.outlined,
            onPressed: () {},
          ),
          _section(context, 'VdIconButton — Styles'),
          _row([
            VdIconButton(icon: const Icon(Icons.star), onPressed: () {}),
            VdIconButton(icon: const Icon(Icons.star), style: VdIconButtonStyle.subtle, onPressed: () {}),
            VdIconButton(icon: const Icon(Icons.star), style: VdIconButtonStyle.outlined, onPressed: () {}),
            VdIconButton(icon: const Icon(Icons.star), style: VdIconButtonStyle.transparent, onPressed: () {}),
          ]),
          _section(context, 'VdIconButton — Sizes & Rounded'),
          _row([
            VdIconButton(icon: const Icon(Icons.add), size: VdIconButtonSize.small, onPressed: () {}),
            VdIconButton(icon: const Icon(Icons.add), onPressed: () {}),
            VdIconButton(icon: const Icon(Icons.add), size: VdIconButtonSize.large, onPressed: () {}),
            VdIconButton(icon: const Icon(Icons.add), rounded: true, onPressed: () {}),
          ]),
          _section(context, 'VdChip'),
          _row([
            VdChip('Default', onTap: () {}),
            VdChip(
              _chipSelected ? 'Selected' : 'Tap to select',
              isSelected: _chipSelected,
              onTap: () => setState(() => _chipSelected = !_chipSelected),
            ),
            VdChip('Closable', closable: true, onTap: () {}, onClose: () {}),
            VdChip('With Icon', icon: const Icon(Icons.label), onTap: () {}),
            VdChip('Disabled', isDisabled: true, onTap: () {}),
          ]),
          const SizedBox(height: VdSpacing.s1600),
        ],
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────
// Phase 2 — Displays
// ─────────────────────────────────────────────────────────────

class _Phase2DisplaysScreen extends StatelessWidget {
  const _Phase2DisplaysScreen();

  @override
  Widget build(BuildContext context) {
    final scheme = VdColorScheme.of(context);

    return Scaffold(
      backgroundColor: scheme.backgroundDefaultBase,
      appBar: _appBar(context, 'Phase 2 — Displays'),
      body: ListView(
        padding: const EdgeInsets.all(VdSpacing.s600),
        children: [
          _section(context, 'VdBadge — Colors (solid)'),
          _row([
            VdBadge('Primary', color: VdBadgeColor.primary),
            VdBadge('Neutral', color: VdBadgeColor.neutral),
            VdBadge('Error', color: VdBadgeColor.error),
            VdBadge('Success', color: VdBadgeColor.success),
            VdBadge('Warning', color: VdBadgeColor.warning),
            VdBadge('Info', color: VdBadgeColor.info),
          ]),
          _section(context, 'VdBadge — Styles'),
          _row([
            VdBadge('Solid', style: VdBadgeStyle.solid),
            VdBadge('Subtle', style: VdBadgeStyle.subtle),
          ]),
          _section(context, 'VdBadge — Sizes & Rounded'),
          _row([
            VdBadge('Small', size: VdBadgeSize.small),
            VdBadge('Medium'),
            VdBadge('Rounded SM', size: VdBadgeSize.small, rounded: true),
            VdBadge('Rounded MD', rounded: true),
          ]),
          _section(context, 'VdDivider — Horizontal'),
          const VdDivider(),
          const SizedBox(height: VdSpacing.s200),
          const VdDivider(label: 'Center'),
          const SizedBox(height: VdSpacing.s200),
          const VdDivider(label: 'Leading', labelAlignment: VdDividerLabelAlignment.leading),
          const SizedBox(height: VdSpacing.s200),
          const VdDivider(label: 'Trailing', labelAlignment: VdDividerLabelAlignment.trailing),
          _section(context, 'VdDivider — Colors'),
          _row([
            const SizedBox(width: 200, child: VdDivider(color: VdDividerColor.defaultColor)),
            const SizedBox(width: 200, child: VdDivider(color: VdDividerColor.primary)),
            const SizedBox(width: 200, child: VdDivider(color: VdDividerColor.neutral)),
          ]),
          const SizedBox(height: VdSpacing.s1600),
        ],
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────
// Phase 2 — Feedbacks
// ─────────────────────────────────────────────────────────────

class _Phase2FeedbacksScreen extends StatefulWidget {
  const _Phase2FeedbacksScreen();

  @override
  State<_Phase2FeedbacksScreen> createState() => _Phase2FeedbacksScreenState();
}

class _Phase2FeedbacksScreenState extends State<_Phase2FeedbacksScreen> {
  bool _showOverlay = false;

  @override
  Widget build(BuildContext context) {
    final scheme = VdColorScheme.of(context);

    return Scaffold(
      backgroundColor: scheme.backgroundDefaultBase,
      appBar: _appBar(context, 'Phase 2 — Feedbacks'),
      body: Stack(
        children: [
          ListView(
            padding: const EdgeInsets.all(VdSpacing.s600),
            children: [
              _section(context, 'VdAlert — Colors'),
              VdAlert(description: 'Primary alert', color: VdAlertColor.primary),
              const SizedBox(height: VdSpacing.s200),
              VdAlert(description: 'Success alert', color: VdAlertColor.success),
              const SizedBox(height: VdSpacing.s200),
              VdAlert(description: 'Error alert', color: VdAlertColor.error),
              const SizedBox(height: VdSpacing.s200),
              VdAlert(description: 'Warning alert', color: VdAlertColor.warning),
              const SizedBox(height: VdSpacing.s200),
              VdAlert(description: 'Neutral alert', color: VdAlertColor.neutral),
              _section(context, 'VdAlert — With content'),
              VdAlert(
                title: 'Update available',
                description: 'A new version of the app is ready to install.',
                color: VdAlertColor.info,
                action: 'Update now',
                closable: true,
                onAction: () {},
                onClose: () {},
              ),
              _section(context, 'VdEmptyState'),
              VdEmptyState(
                icon: const Icon(Icons.inbox),
                title: 'No messages yet',
                description: 'Your inbox is empty. Start a conversation to see messages here.',
                primaryActionTitle: 'New message',
                onPrimaryAction: () {},
              ),
              _section(context, 'VdLoadingState — Inline'),
              const VdLoadingState(title: 'Loading data…', description: 'Please wait a moment.'),
              _section(context, 'VdLoadingState — Overlay (tap button)'),
              VdButton(
                _showOverlay ? 'Hide overlay' : 'Show overlay',
                onPressed: () => setState(() => _showOverlay = !_showOverlay),
              ),
              _section(context, 'VdSkeleton shimmer'),
              Container(height: 20, width: 200, color: Colors.transparent)
                  .vdSkeleton(),
              const SizedBox(height: VdSpacing.s200),
              const Text('Name').vdSkeleton(),
              const SizedBox(height: VdSpacing.s1600),
            ],
          ),
          if (_showOverlay)
            VdLoadingState(
              style: VdLoadingStyle.overlay,
              title: 'Saving…',
              description: 'Do not close the app.',
            ),
        ],
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────
// Phase 2 — Forms
// ─────────────────────────────────────────────────────────────

class _Phase2FormsScreen extends StatefulWidget {
  const _Phase2FormsScreen();

  @override
  State<_Phase2FormsScreen> createState() => _Phase2FormsScreenState();
}

class _Phase2FormsScreenState extends State<_Phase2FormsScreen> {
  final _textCtrl = TextEditingController();
  final _textErrorCtrl = TextEditingController(text: 'Bad input');
  final _textSuccessCtrl = TextEditingController(text: 'All good');
  final _textDisabledCtrl = TextEditingController(text: 'Disabled');
  final _textIconCtrl = TextEditingController();
  final _areaCtrl = TextEditingController();
  final _areaErrorCtrl = TextEditingController();
  bool _checked = false;
  bool _indeterminate = false;
  String _radioPick = 'a';
  String _cardPick = 'x';
  bool _cardChecked = false;
  DateTime? _dateTime;

  @override
  void dispose() {
    _textCtrl.dispose();
    _textErrorCtrl.dispose();
    _textSuccessCtrl.dispose();
    _textDisabledCtrl.dispose();
    _textIconCtrl.dispose();
    _areaCtrl.dispose();
    _areaErrorCtrl.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final scheme = VdColorScheme.of(context);

    return Scaffold(
      backgroundColor: scheme.backgroundDefaultBase,
      appBar: _appBar(context, 'Phase 2 — Forms'),
      body: ListView(
        padding: const EdgeInsets.all(VdSpacing.s600),
        children: [
          _section(context, 'VdTextField — States'),
          VdTextField('Normal', controller: _textCtrl, placeholder: 'Enter text'),
          const SizedBox(height: VdSpacing.s400),
          VdTextField('Error', controller: _textErrorCtrl,
              state: VdInputState.error, helperText: 'Something went wrong'),
          const SizedBox(height: VdSpacing.s400),
          VdTextField('Success', controller: _textSuccessCtrl,
              state: VdInputState.success, helperText: 'Looks great'),
          const SizedBox(height: VdSpacing.s400),
          VdTextField('Disabled', controller: _textDisabledCtrl,
              state: VdInputState.disabled),
          const SizedBox(height: VdSpacing.s400),
          VdTextField('With icon',
              controller: _textIconCtrl,
              leadingIcon: const Icon(Icons.search),
              placeholder: 'Search…'),
          const SizedBox(height: VdSpacing.s400),
          VdTextField('With limit',
              controller: _textCtrl,
              characterLimit: 100,
              helperText: 'Optional field',
              isOptional: true),

          _section(context, 'VdTextArea'),
          VdTextArea(
            controller: _areaCtrl,
            label: 'Notes',
            placeholder: 'Enter your notes here…',
            helperText: 'Up to 240pt of text',
            characterLimit: 300,
          ),
          const SizedBox(height: VdSpacing.s400),
          VdTextArea(
            controller: _areaErrorCtrl,
            label: 'Error',
            state: VdInputState.error,
            helperText: 'Please fill in this field',
          ),

          _section(context, 'VdCheckbox'),
          VdCheckbox(
            value: _checked,
            label: 'Accept terms',
            description: 'You agree to our terms of service.',
            onChanged: (v) => setState(() => _checked = v),
          ),
          const SizedBox(height: VdSpacing.s300),
          VdCheckbox(
            value: true,
            isIndeterminate: _indeterminate,
            label: _indeterminate ? 'Indeterminate' : 'Checked',
            onChanged: (_) => setState(() => _indeterminate = !_indeterminate),
          ),
          const SizedBox(height: VdSpacing.s300),
          const VdCheckbox(value: false, label: 'Disabled unchecked', isDisabled: true),
          const SizedBox(height: VdSpacing.s300),
          const VdCheckbox(value: true, label: 'Disabled checked', isDisabled: true),

          _section(context, 'VdRadioButton — Standalone'),
          VdRadioButton(
            isSelected: _radioPick == 'a',
            label: 'Option A',
            onChanged: (_) => setState(() => _radioPick = 'a'),
          ),
          const SizedBox(height: VdSpacing.s300),
          VdRadioButton(
            isSelected: _radioPick == 'b',
            label: 'Option B',
            description: 'Some extra detail',
            onChanged: (_) => setState(() => _radioPick = 'b'),
          ),
          const SizedBox(height: VdSpacing.s300),
          const VdRadioButton(isSelected: false, label: 'Disabled', isDisabled: true),

          _section(context, 'VdRadioGroup'),
          VdRadioGroup<String>(
            value: _radioPick,
            onChanged: (v) => setState(() => _radioPick = v),
            child: Column(
              children: const [
                VdRadioOption(value: 'a', label: 'Option A'),
                SizedBox(height: VdSpacing.s300),
                VdRadioOption(value: 'b', label: 'Option B'),
                SizedBox(height: VdSpacing.s300),
                VdRadioOption(value: 'c', label: 'Option C (disabled)', isDisabled: true),
              ],
            ),
          ),

          _section(context, 'VdSelectionCard — Checkbox'),
          VdSelectionCard(
            isSelected: _cardChecked,
            title: 'Enable notifications',
            description: 'Receive alerts for important updates.',
            icon: const Icon(Icons.notifications),
            onChanged: (v) => setState(() => _cardChecked = v),
          ),
          const SizedBox(height: VdSpacing.s300),
          const VdSelectionCard(
            isSelected: true,
            title: 'Disabled selected',
            isDisabled: true,
          ),

          _section(context, 'VdSelectionCardGroup (radio)'),
          VdSelectionCardGroup<String>(
            value: _cardPick,
            onChanged: (v) => setState(() => _cardPick = v),
            child: Column(
              children: const [
                VdSelectionCardOption(
                  value: 'x',
                  title: 'Starter plan',
                  description: 'Best for individuals.',
                  icon: Icon(Icons.person),
                ),
                SizedBox(height: VdSpacing.s300),
                VdSelectionCardOption(
                  value: 'y',
                  title: 'Pro plan',
                  description: 'Great for small teams.',
                  icon: Icon(Icons.groups),
                ),
                SizedBox(height: VdSpacing.s300),
                VdSelectionCardOption(
                  value: 'z',
                  title: 'Enterprise (disabled)',
                  isDisabled: true,
                ),
              ],
            ),
          ),

          _section(context, 'VdDateTimeField'),
          VdDateTimeField(
            'Date & Time',
            selection: _dateTime,
            placeholder: 'Select date & time',
            leadingIcon: const Icon(Icons.calendar_today),
            helperText: 'Tap to open picker',
            onChanged: (d) => setState(() => _dateTime = d),
          ),
          const SizedBox(height: VdSpacing.s400),
          VdDateTimeField(
            'Date only',
            selection: _dateTime,
            placeholder: 'Select date',
            mode: VdDateTimeFieldMode.date,
            leadingIcon: const Icon(Icons.calendar_month),
            onChanged: (d) => setState(() => _dateTime = d),
          ),
          const SizedBox(height: VdSpacing.s400),
          VdDateTimeField(
            'Disabled',
            selection: _dateTime,
            state: VdInputState.disabled,
            helperText: 'Not available right now',
            onChanged: null,
          ),
          const SizedBox(height: VdSpacing.s400),
          VdDateTimeField(
            'Error state',
            selection: null,
            state: VdInputState.error,
            helperText: 'Please select a valid date',
            onChanged: (d) => setState(() => _dateTime = d),
          ),

          const SizedBox(height: VdSpacing.s1600),
        ],
      ),
    );
  }
}

// ─────────────────────────────────────────────────────────────
// Phase 3 — VdSnackbar, VdSearchField, VdSelectField, VdCodeInput
// ─────────────────────────────────────────────────────────────

class _Phase3Screen extends StatefulWidget {
  const _Phase3Screen();

  @override
  State<_Phase3Screen> createState() => _Phase3ScreenState();
}

class _Phase3ScreenState extends State<_Phase3Screen> {
  final _searchCtrl = TextEditingController();
  String? _selectedFruit;
  String? _selectedState;
  String _code = '';

  static const _fruits = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig'];
  static const _states = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California'];

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

  @override
  Widget build(BuildContext context) {
    final scheme = VdColorScheme.of(context);

    return Scaffold(
      backgroundColor: scheme.backgroundDefaultBase,
      appBar: _appBar(context, 'Phase 3'),
      body: ListView(
        padding: const EdgeInsets.all(VdSpacing.s600),
        children: [
          // ── VdSnackbar pill ──────────────────────────────
          _section(context, 'VdSnackbar — Pill'),
          VdSnackbar(message: 'Changes saved successfully'),
          const SizedBox(height: VdSpacing.s200),
          VdSnackbar(
            message: 'Message deleted',
            action: 'Undo',
            onAction: () {},
          ),
          const SizedBox(height: VdSpacing.s200),
          VdSnackbar(
            message: 'Your session will expire in 5 minutes',
            leadingIcon: const Icon(Icons.warning),
            closable: true,
            onClose: () {},
          ),

          // ── showVdSnackbar ───────────────────────────────
          _section(context, 'showVdSnackbar — Global presenter'),
          Wrap(spacing: VdSpacing.s300, runSpacing: VdSpacing.s300, children: [
            VdButton(
              'Basic',
              style: VdButtonStyle.outlined,
              onPressed: () => showVdSnackbar(
                context,
                message: 'Changes saved',
                closable: true,
              ),
            ),
            VdButton(
              'With action',
              onPressed: () => showVdSnackbar(
                context,
                message: 'Message deleted',
                action: 'Undo',
                onAction: () {},
                closable: true,
              ),
            ),
            VdButton(
              'With icon',
              style: VdButtonStyle.subtle,
              onPressed: () => showVdSnackbar(
                context,
                message: 'File uploaded successfully',
                leadingIcon: const Icon(Icons.check_circle),
                closable: true,
              ),
            ),
          ]),

          // ── VdSearchField ────────────────────────────────
          _section(context, 'VdSearchField'),
          VdSearchField(controller: _searchCtrl, placeholder: 'Search items…'),
          const SizedBox(height: VdSpacing.s400),
          VdSearchField(
            controller: TextEditingController(),
            placeholder: 'Disabled',
            isDisabled: true,
          ),

          // ── VdSelectField ────────────────────────────────
          _section(context, 'VdSelectField'),
          VdSelectField<String>(
            selection: _selectedFruit,
            options: _fruits,
            label: 'Fruit',
            placeholder: 'Select a fruit',
            helperText: 'Pick your favourite',
            onChanged: (v) => setState(() => _selectedFruit = v),
          ),
          const SizedBox(height: VdSpacing.s400),
          VdSelectField<String>(
            selection: _selectedState,
            options: _states,
            label: 'State (with icon)',
            leadingIcon: const Icon(Icons.location_on),
            isOptional: true,
            onChanged: (v) => setState(() => _selectedState = v),
          ),
          const SizedBox(height: VdSpacing.s400),
          VdSelectField<String>(
            selection: null,
            options: _fruits,
            label: 'Error',
            state: VdInputState.error,
            helperText: 'Please select an option',
            onChanged: null,
          ),
          const SizedBox(height: VdSpacing.s400),
          VdSelectField<String>(
            selection: _selectedFruit,
            options: _fruits,
            label: 'Disabled',
            state: VdInputState.disabled,
            onChanged: null,
          ),

          // ── VdCodeInput ──────────────────────────────────
          _section(context, 'VdCodeInput — Normal (6 digits)'),
          VdCodeInput(
            code: _code,
            onChanged: (v) => setState(() => _code = v),
            onComplete: () => showVdSnackbar(
              context,
              message: 'Code complete: $_code',
              closable: true,
            ),
          ),
          if (_code.isNotEmpty) ...[
            const SizedBox(height: VdSpacing.s200),
            Text(
              'Entered: $_code',
              style: VdFont.bodySmall.textStyle.copyWith(
                color: VdColorScheme.of(context).contentDefaultTertiary,
              ),
            ),
          ],

          _section(context, 'VdCodeInput — 4 digits'),
          VdCodeInput(
            code: '',
            length: 4,
            onChanged: (_) {},
          ),

          _section(context, 'VdCodeInput — Error'),
          VdCodeInput(
            code: '123',
            state: VdCodeInputState.error,
            onChanged: (_) {},
          ),

          _section(context, 'VdCodeInput — Disabled'),
          VdCodeInput(
            code: '456789',
            state: VdCodeInputState.disabled,
            onChanged: null,
          ),

          const SizedBox(height: VdSpacing.s1600),
        ],
      ),
    );
  }
}
1
likes
140
points
163
downloads

Documentation

API reference

Publisher

verified publisherpramodpoudel.com.np

Weekly Downloads

Vroxal Design System for Flutter — design tokens (color, typography, spacing), a 1,598-glyph custom icon library (VdIcons), and 21 production-ready UI components with automatic light/dark mode support.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter

More

Packages that depend on vroxaldesign