verdify_ui 0.1.0
verdify_ui: ^0.1.0 copied to clipboard
Verdify's Flutter design system: 36 token-driven, WCAG 2.2 AA widgets with light and dark themes, built on Material 3.
example/lib/main.dart
import 'package:flutter/material.dart';
import 'package:verdify_ui/verdify_ui.dart';
void main() {
runApp(const VerdifyExampleApp());
}
class VerdifyExampleApp extends StatelessWidget {
const VerdifyExampleApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'verdify_ui example',
theme: verdifyLightTheme(),
home: const _GalleryPage(),
);
}
}
class _GalleryPage extends StatelessWidget {
const _GalleryPage();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Verdify UI Gallery')),
body: SingleChildScrollView(
padding: const EdgeInsets.all(24),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
VerdifyButton(
onPressed: () {},
child: const Text('Primary'),
),
const SizedBox(height: 12),
VerdifyButton(
variant: VerdifyButtonVariant.secondary,
onPressed: () {},
child: const Text('Secondary'),
),
const SizedBox(height: 12),
VerdifyButton(
variant: VerdifyButtonVariant.ghost,
onPressed: () {},
child: const Text('Ghost'),
),
const SizedBox(height: 12),
VerdifyButton(
variant: VerdifyButtonVariant.destructive,
onPressed: () {},
child: const Text('Destructive'),
),
const SizedBox(height: 24),
const VerdifyTextField(label: 'Email'),
const SizedBox(height: 24),
const Text('Badges'),
const SizedBox(height: 8),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
const VerdifyBadge(label: 'Neutral'),
VerdifyBadge(
label: 'Verified',
status: VerdifyBadgeStatus.verified,
),
VerdifyBadge(
label: 'Signal',
status: VerdifyBadgeStatus.signal,
),
VerdifyBadge(
label: 'Caution',
status: VerdifyBadgeStatus.caution,
),
VerdifyBadge(
label: 'Critical',
status: VerdifyBadgeStatus.critical,
),
VerdifyBadge(
label: 'With icon',
status: VerdifyBadgeStatus.verified,
icon: const Icon(Icons.check),
),
],
),
const SizedBox(height: 24),
const SizedBox(height: 24),
const Text('Cards'),
const SizedBox(height: 8),
VerdifyCard(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
VerdifyCardHeader(
title: const Text('Static Card'),
supporting: const Text('Non-interactive grouping.'),
),
const VerdifyCardBody(
child: Text('Card body content goes here.')),
const VerdifyCardFooter(child: Text('Footer metadata')),
],
),
),
const SizedBox(height: 12),
VerdifyCard(
onPressed: () {},
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
VerdifyCardHeader(title: const Text('Interactive Card')),
const VerdifyCardBody(child: Text('Tap the whole card.')),
],
),
),
const SizedBox(height: 12),
VerdifyCard(
onPressed: () {},
enabled: false,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
VerdifyCardHeader(title: const Text('Disabled Card')),
const VerdifyCardBody(
child: Text('This card is unavailable.')),
],
),
),
const SizedBox(height: 24),
const Text('Checkboxes'),
const SizedBox(height: 8),
const _CheckboxGallery(),
const SizedBox(height: 24),
const Text('Tabs'),
const SizedBox(height: 8),
const _TabsGallery(),
const SizedBox(height: 24),
const Text('Radio (roving foundation)'),
const SizedBox(height: 8),
const _RadioGallery(),
const SizedBox(height: 24),
const Text('Popover (non-modal)'),
const SizedBox(height: 8),
const _PopoverGallery(),
const SizedBox(height: 24),
const Text('Toast (transient queue)'),
const SizedBox(height: 8),
const _ToastGallery(),
const SizedBox(height: 24),
const Text('VerifiedBadge (motion theatre exemplar)'),
const SizedBox(height: 8),
Wrap(
spacing: 8,
runSpacing: 8,
children: const [
VerdifyVerifiedBadge(label: 'Email verified.'),
VerdifyVerifiedBadge(
label: 'ID verified.',
size: VerifiedBadgeSize.sm,
),
VerdifyVerifiedBadge(
labelHidden: true,
ariaLabel: 'Phone verified.',
),
VerdifyVerifiedBadge(
labelHidden: true,
ariaLabel: 'Address verified.',
size: VerifiedBadgeSize.sm,
),
],
),
const SizedBox(height: 24),
VerdifyButton(
onPressed: () => showVerdifyDialog<void>(
context: context,
title: 'Confirm',
content: const Text('Proceed?'),
),
child: const Text('Open dialog'),
),
const SizedBox(height: 24),
const Text('Menu', style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
const _MenuGallery(),
const SizedBox(height: 24),
const Text('Select', style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
const _SelectGallery(),
const SizedBox(height: 24),
const Text('Credential Cards',
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
const _CredentialCardGallery(),
const SizedBox(height: 24),
const Text('Alert (inline status)',
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
const _AlertGallery(),
const SizedBox(height: 24),
const Text('Spinner (unmeasured wait)',
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Wrap(
spacing: 16,
runSpacing: 12,
crossAxisAlignment: WrapCrossAlignment.center,
children: [
VerdifySpinner(
size: VerdifySpinnerSize.sm,
accessibleLabel: 'Loading.',
),
VerdifySpinner(accessibleLabel: 'Loading.'),
VerdifySpinner(
size: VerdifySpinnerSize.lg,
accessibleLabel: 'Loading.',
),
VerdifySpinner(label: 'Checking your ID.'),
],
),
const SizedBox(height: 24),
const Text('Progress (measured wait)',
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
VerdifyProgress(
label: 'Uploading',
value: 0.6,
valueText: '60%',
description: 'Uploading your document.',
),
const SizedBox(height: 12),
VerdifyProgress(label: 'Loading'),
const SizedBox(height: 12),
VerdifyProgress(
label: 'Upload',
value: 0.4,
error: 'Upload failed. Try again.',
),
const SizedBox(height: 24),
const Text('Skeleton (loading placeholder)',
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
VerdifySkeletonGroup(
children: [
Row(
spacing: 12,
children: [
VerdifySkeleton.circle(diameter: 40),
Expanded(
child: VerdifySkeletonGroup(
children: [
VerdifySkeleton.text(width: 160),
VerdifySkeleton.text(width: 100),
],
),
),
],
),
VerdifySkeleton.block(width: double.infinity, height: 120),
VerdifySkeleton.text(width: 240),
VerdifySkeleton.text(width: 180),
],
),
const SizedBox(height: 24),
const Text('Label', style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Wrap(
spacing: 16,
runSpacing: 8,
children: [
const VerdifyLabel(label: 'Email address'),
const VerdifyLabel(label: 'Password', required: true),
const VerdifyLabel(label: 'Middle name', optional: true),
const VerdifyLabel(label: 'Disabled field', disabled: true),
],
),
const SizedBox(height: 24),
const Text('Separator',
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
const VerdifySeparator(),
const SizedBox(height: 8),
const VerdifySeparator(label: 'or'),
const SizedBox(height: 8),
const VerdifySeparator(decorative: true),
const SizedBox(height: 8),
const SizedBox(
height: 40,
child: Row(
children: [
Text('Left'),
VerdifySeparator(
orientation: VerdifySeparatorOrientation.vertical),
Text('Right'),
],
),
),
const SizedBox(height: 24),
const Text('Agent Badges',
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
Wrap(
spacing: 8,
runSpacing: 8,
children: [
const VerdifyAgentBadge(label: 'Agent'),
const VerdifyAgentBadge(
label: 'Agent',
icon: Icon(Icons.smart_toy, size: 16),
),
const VerdifyAgentBadge(
label: 'Agent — expiring',
status: VerdifyAgentBadgeStatus.caution,
),
const VerdifyAgentBadge(
label: 'Agent — revoked',
status: VerdifyAgentBadgeStatus.critical,
),
const VerdifyAgentBadge(
icon: Icon(Icons.smart_toy, size: 16),
semanticLabel: 'AI agent',
),
],
),
const SizedBox(height: 24),
const Text('Switch (Phase-3 assembly)',
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
const _SwitchGallery(),
const SizedBox(height: 24),
const Text('Textarea (Phase-3 assembly)',
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
const _TextareaGallery(),
const SizedBox(height: 24),
const Text('Tooltip (Phase-3 assembly)',
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
const _TooltipGallery(),
const SizedBox(height: 24),
const Text('Sheet (Phase-3 assembly)',
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
const _SheetGallery(),
const SizedBox(height: 24),
const Text('Accordion (Phase-3 assembly)',
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
const _AccordionGallery(),
const SizedBox(height: 24),
const Text('Sidebar (Phase-3 assembly)',
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
const _SidebarGallery(),
const SizedBox(height: 24),
const Text('Command palette (Phase-3 assembly)',
style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 8),
const _CommandPaletteGallery(),
],
),
),
);
}
}
/// Gallery section: opens the modal command palette via
/// [showVerdifyCommandPalette] with grouped commands, a disabled row, a recent
/// list, and a footer of key hints. Demonstrates the activedescendant model —
/// type to filter, arrow to move the active row (focus stays on the input),
/// Enter/tap to run.
class _CommandPaletteGallery extends StatelessWidget {
const _CommandPaletteGallery();
static const _items = <VerdifyCommandPaletteItem>[
VerdifyCommandPaletteItem(
id: 'new-doc',
label: 'New document',
secondary: 'Create a blank document',
icon: Icon(Icons.add),
shortcut: Text('⌘N'),
group: 'Commands',
),
VerdifyCommandPaletteItem(
id: 'open',
label: 'Open file',
icon: Icon(Icons.folder_open),
group: 'Commands',
),
VerdifyCommandPaletteItem(
id: 'archived',
label: 'Archived (unavailable)',
secondary: 'Restore from settings first',
group: 'Commands',
disabled: true,
),
VerdifyCommandPaletteItem(
id: 'settings',
label: 'Go to settings',
icon: Icon(Icons.settings),
group: 'Navigation',
),
VerdifyCommandPaletteItem(
id: 'profile',
label: 'Go to profile',
icon: Icon(Icons.person),
group: 'Navigation',
),
];
static const _recent = <VerdifyCommandPaletteItem>[
VerdifyCommandPaletteItem(id: 'r1', label: 'Recent dashboard'),
VerdifyCommandPaletteItem(id: 'r2', label: 'Recent profile'),
];
@override
Widget build(BuildContext context) {
return Builder(
builder: (context) => VerdifyButton(
onPressed: () => showVerdifyCommandPalette(
context: context,
inputLabel: 'Search commands',
placeholder: 'Type a command…',
items: _items,
recent: _recent,
footer: const Text('↑↓ Move ⏎ Run Esc Dismiss'),
onRun: (item) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Ran: ${item.label}')),
);
},
),
child: const Text('Open command palette (⌘K)'),
),
);
}
}
class _CheckboxGallery extends StatefulWidget {
const _CheckboxGallery();
@override
State<_CheckboxGallery> createState() => _CheckboxGalleryState();
}
class _CheckboxGalleryState extends State<_CheckboxGallery> {
bool _standalone = false;
bool? _parent; // null = indeterminate
bool _withDesc = false;
bool _error = false;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
VerdifyCheckbox(
label: 'Standalone',
checked: _standalone,
onChanged: (v) => setState(() => _standalone = v),
),
const SizedBox(height: 8),
VerdifyCheckbox(
label: 'Parent (indeterminate capable)',
variant: VerdifyCheckboxVariant.parent,
checked: _parent,
onChanged: (v) => setState(() => _parent = v),
),
const SizedBox(height: 8),
VerdifyCheckbox(
label: 'With description',
description: 'Receive weekly updates',
checked: _withDesc,
onChanged: (v) => setState(() => _withDesc = v),
),
const SizedBox(height: 8),
VerdifyCheckbox(
label: 'Error state',
error: 'You must agree to proceed',
checked: _error,
onChanged: (v) => setState(() => _error = v),
),
const SizedBox(height: 8),
VerdifyCheckbox(
label: 'Disabled checked',
checked: true,
onChanged: null,
),
const SizedBox(height: 8),
VerdifyCheckbox(
label: 'Size small',
size: VerdifyCheckboxSize.sm,
checked: false,
onChanged: (_) {},
),
],
);
}
}
class _TabsGallery extends StatefulWidget {
const _TabsGallery();
@override
State<_TabsGallery> createState() => _TabsGalleryState();
}
class _TabsGalleryState extends State<_TabsGallery> {
int _underline = 0;
int _pill = 1;
static const _tabs = [
VerdifyTab(label: 'Overview', icon: Icons.dashboard_outlined),
VerdifyTab(label: 'Activity', count: 3),
VerdifyTab(label: 'Settings'),
VerdifyTab(label: 'Archived', disabled: true),
];
static const _panels = [
Text('Overview panel content.'),
Text('Activity panel content.'),
Text('Settings panel content.'),
Text('Archived panel content.'),
];
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
VerdifyTabs(
tabs: _tabs,
panels: _panels,
selected: _underline,
onChanged: (i) => setState(() => _underline = i),
),
const SizedBox(height: 16),
VerdifyTabs(
tabs: _tabs,
panels: _panels,
selected: _pill,
variant: VerdifyTabsVariant.pill,
activation: VerdifyTabsActivation.automatic,
onChanged: (i) => setState(() => _pill = i),
),
],
);
}
}
class _RadioGallery extends StatefulWidget {
const _RadioGallery();
@override
State<_RadioGallery> createState() => _RadioGalleryState();
}
class _RadioGalleryState extends State<_RadioGallery> {
String? _fruit = 'banana';
String? _plan = 'pro';
String? _billing = 'monthly';
static const _fruits = [
VerdifyRadioOption(value: 'apple', label: 'Apple'),
VerdifyRadioOption(value: 'banana', label: 'Banana'),
VerdifyRadioOption(value: 'cherry', label: 'Cherry', disabled: true),
VerdifyRadioOption(value: 'date', label: 'Date'),
];
static const _plans = [
VerdifyRadioOption(
value: 'free',
label: 'Free',
description: 'For getting started — 1 identity.',
),
VerdifyRadioOption(
value: 'pro',
label: 'Pro',
description: 'For growing teams — unlimited profiles.',
),
];
static const _billingOptions = [
VerdifyRadioOption(value: 'monthly', label: 'Monthly'),
VerdifyRadioOption(value: 'annual', label: 'Annual'),
];
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
VerdifyRadioGroup(
label: 'Pick a fruit',
options: _fruits,
value: _fruit,
onChanged: (v) => setState(() => _fruit = v),
),
const SizedBox(height: 24),
VerdifyRadioGroup(
label: 'Plan',
options: _plans,
value: _plan,
variant: VerdifyRadioVariant.withDescription,
onChanged: (v) => setState(() => _plan = v),
),
const SizedBox(height: 24),
VerdifyRadioGroup(
label: 'Billing',
options: _billingOptions,
value: _billing,
variant: VerdifyRadioVariant.card,
onChanged: (v) => setState(() => _billing = v),
),
],
);
}
}
class _PopoverGallery extends StatelessWidget {
const _PopoverGallery();
@override
Widget build(BuildContext context) {
return Wrap(
spacing: 16,
runSpacing: 16,
children: [
// Content (default): a plain anchored note.
VerdifyPopover(
trigger: (context, isOpen, toggle) => VerdifyButton(
variant: VerdifyButtonVariant.secondary,
onPressed: toggle,
child: Text(isOpen ? 'Hide note' : 'Show note'),
),
builder: (context) => const VerdifyPopoverBody(
child: Text('A short, non-modal note anchored to its trigger. The '
'page behind stays live.'),
),
),
// With-header: title + close control.
VerdifyPopover(
trigger: (context, isOpen, toggle) => VerdifyButton(
variant: VerdifyButtonVariant.secondary,
onPressed: toggle,
child: const Text('Settings'),
),
builder: (context) => const VerdifyPopoverHeader(
title: VerdifyPopoverTitle(child: Text('Settings')),
close: VerdifyPopoverClose(),
body: VerdifyPopoverBody(
child: Text('Tune how this credential is shared.'),
),
),
),
// With-arrow, opening to the right.
VerdifyPopover(
side: PopoverSide.right,
showArrow: true,
trigger: (context, isOpen, toggle) => VerdifyButton(
variant: VerdifyButtonVariant.secondary,
onPressed: toggle,
child: const Text('Details'),
),
builder: (context) => const VerdifyPopoverBody(
child: Text('Anchored with a pointer to its trigger.'),
),
),
],
);
}
}
class _ToastGallery extends StatefulWidget {
const _ToastGallery();
@override
State<_ToastGallery> createState() => _ToastGalleryState();
}
class _ToastGalleryState extends State<_ToastGallery> {
final _controller = VerdifyToastController();
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return VerdifyToastRegion(
controller: _controller,
child: Wrap(
spacing: 8,
runSpacing: 8,
children: [
for (final variant in VerdifyToastVariant.values)
VerdifyButton(
variant: VerdifyButtonVariant.secondary,
onPressed: () => _controller.show(
'${variant.name[0].toUpperCase()}'
'${variant.name.substring(1)} notification.',
variant: variant,
),
child: Text(variant.name),
),
VerdifyButton(
variant: VerdifyButtonVariant.ghost,
onPressed: _controller.dismissAll,
child: const Text('Dismiss all'),
),
],
),
);
}
}
class _CredentialCardGallery extends StatefulWidget {
const _CredentialCardGallery();
@override
State<_CredentialCardGallery> createState() => _CredentialCardGalleryState();
}
class _CredentialCardGalleryState extends State<_CredentialCardGallery> {
bool _selected = false;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Email — default
VerdifyCredentialCard(
label: 'Email',
identifier: 'jordan@example.com',
identifierText: 'jordan@example.com',
onRemove: () {},
),
const SizedBox(height: 8),
// Email — verified + primary badges
VerdifyCredentialCard(
label: 'Email',
identifier: 'verified@example.com',
identifierText: 'verified@example.com',
onRemove: () {},
status: const [
CredentialCardStatus(
kind: CredentialCardStatusKind.verified, label: 'Verified'),
CredentialCardStatus(
kind: CredentialCardStatusKind.primary, label: 'Primary'),
],
),
const SizedBox(height: 8),
// Passkey — with meta
VerdifyCredentialCard(
label: 'Passkey',
identifier: 'MacBook Pro',
identifierText: 'MacBook Pro',
kind: CredentialKind.passkey,
onRemove: () {},
meta: 'Added 3 months ago',
),
const SizedBox(height: 8),
// Phone — selectable, stateful toggle
VerdifyCredentialCard(
label: 'Phone',
identifier: '+1 (555) 123-4567',
identifierText: 'phone +1 (555) 123-4567',
kind: CredentialKind.phone,
onRemove: () {},
selectable: true,
selected: _selected,
onSelectedChange: (v) => setState(() => _selected = v),
),
const SizedBox(height: 8),
// Wallet — disabled remove
VerdifyCredentialCard(
label: 'Wallet',
identifier: '0x7a25...4e2f',
identifierText: '0x7a25...4e2f',
kind: CredentialKind.wallet,
onRemove: () {},
removeDisabled: true,
removeDisabledReason: 'Cannot remove the last sign-in credential',
),
const SizedBox(height: 8),
// Enterprise SSO — loading
VerdifyCredentialCard(
label: 'Enterprise SSO',
identifier: 'acme-corp.okta.com',
identifierText: 'acme-corp.okta.com',
kind: CredentialKind.enterpriseSso,
onRemove: () {},
loading: true,
),
],
);
}
}
class _MenuGallery extends StatelessWidget {
const _MenuGallery();
@override
Widget build(BuildContext context) {
final messenger = ScaffoldMessenger.of(context);
void fire(String command) => messenger.showSnackBar(
SnackBar(content: Text('Ran: $command')),
);
return Align(
alignment: Alignment.centerLeft,
child: VerdifyMenu(
triggerSemanticLabel: 'Credential actions',
trigger: (context, isOpen, open) => VerdifyButton(
variant: VerdifyButtonVariant.secondary,
onPressed: open,
child: const Text('Actions'),
),
items: [
VerdifyMenuGroup(
label: 'Manage',
items: [
VerdifyMenuItem(
label: 'Open',
icon: const Icon(Icons.open_in_new),
shortcut: 'Cmd+O',
onSelected: () => fire('Open'),
),
VerdifyMenuItem(
label: 'Rename',
icon: const Icon(Icons.edit_outlined),
onSelected: () => fire('Rename'),
),
const VerdifyMenuItem(
label: 'Archive',
icon: Icon(Icons.archive_outlined),
disabled: true,
),
VerdifyMenuSubmenu(
label: 'Share',
icon: const Icon(Icons.ios_share),
items: [
VerdifyMenuItem(
label: 'Copy link',
onSelected: () => fire('Copy link'),
),
VerdifyMenuItem(
label: 'Email',
onSelected: () => fire('Email'),
),
],
),
],
),
const VerdifyMenuSeparator(),
VerdifyMenuItem(
label: 'Revoke key',
icon: const Icon(Icons.delete_outline),
destructive: true,
onSelected: () => fire('Revoke key'),
),
],
),
);
}
}
class _SelectGallery extends StatefulWidget {
const _SelectGallery();
@override
State<_SelectGallery> createState() => _SelectGalleryState();
}
class _SelectGalleryState extends State<_SelectGallery> {
String? _region;
String? _plan = 'pro';
String? _invalid;
static const _regions = [
VerdifySelectOption(value: 'na', label: 'North America', group: 'Americas'),
VerdifySelectOption(value: 'sa', label: 'South America', group: 'Americas'),
VerdifySelectOption(value: 'eu', label: 'Europe', group: 'EMEA'),
VerdifySelectOption(value: 'me', label: 'Middle East', group: 'EMEA'),
VerdifySelectOption(value: 'ap', label: 'Asia Pacific', group: 'APAC'),
];
static const _plans = [
VerdifySelectOption(value: 'free', label: 'Free'),
VerdifySelectOption(value: 'pro', label: 'Pro'),
VerdifySelectOption(value: 'team', label: 'Team', disabled: true),
VerdifySelectOption(value: 'enterprise', label: 'Enterprise'),
];
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
VerdifySelect(
label: 'Region',
placeholder: 'Choose a region',
description: 'Where your data is stored.',
options: _regions,
value: _region,
width: VerdifySelectWidth.full,
onChanged: (v) => setState(() => _region = v),
),
const SizedBox(height: 16),
VerdifySelect(
label: 'Plan',
options: _plans,
value: _plan,
onChanged: (v) => setState(() => _plan = v),
),
const SizedBox(height: 16),
VerdifySelect(
label: 'Required field',
placeholder: 'Pick one',
options: _plans,
value: _invalid,
error: _invalid == null ? 'This field is required.' : null,
width: VerdifySelectWidth.full,
onChanged: (v) => setState(() => _invalid = v),
),
const SizedBox(height: 16),
const VerdifySelect(
label: 'Disabled',
placeholder: 'Not available',
options: _plans,
enabled: false,
onChanged: _noop,
),
],
);
}
static void _noop(String _) {}
}
class _AlertGallery extends StatefulWidget {
const _AlertGallery();
@override
State<_AlertGallery> createState() => _AlertGalleryState();
}
class _AlertGalleryState extends State<_AlertGallery> {
bool _criticalDismissed = false;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Verified — body only
const VerdifyAlert(
variant: VerdifyAlertVariant.verified,
body: Text('Your email address has been verified.'),
),
const SizedBox(height: 8),
// Signal — title + body
const VerdifyAlert(
variant: VerdifyAlertVariant.signal,
title: Text('Heads up.'),
body: Text('Your trial ends in 3 days. No action needed yet.'),
),
const SizedBox(height: 8),
// Caution — title + body + dismiss
VerdifyAlert(
variant: VerdifyAlertVariant.caution,
title: const Text('Review required.'),
body: const Text('Your proof document is pending manual review.'),
onDismiss: () {},
),
const SizedBox(height: 8),
// Critical — title + body + action + dismiss (dismissible)
if (!_criticalDismissed)
VerdifyAlert(
variant: VerdifyAlertVariant.critical,
title: const Text('Verification failed.'),
body: const Text(
'The proof could not be validated. Check the document and try again.'),
actions: [
VerdifyButton(
variant: VerdifyButtonVariant.secondary,
onPressed: () {},
child: const Text('Retry'),
),
],
onDismiss: () => setState(() => _criticalDismissed = true),
),
if (_criticalDismissed)
VerdifyButton(
variant: VerdifyButtonVariant.ghost,
onPressed: () => setState(() => _criticalDismissed = false),
child: const Text('Show critical alert again'),
),
const SizedBox(height: 24),
const Text('Breadcrumb'),
const SizedBox(height: 8),
VerdifyBreadcrumb(
items: [
VerdifyBreadcrumbItem(label: 'Home', onTap: () {}),
VerdifyBreadcrumbItem(label: 'Billing', onTap: () {}),
],
current: 'Invoice',
),
const SizedBox(height: 8),
VerdifyBreadcrumb(
items: [
VerdifyBreadcrumbItem(
label: 'Home',
icon: const Icon(Icons.home),
onTap: () {},
),
VerdifyBreadcrumbItem(label: 'Org', onTap: () {}),
VerdifyBreadcrumbItem(label: 'Project', onTap: () {}),
],
current: 'Settings',
collapsed: true,
),
const SizedBox(height: 24),
const Text('Table'),
const SizedBox(height: 8),
VerdifyTable(
columns: [
VerdifyTableColumn(
key: 'name',
header: 'Name',
sortable: true,
onSort: (_) {},
),
const VerdifyTableColumn(key: 'status', header: 'Status'),
const VerdifyTableColumn(key: 'date', header: 'Date'),
],
rows: [
VerdifyTableRow(
id: 'r1',
cells: {
'name': const Text('Alice Brown'),
'status': const Text('Active'),
'date': const Text('2026-06-01'),
},
),
VerdifyTableRow(
id: 'r2',
cells: {
'name': const Text('Bob Clark'),
'status': const Text('Pending'),
'date': const Text('2026-06-02'),
},
),
],
caption: 'Users',
sortedColumnKey: 'name',
sortAscending: true,
),
const SizedBox(height: 24),
const Text('Data Grid'),
const SizedBox(height: 8),
const _DataGridGallery(),
const SizedBox(height: 24),
const Text('Avatar'),
const SizedBox(height: 8),
const Row(
children: [
VerdifyAvatar(
displayName: 'Alice Brown', size: VerdifyAvatarSize.sm),
SizedBox(width: 8),
VerdifyAvatar(displayName: 'Alice Brown'),
SizedBox(width: 8),
VerdifyAvatar(
displayName: 'Alice Brown', size: VerdifyAvatarSize.lg),
SizedBox(width: 8),
VerdifyAvatar(displayName: '', size: VerdifyAvatarSize.md),
SizedBox(width: 8),
VerdifyAvatar(
displayName: 'Org',
shape: VerdifyAvatarShape.rounded,
),
SizedBox(width: 8),
VerdifyAvatar(displayName: 'A B', showBorder: true),
SizedBox(width: 8),
VerdifyAvatar(displayName: 'Loading', loading: true),
],
),
const SizedBox(height: 24),
const Text('Pagination'),
const SizedBox(height: 8),
VerdifyPagination(
currentPage: 3,
totalPages: 10,
onPageChanged: (_) {},
),
const SizedBox(height: 8),
VerdifyPagination(
currentPage: 2,
totalPages: 5,
onPageChanged: (_) {},
variant: VerdifyPaginationVariant.prevNext,
),
const SizedBox(height: 24),
const Text('IdentityChip'),
const SizedBox(height: 8),
const Wrap(
spacing: 8,
runSpacing: 8,
children: [
VerdifyIdentityChip(
displayName: 'Jordan Rivera',
name: 'Jordan Rivera',
),
VerdifyIdentityChip(
displayName: 'Atlas',
name: 'Atlas',
isAgent: true,
),
VerdifyIdentityChip(
displayName: 'Jordan Rivera',
name: 'Jordan Rivera',
showVerified: true,
verifiedLabel: 'ID verified.',
),
],
),
const SizedBox(height: 24),
const Text('ConsentToggle'),
const SizedBox(height: 8),
VerdifyConsentToggle(
scope: 'Share location data.',
recipient: 'Verdify Maps',
value: false,
onChanged: (_) {},
),
const SizedBox(height: 12),
VerdifyConsentToggle(
scope: 'Share location data.',
recipient: 'Verdify Maps',
value: true,
onChanged: (_) {},
),
const SizedBox(height: 12),
VerdifyConsentToggle(
scope: 'Allow Atlas to read your credentials.',
recipient: 'Atlas (AI agent)',
value: false,
onChanged: (_) {},
),
const SizedBox(height: 24),
const Text('TrustScore'),
const SizedBox(height: 8),
const VerdifyTrustScore(
score: 82,
label: 'Trust score',
scale: 'out of 100',
),
const SizedBox(height: 12),
const VerdifyTrustScore(
score: 82,
label: 'Trust score',
scale: 'out of 100',
variant: VerdifyTrustScoreVariant.meter,
scoreMax: 100,
),
const SizedBox(height: 12),
const VerdifyTrustScore(
score: 82,
label: 'Trust score',
variant: VerdifyTrustScoreVariant.compact,
),
],
);
}
}
class _TextareaGallery extends StatelessWidget {
const _TextareaGallery();
@override
Widget build(BuildContext context) {
return const Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
VerdifyTextarea(label: 'Notes'),
SizedBox(height: 12),
VerdifyTextarea(
label: 'Bio',
description: 'A short biography.',
placeholder: 'Tell us about yourself.',
),
SizedBox(height: 12),
VerdifyTextarea(
label: 'Tweet',
maxLength: 280,
showCounter: true,
),
SizedBox(height: 12),
VerdifyTextarea(
label: 'Reason',
errorText: 'This field is required.',
),
SizedBox(height: 12),
VerdifyTextarea(
label: 'Disabled notes',
enabled: false,
),
SizedBox(height: 12),
VerdifyTextarea(
label: 'Read-only notes',
readOnly: true,
),
SizedBox(height: 12),
VerdifyTextarea(
label: 'Auto-grow',
resize: VerdifyTextareaResize.autoGrow,
),
],
);
}
}
class _SwitchGallery extends StatefulWidget {
const _SwitchGallery();
@override
State<_SwitchGallery> createState() => _SwitchGalleryState();
}
class _SwitchGalleryState extends State<_SwitchGallery> {
bool _notifications = false;
bool _darkMode = true;
bool _sync = false;
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
VerdifySwitch(
label: 'Notifications',
value: _notifications,
onChanged: (v) => setState(() => _notifications = v),
),
const SizedBox(height: 8),
VerdifySwitch(
label: 'Dark mode',
description: 'Applies a dark colour scheme.',
value: _darkMode,
onChanged: (v) => setState(() => _darkMode = v),
),
const SizedBox(height: 8),
VerdifySwitch(
label: 'Sync',
value: _sync,
onChanged: (v) => setState(() => _sync = v),
labelPlacement: VerdifySwitchLabelPlacement.after,
),
const SizedBox(height: 8),
VerdifySwitch(
label: 'Small switch',
value: false,
onChanged: (v) => setState(() {}),
size: VerdifySwitchSize.sm,
),
const SizedBox(height: 8),
VerdifySwitch(
label: 'Disabled (off)',
value: false,
onChanged: null,
),
const SizedBox(height: 8),
VerdifySwitch(
label: 'Disabled (on)',
value: true,
onChanged: null,
),
],
);
}
}
/// Gallery section: VerdifySheet rows.
class _SheetGallery extends StatelessWidget {
const _SheetGallery();
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Default: inline-end, md, with footer.
VerdifyButton(
onPressed: () => showVerdifySheet(
context: context,
title: 'Edit profile',
body: const Text('Profile fields go here.'),
footer: VerdifyButton(
onPressed: () {},
child: const Text('Save'),
),
),
child: const Text('Open sheet (inline-end, md)'),
),
const SizedBox(height: 8),
// inline-start, sm.
VerdifyButton(
variant: VerdifyButtonVariant.secondary,
onPressed: () => showVerdifySheet(
context: context,
title: 'Navigation',
side: VerdifySheetSide.inlineStart,
size: VerdifySheetSize.sm,
body: const Text('Nav links here.'),
),
child: const Text('Open sheet (inline-start, sm)'),
),
const SizedBox(height: 8),
// block-end (bottom).
VerdifyButton(
variant: VerdifyButtonVariant.secondary,
onPressed: () => showVerdifySheet(
context: context,
title: 'Filters',
side: VerdifySheetSide.blockEnd,
body: const Text('Filter controls here.'),
),
child: const Text('Open sheet (block-end)'),
),
],
);
}
}
/// Gallery section: VerdifyTooltip rows.
class _TooltipGallery extends StatefulWidget {
const _TooltipGallery();
@override
State<_TooltipGallery> createState() => _TooltipGalleryState();
}
class _TooltipGalleryState extends State<_TooltipGallery> {
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// label variant — tooltip IS the accessible name for an icon-only button.
VerdifyTooltip(
content: 'Copy key',
variant: VerdifyTooltipVariant.label,
child: VerdifyButton(
onPressed: () {},
semanticLabel: 'Copy key',
child: const Icon(Icons.copy, size: 18),
),
),
const SizedBox(height: 8),
// description variant — tooltip supplements a labeled button.
VerdifyTooltip(
content: 'Rotates every 90 days.',
variant: VerdifyTooltipVariant.description,
child: VerdifyButton(
onPressed: () {},
child: const Text('API Key'),
),
),
const SizedBox(height: 8),
// side: bottom — opens below trigger.
VerdifyTooltip(
content: 'Opens below',
side: AnchoredSide.bottom,
child: VerdifyButton(
onPressed: () {},
variant: VerdifyButtonVariant.secondary,
child: const Text('Below'),
),
),
],
);
}
}
// ── Accordion gallery ─────────────────────────────────────────────────────────
class _AccordionGallery extends StatelessWidget {
const _AccordionGallery();
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
const Text('Single mode'),
const SizedBox(height: 8),
VerdifyAccordion(
items: [
VerdifyAccordionItem(
title: 'What is Verdify?',
panel: const Text('A decentralised identity platform.'),
),
VerdifyAccordionItem(
title: 'How does it work?',
panel: const Text('Via proofs and verifiable credentials.'),
),
VerdifyAccordionItem(
title: 'Coming soon',
panel: const Text('More features.'),
disabled: true,
),
],
),
const SizedBox(height: 24),
const Text('Multiple mode'),
const SizedBox(height: 8),
VerdifyAccordion(
mode: VerdifyAccordionMode.multiple,
items: [
VerdifyAccordionItem(
title: 'Credentials',
panel: const Text('Tamper-evident proofs.'),
initiallyOpen: true,
),
VerdifyAccordionItem(
title: 'Identity',
panel: const Text('One identity, many profiles.'),
initiallyOpen: true,
),
],
),
],
);
}
}
// ── Sidebar gallery ───────────────────────────────────────────────────────────
class _SidebarGallery extends StatefulWidget {
const _SidebarGallery();
@override
State<_SidebarGallery> createState() => _SidebarGalleryState();
}
class _SidebarGalleryState extends State<_SidebarGallery> {
int _current = 0;
bool _collapsed = false;
static const _items = [
VerdifySidebarItem(icon: Icons.home, label: 'Home'),
VerdifySidebarItem(icon: Icons.search, label: 'Search'),
VerdifySidebarItem(icon: Icons.person, label: 'Profile', disabled: true),
VerdifySidebarItem(icon: Icons.settings, label: 'Settings'),
];
@override
Widget build(BuildContext context) {
return Row(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
VerdifySidebar(
items: _items,
currentIndex: _current,
onItemTap: (i) => setState(() => _current = i),
collapsed: _collapsed,
collapsible: true,
onCollapseToggle: (v) => setState(() => _collapsed = v),
header: const Text('Verdify'),
footer: const Text('user@example.com'),
),
const SizedBox(width: 16),
Padding(
padding: const EdgeInsets.all(16),
child: Text('Page: ${_items[_current].label}'),
),
],
);
}
}
// ── Data Grid gallery ───────────────────────────────────────────────────────
class _DataGridGallery extends StatefulWidget {
const _DataGridGallery();
@override
State<_DataGridGallery> createState() => _DataGridGalleryState();
}
class _DataGridGalleryState extends State<_DataGridGallery> {
Set<String> _selected = {'sk_live_1'};
String? _sortedKey = 'status';
bool _ascending = true;
static const _rows = [
VerdifyDataGridRow(
id: 'sk_live_1',
rowIndex: 4210,
cells: {
'key': VerdifyDataGridCell(text: 'sk_live_1'),
'status': VerdifyDataGridCell(
text: 'Verified',
status: VerdifyDataGridCellStatus.verified,
),
},
),
VerdifyDataGridRow(
id: 'sk_live_2',
rowIndex: 4211,
cells: {
'key': VerdifyDataGridCell(text: 'sk_live_2'),
'status': VerdifyDataGridCell(
text: 'Expired',
status: VerdifyDataGridCellStatus.critical,
),
},
),
VerdifyDataGridRow(
id: 'sk_live_3',
rowIndex: 4212,
cells: {
'key': VerdifyDataGridCell(text: 'sk_live_3'),
'status': VerdifyDataGridCell(
text: 'Pending',
status: VerdifyDataGridCellStatus.caution,
),
},
),
];
@override
Widget build(BuildContext context) {
return SizedBox(
height: 280,
child: VerdifyDataGrid(
label: 'API keys',
rowCount: 12000,
colCount: 3,
columns: [
const VerdifyDataGridColumn(
key: 'key',
header: 'Key',
mono: true,
flex: 2,
),
VerdifyDataGridColumn(
key: 'status',
header: 'Status',
sortable: true,
onSort: (asc) => setState(() {
_sortedKey = 'status';
_ascending = asc;
}),
),
],
rows: _rows,
selection: VerdifyDataGridSelection.multiple,
selectedIds: _selected,
onSelectionChanged: (s) => setState(() => _selected = s),
sortedColumnKey: _sortedKey,
sortAscending: _ascending,
announcement: '${_selected.length} selected.',
onSelectAll: () =>
setState(() => _selected = _rows.map((r) => r.id).toSet()),
bulkActions: [
VerdifyDataGridBulkAction(label: 'Export', onPressed: () {}),
VerdifyDataGridBulkAction(
label: 'Revoke',
destructive: true,
onPressed: () => setState(() => _selected = {}),
),
],
),
);
}
}