nice2dev_flutter_ui 1.0.2 copy "nice2dev_flutter_ui: ^1.0.2" to clipboard
nice2dev_flutter_ui: ^1.0.2 copied to clipboard

NiceToDev Flutter UI — A comprehensive set of enterprise-grade widgets for building ERP applications. Counterpart of @nice2dev/ui for Flutter.

example/lib/main.dart

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

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

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

  @override
  Widget build(BuildContext context) {
    return NiceTheme(
      data: NiceThemeData.light(),
      child: MaterialApp(
        title: 'NiceToDev Flutter UI',
        theme: NiceThemeData.light().toThemeData(),
        darkTheme: NiceThemeData.dark().toThemeData(),
        home: const ExampleHomePage(),
      ),
    );
  }
}

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

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

class _ExampleHomePageState extends State<ExampleHomePage> {
  int _selectedIndex = 0;

  final List<({String title, IconData icon, Widget page})> _sections = [
    (title: 'Buttons', icon: Icons.smart_button, page: const ButtonsSection()),
    (title: 'Inputs', icon: Icons.text_fields, page: const InputsSection()),
    (title: 'Data', icon: Icons.table_chart, page: const DataSection()),
    (title: 'Forms', icon: Icons.dynamic_form, page: const FormsSection()),
    (title: 'Charts', icon: Icons.bar_chart, page: const ChartsSection()),
    (title: 'Navigation', icon: Icons.menu, page: const NavigationSection()),
    (title: 'Feedback', icon: Icons.notifications, page: const FeedbackSection()),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('NiceToDev Flutter UI'),
      ),
      drawer: Drawer(
        child: ListView.builder(
          itemCount: _sections.length + 1,
          itemBuilder: (context, index) {
            if (index == 0) {
              return const DrawerHeader(
                decoration: BoxDecoration(color: Colors.blue),
                child: Text(
                  'Widget Examples',
                  style: TextStyle(color: Colors.white, fontSize: 24),
                ),
              );
            }
            final section = _sections[index - 1];
            return ListTile(
              leading: Icon(section.icon),
              title: Text(section.title),
              selected: _selectedIndex == index - 1,
              onTap: () {
                setState(() => _selectedIndex = index - 1);
                Navigator.pop(context);
              },
            );
          },
        ),
      ),
      body: _sections[_selectedIndex].page,
    );
  }
}

// === BUTTONS SECTION ===
class ButtonsSection extends StatelessWidget {
  const ButtonsSection({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        _SectionTitle('NiceButton'),
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: [
            NiceButton(
              label: 'Primary',
              onPressed: () {},
            ),
            NiceButton(
              label: 'Outlined',
              variant: NiceButtonVariant.outlined,
              onPressed: () {},
            ),
            NiceButton(
              label: 'Text',
              variant: NiceButtonVariant.text,
              onPressed: () {},
            ),
            NiceButton(
              label: 'Danger',
              variant: NiceButtonVariant.danger,
              onPressed: () {},
            ),
            const NiceButton(
              label: 'Disabled',
              disabled: true,
            ),
            const NiceButton(
              label: 'Loading',
              loading: true,
            ),
          ],
        ),
        const SizedBox(height: 24),
        _SectionTitle('NiceButton Sizes'),
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: [
            NiceButton(
              label: 'Small',
              size: NiceButtonSize.small,
              onPressed: () {},
            ),
            NiceButton(
              label: 'Medium',
              size: NiceButtonSize.medium,
              onPressed: () {},
            ),
            NiceButton(
              label: 'Large',
              size: NiceButtonSize.large,
              onPressed: () {},
            ),
          ],
        ),
        const SizedBox(height: 24),
        _SectionTitle('NiceIconButton'),
        Wrap(
          spacing: 8,
          children: [
            NiceIconButton(
              icon: Icons.add,
              onPressed: () {},
              tooltip: 'Add item',
            ),
            NiceIconButton(
              icon: Icons.edit,
              onPressed: () {},
              tooltip: 'Edit',
            ),
            NiceIconButton(
              icon: Icons.delete,
              onPressed: () {},
              tooltip: 'Delete',
            ),
          ],
        ),
        const SizedBox(height: 24),
        _SectionTitle('NiceButtonGroup'),
        const NiceButtonGroup(
          children: [
            NiceButton(label: 'Left'),
            NiceButton(label: 'Center'),
            NiceButton(label: 'Right'),
          ],
        ),
      ],
    );
  }
}

// === INPUTS SECTION ===
class InputsSection extends StatefulWidget {
  const InputsSection({super.key});

  @override
  State<InputsSection> createState() => _InputsSectionState();
}

class _InputsSectionState extends State<InputsSection> {
  String _textValue = '';
  double _numberValue = 5;
  String? _selectedOption;
  bool _checkboxValue = false;
  bool _toggleValue = false;
  String _radioValue = 'option1';

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        _SectionTitle('NiceTextInput'),
        NiceTextInput(
          label: 'Username',
          hint: 'Enter your username',
          onChanged: (value) => setState(() => _textValue = value),
        ),
        const SizedBox(height: 8),
        Text('Value: $_textValue'),
        const SizedBox(height: 24),
        _SectionTitle('NiceNumberInput'),
        NiceNumberInput(
          label: 'Quantity',
          value: _numberValue,
          min: 0,
          max: 10,
          onChanged: (value) => setState(() => _numberValue = value.toDouble()),
        ),
        Text('Value: $_numberValue'),
        const SizedBox(height: 24),
        _SectionTitle('NiceSelect'),
        NiceSelect<String>(
          label: 'Select Option',
          value: _selectedOption,
          items: const [
            NiceSelectItem(value: 'opt1', label: 'Option 1'),
            NiceSelectItem(value: 'opt2', label: 'Option 2'),
            NiceSelectItem(value: 'opt3', label: 'Option 3'),
          ],
          onChanged: (value) => setState(() => _selectedOption = value),
        ),
        const SizedBox(height: 24),
        _SectionTitle('NiceCheckbox'),
        NiceCheckbox(
          label: 'Accept terms and conditions',
          value: _checkboxValue,
          onChanged: (value) => setState(() => _checkboxValue = value ?? false),
        ),
        const SizedBox(height: 24),
        _SectionTitle('NiceToggle'),
        NiceToggle(
          label: 'Enable notifications',
          value: _toggleValue,
          onChanged: (value) => setState(() => _toggleValue = value),
        ),
        const SizedBox(height: 24),
        _SectionTitle('NiceRadioGroup'),
        NiceRadioGroup<String>(
          label: 'Choose option',
          value: _radioValue,
          items: const [
            NiceRadioItem(value: 'option1', label: 'Option 1'),
            NiceRadioItem(value: 'option2', label: 'Option 2'),
            NiceRadioItem(value: 'option3', label: 'Option 3'),
          ],
          onChanged: (value) => setState(() => _radioValue = value),
        ),
        const SizedBox(height: 24),
        _SectionTitle('NiceTextArea'),
        const NiceTextArea(
          label: 'Description',
          hint: 'Enter a description...',
          minLines: 3,
          maxLines: 5,
        ),
      ],
    );
  }
}

// === DATA SECTION ===
class DataSection extends StatelessWidget {
  const DataSection({super.key});

  @override
  Widget build(BuildContext context) {
    final columns = [
      NiceGridColumn<Map<String, dynamic>>(
        id: 'name',
        header: 'Name',
        cellBuilder: (row, index) => Text(row['name'] as String),
      ),
      NiceGridColumn<Map<String, dynamic>>(
        id: 'email',
        header: 'Email',
        cellBuilder: (row, index) => Text(row['email'] as String),
      ),
      NiceGridColumn<Map<String, dynamic>>(
        id: 'role',
        header: 'Role',
        cellBuilder: (row, index) => Text(row['role'] as String),
      ),
    ];

    final rows = [
      {'name': 'John Doe', 'email': 'john@example.com', 'role': 'Admin'},
      {'name': 'Jane Smith', 'email': 'jane@example.com', 'role': 'User'},
      {'name': 'Bob Wilson', 'email': 'bob@example.com', 'role': 'Editor'},
      {'name': 'Alice Brown', 'email': 'alice@example.com', 'role': 'User'},
    ];

    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        _SectionTitle('NiceDataGrid'),
        SizedBox(
          height: 300,
          child: NiceDataGrid<Map<String, dynamic>>(
            columns: columns,
            rows: rows,
            onRowTap: (row, index) {
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(content: Text('Tapped: ${row['name']}')),
              );
            },
          ),
        ),
        const SizedBox(height: 24),
        _SectionTitle('NiceList'),
        SizedBox(
          height: 200,
          child: NiceList<String>(
            items: List.generate(10, (i) => 'List Item ${i + 1}'),
            itemBuilder: (item, index) => ListTile(
              leading: CircleAvatar(child: Text('${index + 1}')),
              title: Text(item),
              subtitle: Text('Subtitle for $item'),
            ),
          ),
        ),
        const SizedBox(height: 24),
        _SectionTitle('NiceTreeView'),
        SizedBox(
          height: 200,
          child: NiceTreeView<String>(
            nodes: [
              NiceTreeNode(
                key: 'root1',
                data: 'Documents',
                children: [
                  NiceTreeNode(key: 'doc1', data: 'Report.pdf'),
                  NiceTreeNode(key: 'doc2', data: 'Presentation.pptx'),
                ],
              ),
              NiceTreeNode(
                key: 'root2',
                data: 'Images',
                children: [
                  NiceTreeNode(key: 'img1', data: 'Photo.jpg'),
                  NiceTreeNode(key: 'img2', data: 'Logo.png'),
                ],
              ),
            ],
            labelBuilder: (node) => node.data,
          ),
        ),
      ],
    );
  }
}

// === FEEDBACK SECTION ===
class FeedbackSection extends StatelessWidget {
  const FeedbackSection({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        _SectionTitle('Toast'),
        NiceButton(
          label: 'Show Toast',
          onPressed: () {
            showNiceToast(
              context,
              message: 'This is a toast notification!',
              severity: NiceToastSeverity.success,
            );
          },
        ),
        const SizedBox(height: 24),
        _SectionTitle('Dialog'),
        NiceButton(
          label: 'Show Dialog',
          onPressed: () async {
            final result = await showNiceDialog(
              context,
              title: 'Confirm Action',
              message: 'Are you sure you want to continue?',
              confirmLabel: 'Yes',
              cancelLabel: 'No',
            );
            if (context.mounted) {
              showNiceToast(
                context,
                message: result ? 'Confirmed!' : 'Cancelled',
                severity: result ? NiceToastSeverity.success : NiceToastSeverity.info,
              );
            }
          },
        ),
        const SizedBox(height: 24),
        _SectionTitle('Loading States'),
        Wrap(
          spacing: 16,
          runSpacing: 8,
          children: [
            const SizedBox(
              width: 100,
              child: Column(
                children: [
                  NiceSpinner(),
                  SizedBox(height: 8),
                  Text('Spinner'),
                ],
              ),
            ),
            const SizedBox(
              width: 100,
              child: Column(
                children: [
                  NiceSkeleton(
                    width: 80,
                    height: 80,
                    borderRadius: 8,
                  ),
                  SizedBox(height: 8),
                  Text('Skeleton'),
                ],
              ),
            ),
          ],
        ),
        const SizedBox(height: 24),
        _SectionTitle('Alerts'),
        const NiceAlert(
          severity: NiceAlertSeverity.info,
          message: 'This is an info alert.',
        ),
        const SizedBox(height: 8),
        const NiceAlert(
          severity: NiceAlertSeverity.success,
          message: 'This is a success alert.',
        ),
        const SizedBox(height: 8),
        const NiceAlert(
          severity: NiceAlertSeverity.warning,
          message: 'This is a warning alert.',
        ),
        const SizedBox(height: 8),
        const NiceAlert(
          severity: NiceAlertSeverity.error,
          message: 'This is an error alert.',
        ),
        const SizedBox(height: 24),
        _SectionTitle('Empty State'),
        const NiceEmpty(
          icon: Icons.inbox_outlined,
          title: 'No items',
          description: 'There are no items to display.',
        ),
      ],
    );
  }
}

// === FORMS SECTION ===
class FormsSection extends StatefulWidget {
  const FormsSection({super.key});

  @override
  State<FormsSection> createState() => _FormsSectionState();
}

class _FormsSectionState extends State<FormsSection> {
  String _submittedJson = '';

  // JSON Schema example - contact form
  static const Map<String, dynamic> _contactSchema = {
    'type': 'object',
    'properties': {
      'firstName': {
        'type': 'string',
        'title': 'First Name',
        'minLength': 2,
        'maxLength': 50,
      },
      'lastName': {
        'type': 'string',
        'title': 'Last Name',
        'minLength': 2,
        'maxLength': 50,
      },
      'email': {
        'type': 'string',
        'title': 'Email',
        'format': 'email',
      },
      'age': {
        'type': 'integer',
        'title': 'Age',
        'minimum': 18,
        'maximum': 120,
      },
      'country': {
        'title': 'Country',
        'oneOf': [
          {'const': 'PL', 'title': 'Poland'},
          {'const': 'DE', 'title': 'Germany'},
          {'const': 'US', 'title': 'United States'},
          {'const': 'UK', 'title': 'United Kingdom'},
          {'const': 'FR', 'title': 'France'},
        ],
      },
      'newsletter': {
        'type': 'boolean',
        'title': 'Subscribe to newsletter',
        'default': false,
      },
      'bio': {
        'type': 'string',
        'title': 'Bio',
        'maxLength': 500,
      },
    },
    'required': ['firstName', 'lastName', 'email'],
  };

  // UI Schema for custom rendering
  static const Map<String, dynamic> _contactUiSchema = {
    'firstName': {'ui:order': 1},
    'lastName': {'ui:order': 2},
    'email': {'ui:order': 3},
    'age': {'ui:order': 4},
    'country': {'ui:order': 5},
    'newsletter': {'ui:order': 6},
    'bio': {'ui:order': 7, 'ui:widget': 'textarea'},
  };

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        _SectionTitle('JSON Schema Form'),
        const Text(
          'Form automatically generated from JSON Schema (draft-07).\n'
          'Supports validation, custom widgets, and nested objects.',
          style: TextStyle(color: Colors.grey),
        ),
        const SizedBox(height: 16),
        Card(
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: NiceJsonSchemaForm(
              schema: _contactSchema,
              uiSchema: _contactUiSchema,
              columnsPerRow: 2,
              onSubmit: (values) {
                setState(() {
                  _submittedJson = _formatJson(values);
                });
                showNiceToast(
                  context,
                  message: 'Form submitted successfully!',
                  severity: NiceToastSeverity.success,
                );
              },
              submitLabel: 'Submit Form',
            ),
          ),
        ),
        if (_submittedJson.isNotEmpty) ...[
          const SizedBox(height: 24),
          _SectionTitle('Submitted Data'),
          Card(
            color: Colors.grey.shade100,
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: SelectableText(
                _submittedJson,
                style: const TextStyle(
                  fontFamily: 'monospace',
                  fontSize: 12,
                ),
              ),
            ),
          ),
        ],
        const SizedBox(height: 32),
        _SectionTitle('NiceFormBuilder'),
        const Text(
          'Low-code form builder using field definitions.',
          style: TextStyle(color: Colors.grey),
        ),
        const SizedBox(height: 16),
        Card(
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: NiceFormBuilder(
              rows: [
                NiceFormRow(fields: [
                  const NiceFormFieldDef(
                    key: 'companyName',
                    type: NiceFormFieldType.text,
                    label: 'Company Name',
                    required: true,
                  ),
                  const NiceFormFieldDef(
                    key: 'taxId',
                    type: NiceFormFieldType.text,
                    label: 'Tax ID (NIP)',
                    hint: '10 digits',
                  ),
                ]),
                NiceFormRow(fields: [
                  const NiceFormFieldDef(
                    key: 'employees',
                    type: NiceFormFieldType.number,
                    label: 'Number of Employees',
                  ),
                  const NiceFormFieldDef(
                    key: 'founded',
                    type: NiceFormFieldType.date,
                    label: 'Founded Date',
                  ),
                ]),
                NiceFormRow(fields: [
                  const NiceFormFieldDef(
                    key: 'industry',
                    type: NiceFormFieldType.dropdown,
                    label: 'Industry',
                    options: ['Technology', 'Finance', 'Healthcare', 'Manufacturing', 'Retail'],
                  ),
                ]),
                NiceFormRow(fields: [
                  const NiceFormFieldDef(
                    key: 'description',
                    type: NiceFormFieldType.textarea,
                    label: 'Company Description',
                  ),
                ]),
                NiceFormRow(fields: [
                  const NiceFormFieldDef(
                    key: 'active',
                    type: NiceFormFieldType.toggle,
                    label: 'Active',
                    defaultValue: true,
                  ),
                  const NiceFormFieldDef(
                    key: 'verified',
                    type: NiceFormFieldType.checkbox,
                    label: 'Verified Company',
                  ),
                ]),
              ],
              onSubmit: (values) {
                showNiceToast(
                  context,
                  message: 'Company form submitted!',
                  severity: NiceToastSeverity.success,
                );
              },
              submitLabel: 'Save Company',
            ),
          ),
        ),
      ],
    );
  }

  String _formatJson(Map<String, dynamic> data) {
    final buffer = StringBuffer();
    buffer.writeln('{');
    final entries = data.entries.toList();
    for (var i = 0; i < entries.length; i++) {
      final e = entries[i];
      final comma = i < entries.length - 1 ? ',' : '';
      if (e.value is String) {
        buffer.writeln('  "${e.key}": "${e.value}"$comma');
      } else {
        buffer.writeln('  "${e.key}": ${e.value}$comma');
      }
    }
    buffer.write('}');
    return buffer.toString();
  }
}

// Helper widget for section titles
class _SectionTitle extends StatelessWidget {
  final String title;
  const _SectionTitle(this.title);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 12, top: 8),
      child: Text(
        title,
        style: Theme.of(context).textTheme.titleLarge?.copyWith(
              fontWeight: FontWeight.bold,
            ),
      ),
    );
  }
}

// === CHARTS SECTION ===
class ChartsSection extends StatelessWidget {
  const ChartsSection({super.key});

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        _SectionTitle('NiceChart — Line'),
        SizedBox(
          height: 250,
          child: NiceChart(
            type: NiceChartType.line,
            series: [
              NiceChartSeries(
                name: 'Revenue',
                data: [10, 25, 18, 32, 40, 38, 55],
                color: Colors.blue,
              ),
              NiceChartSeries(
                name: 'Costs',
                data: [8, 15, 12, 20, 25, 22, 30],
                color: Colors.red,
              ),
            ],
            categories: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul'],
          ),
        ),
        const SizedBox(height: 24),
        _SectionTitle('NiceChart — Bar'),
        SizedBox(
          height: 250,
          child: NiceChart(
            type: NiceChartType.bar,
            series: [
              NiceChartSeries(
                name: 'Sales',
                data: [45, 62, 38, 71, 55],
                color: Colors.teal,
              ),
            ],
            categories: ['Q1', 'Q2', 'Q3', 'Q4', 'Q5'],
          ),
        ),
        const SizedBox(height: 24),
        _SectionTitle('NicePieChart'),
        SizedBox(
          height: 250,
          child: NicePieChart(
            data: [
              NicePieSlice(label: 'React', value: 45, color: Colors.blue),
              NicePieSlice(label: 'Flutter', value: 30, color: Colors.cyan),
              NicePieSlice(label: 'Angular', value: 15, color: Colors.red),
              NicePieSlice(label: 'Vue', value: 10, color: Colors.green),
            ],
            showLegend: true,
          ),
        ),
        const SizedBox(height: 24),
        _SectionTitle('NiceGauge'),
        Row(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          children: [
            SizedBox(
              width: 120,
              height: 120,
              child: NiceCircularGauge(
                value: 75,
                min: 0,
                max: 100,
                label: 'CPU',
                unit: '%',
              ),
            ),
            SizedBox(
              width: 120,
              height: 120,
              child: NiceCircularGauge(
                value: 42,
                min: 0,
                max: 100,
                label: 'Memory',
                unit: '%',
              ),
            ),
          ],
        ),
      ],
    );
  }
}

// === NAVIGATION SECTION ===
class NavigationSection extends StatefulWidget {
  const NavigationSection({super.key});

  @override
  State<NavigationSection> createState() => _NavigationSectionState();
}

class _NavigationSectionState extends State<NavigationSection> {
  int _tabIndex = 0;
  int _accordionOpen = 0;

  @override
  Widget build(BuildContext context) {
    return ListView(
      padding: const EdgeInsets.all(16),
      children: [
        _SectionTitle('NiceTabs'),
        NiceTabs(
          tabs: const [
            NiceTabItem(label: 'Overview', icon: Icons.dashboard),
            NiceTabItem(label: 'Details', icon: Icons.info),
            NiceTabItem(label: 'Settings', icon: Icons.settings),
          ],
          selectedIndex: _tabIndex,
          onChanged: (index) => setState(() => _tabIndex = index),
        ),
        Container(
          padding: const EdgeInsets.all(16),
          decoration: BoxDecoration(
            border: Border.all(color: Colors.grey.shade300),
            borderRadius: const BorderRadius.only(
              bottomLeft: Radius.circular(8),
              bottomRight: Radius.circular(8),
            ),
          ),
          child: Text(
            'Content for tab ${_tabIndex + 1}',
            style: Theme.of(context).textTheme.bodyLarge,
          ),
        ),
        const SizedBox(height: 24),
        _SectionTitle('NiceBreadcrumb'),
        NiceBreadcrumb(
          items: const [
            NiceBreadcrumbItem(label: 'Home', icon: Icons.home),
            NiceBreadcrumbItem(label: 'Products'),
            NiceBreadcrumbItem(label: 'Electronics'),
            NiceBreadcrumbItem(label: 'Laptops'),
          ],
          onItemTap: (index) {
            showNiceToast(context, message: 'Navigate to item $index');
          },
        ),
        const SizedBox(height: 24),
        _SectionTitle('NiceAccordion'),
        NiceAccordion(
          items: [
            NiceAccordionItem(
              title: 'What is NiceToDev?',
              content: const Padding(
                padding: EdgeInsets.all(16),
                child: Text(
                  'NiceToDev UI is a comprehensive component library with 160+ '
                  'production-grade widgets for React and Flutter.',
                ),
              ),
            ),
            NiceAccordionItem(
              title: 'How to get started?',
              content: const Padding(
                padding: EdgeInsets.all(16),
                child: Text(
                  'Install with: flutter pub add nice2dev_flutter_ui\n'
                  'Import and use any widget in your app.',
                ),
              ),
            ),
            NiceAccordionItem(
              title: 'Is it production-ready?',
              content: const Padding(
                padding: EdgeInsets.all(16),
                child: Text(
                  'Yes! v1.0.1 is production-ready with full i18n (12 languages), '
                  'theming, accessibility, and comprehensive test coverage.',
                ),
              ),
            ),
          ],
        ),
        const SizedBox(height: 24),
        _SectionTitle('NiceMenu'),
        NiceMenu(
          items: const [
            NiceMenuItem(label: 'Dashboard', icon: Icons.dashboard),
            NiceMenuItem(label: 'Users', icon: Icons.people),
            NiceMenuItem(
              label: 'Settings',
              icon: Icons.settings,
              children: [
                NiceMenuItem(label: 'General', icon: Icons.tune),
                NiceMenuItem(label: 'Security', icon: Icons.security),
                NiceMenuItem(label: 'Notifications', icon: Icons.notifications),
              ],
            ),
            NiceMenuItem(label: 'Reports', icon: Icons.bar_chart),
          ],
          onItemTap: (item) {
            showNiceToast(context, message: 'Selected: ${item.label}');
          },
        ),
      ],
    );
  }
}
0
likes
55
points
6
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

NiceToDev Flutter UI — A comprehensive set of enterprise-grade widgets for building ERP applications. Counterpart of @nice2dev/ui for Flutter.

Homepage
Repository (GitHub)
View/report issues

Topics

#ui #widget #erp #enterprise #components

License

MIT (license)

Dependencies

flutter, intl

More

Packages that depend on nice2dev_flutter_ui