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

A comprehensive Flutter UI component library built on top of ForUI with styled buttons, dialogs, tiles, cards, and input controls.

example/lib/main.dart

import 'package:flutter/foundation.dart' show defaultTargetPlatform;
import 'package:flutter/material.dart';
import 'package:v360_ui/v360_ui.dart';

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

/// Root widget wiring up the ForUI theme that every `v360_ui` component needs.
///
/// `v360_ui` re-exports `package:forui/forui.dart`, so [FTheme], [FThemes],
/// [FScaffold] and [FToaster] are all available from the single
/// `package:v360_ui/v360_ui.dart` import.
class ExampleApp extends StatelessWidget {
  const ExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    final isDesktop = switch (defaultTargetPlatform) {
      TargetPlatform.macOS ||
      TargetPlatform.linux ||
      TargetPlatform.windows => true,
      _ => false,
    };
    final theme = isDesktop
        ? FThemes.zinc.light.desktop
        : FThemes.zinc.light.touch;
    return MaterialApp(
      title: 'v360_ui example',
      debugShowCheckedModeBanner: false,
      localizationsDelegates: FLocalizations.localizationsDelegates,
      supportedLocales: FLocalizations.supportedLocales,
      theme: theme.toApproximateMaterialTheme(),
      builder: (context, child) => FTheme(
        data: theme,
        child: FToaster(child: FTooltipGroup(child: child!)),
      ),
      home: const HomePage(),
    );
  }
}

/// A single scrolling gallery of the most common `v360_ui` components.
class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return FScaffold(
      header: const FHeader(title: Text('v360_ui')),
      child: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          const _Section(
            title: 'Typography',
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              spacing: 4,
              children: [
                VTitle('Large title', size: VTitleSize.large, bold: true),
                VTitle('Medium secondary', variant: VTitleVariant.secondary),
                VDetail('A detail line used for supporting body copy.'),
              ],
            ),
          ),
          const _Section(title: 'Buttons', child: _ButtonsSection()),
          const _Section(
            title: 'Badges',
            child: Wrap(
              spacing: 8,
              runSpacing: 8,
              children: [
                VBadge(child: Text('Info')),
                VBadge(variant: VBadgeVariant.success, child: Text('Success')),
                VBadge(variant: VBadgeVariant.warning, child: Text('Warning')),
                VBadge(variant: VBadgeVariant.danger, child: Text('Danger')),
              ],
            ),
          ),
          const _Section(
            title: 'Alerts',
            child: Column(
              spacing: 12,
              children: [
                VAlert(
                  icon: Icons.info_outline,
                  title: 'Sync complete',
                  subtitle: 'All devices are up to date.',
                ),
                VAlert(
                  icon: Icons.warning_amber_outlined,
                  title: 'Payment failed',
                  subtitle: 'Your transaction could not be processed.',
                  variant: VAlertVariant.destructive,
                ),
              ],
            ),
          ),
          const _Section(title: 'Form controls', child: _FormSection()),
          const _Section(title: 'Card', child: _CardSection()),
          const _Section(
            title: 'Tiles',
            child: VTileGroup(
              label: Text('Connected devices'),
              children: [
                VTile(title: Text('Router'), prefix: Icon(Icons.router)),
                VTile(title: Text('Printer'), prefix: Icon(Icons.print)),
              ],
            ),
          ),
          _Section(
            title: 'Table',
            child: VTable(
              height: 180,
              columnFlex: [1, 2, 1],
              rightColumns: [0],
              centerColumns: [2],
              headers: ['ID', 'Customer', 'Status'],
              rows: [
                ['#1001', 'Alice Johnson', 'Active'],
                ['#1002', 'Bob Smith', 'Inactive'],
                ['#1003', 'Carol White', 'Active'],
              ],
            ),
          ),
          const _Section(
            title: 'Progress & avatar',
            child: Row(
              spacing: 16,
              children: [
                Expanded(child: VProgress(value: 0.65)),
                VProgress(mode: VProgressMode.circularIndeterminate),
                VAvatar(text: 'AB'),
              ],
            ),
          ),
          const _Section(title: 'Dialogs', child: _DialogsSection()),
        ],
      ),
    );
  }
}

/// A labelled block used to separate each part of the gallery.
class _Section extends StatelessWidget {
  const _Section({required this.title, required this.child});

  final String title;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.only(bottom: 24),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          VTitle(title, size: VTitleSize.small, variant: VTitleVariant.secondary),
          const SizedBox(height: 8),
          child,
        ],
      ),
    );
  }
}

class _ButtonsSection extends StatelessWidget {
  const _ButtonsSection();

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      spacing: 8,
      children: [
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: [
            VButton(onPress: () {}, child: const Text('Primary')),
            VButton(
              onPress: () {},
              variant: VButtonVariant.secondary,
              child: const Text('Secondary'),
            ),
            VButton(
              onPress: () {},
              variant: VButtonVariant.destructive,
              child: const Text('Destructive'),
            ),
            VButton(
              onPress: () {},
              variant: VButtonVariant.outline,
              child: const Text('Outline'),
            ),
            VButton(
              onPress: () {},
              variant: VButtonVariant.ghost,
              child: const Text('Ghost'),
            ),
            VButton(
              onPress: () {},
              tooltip: 'Delete',
              prefix: const Icon(Icons.delete_outline),
            ),
          ],
        ),
        VButton(
          onPress: () {},
          fullwidth: true,
          prefix: const Icon(Icons.save_outlined),
          child: const Text('Full-width'),
        ),
      ],
    );
  }
}

class _FormSection extends StatefulWidget {
  const _FormSection();

  @override
  State<_FormSection> createState() => _FormSectionState();
}

class _FormSectionState extends State<_FormSection> {
  final _name = TextEditingController();
  String? _role = 'viewer';
  bool _agreed = false;

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

  @override
  Widget build(BuildContext context) {
    return Column(
      spacing: 12,
      children: [
        VTextField(label: 'Name', controller: _name, hint: 'Jane Doe'),
        VSelect(
          label: 'Role',
          hint: 'Pick a role',
          value: _role,
          items: const {
            'admin': 'Administrator',
            'editor': 'Editor',
            'viewer': 'Viewer',
          },
          onChange: (value) => setState(() => _role = value),
        ),
        VCheckbox(
          label: 'I agree to the terms',
          value: _agreed,
          onChange: (value) => setState(() => _agreed = value),
        ),
      ],
    );
  }
}

class _CardSection extends StatelessWidget {
  const _CardSection();

  @override
  Widget build(BuildContext context) {
    return VCard(
      title: const Text('Gratitude'),
      subtitle: const Text('Readiness to show appreciation and return kindness.'),
      content: const VInfoItem(
        label: 'Status',
        value: 'Processing',
        icon: Icons.hourglass_empty,
      ),
      footer: Row(
        spacing: 8,
        children: [
          Expanded(
            child: VButton(
              onPress: () {},
              variant: VButtonVariant.secondary,
              child: const Text('Cancel'),
            ),
          ),
          Expanded(
            child: VButton(onPress: () {}, child: const Text('Submit')),
          ),
        ],
      ),
    );
  }
}

class _DialogsSection extends StatefulWidget {
  const _DialogsSection();

  @override
  State<_DialogsSection> createState() => _DialogsSectionState();
}

class _DialogsSectionState extends State<_DialogsSection> {
  String _lastResult = 'No dialog opened yet.';

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      spacing: 8,
      children: [
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: [
            VButton(
              onPress: () async {
                final confirmed = await VDailog.confirmDialog(
                  context: context,
                  title: const Text('Delete item?'),
                  body: const Text('This action cannot be undone.'),
                  onConfirm: () {},
                );
                setState(() => _lastResult = 'Confirm dialog: $confirmed');
              },
              variant: VButtonVariant.destructive,
              child: const Text('Confirm dialog'),
            ),
            VButton(
              onPress: () => TextEditDialog.show(
                context,
                title: 'Rename',
                initialValue: 'Untitled',
                onSave: (value) async {
                  setState(() => _lastResult = 'Text dialog saved: $value');
                },
              ),
              child: const Text('Text edit dialog'),
            ),
            VButton(
              onPress: () => NumericEditDialog.show(
                context,
                title: 'Set threshold',
                initialValue: 42,
                suffix: '%',
                onSave: (value) async {
                  setState(() => _lastResult = 'Numeric dialog saved: $value');
                },
              ),
              child: const Text('Numeric edit dialog'),
            ),
          ],
        ),
        VDetail(_lastResult),
      ],
    );
  }
}
0
likes
140
points
66
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A comprehensive Flutter UI component library built on top of ForUI with styled buttons, dialogs, tiles, cards, and input controls.

Repository (GitHub)

License

unknown (license)

Dependencies

flutter, flutter_hooks, forui

More

Packages that depend on v360_ui