voo_forms 0.1.13 copy "voo_forms: ^0.1.13" to clipboard
voo_forms: ^0.1.13 copied to clipboard

A comprehensive cross-platform forms package with atomic design, clean architecture, and Material 3 support for Flutter applications.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:voo_forms/voo_forms.dart';
import 'package:voo_ui_core/voo_ui_core.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return VooDesignSystem(
      data: VooDesignSystemData.defaultSystem,
      child: VooResponsiveBuilder(
        child: MaterialApp(
          title: 'VooForms Example',
          theme: ThemeData(
            colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
            useMaterial3: true,
          ),
          home: const FormExamplePage(),
        ),
      ),
    );
  }
}

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

  @override
  State<FormExamplePage> createState() => _FormExamplePageState();
}

class _FormExamplePageState extends State<FormExamplePage> {
  late VooFormController _controller;
  final Map<String, dynamic> _capturedValues = {};
  
  @override
  void initState() {
    super.initState();
    _initializeForm();
  }
  
  void _initializeForm() {
    final form = VooForm(
      id: 'example_form',
      fields: _createFields(),
      sections: [
        VooFormSection(
          id: 'text_fields',
          title: 'Text Fields',
          subtitle: 'Various text input fields with typed onChanged callbacks',
          fieldIds: ['text', 'email', 'password', 'phone', 'multiline'],
        ),
        VooFormSection(
          id: 'numeric_fields',
          title: 'Numeric Fields',
          subtitle: 'Number and slider fields with proper type conversion',
          fieldIds: ['number', 'slider'],
        ),
        VooFormSection(
          id: 'boolean_fields',
          title: 'Boolean Fields',
          subtitle: 'Switch and checkbox fields with bool callbacks',
          fieldIds: ['switch', 'checkbox'],
        ),
        VooFormSection(
          id: 'selection_fields',
          title: 'Selection Fields',
          subtitle: 'Dropdown and radio fields with typed values',
          fieldIds: ['dropdown', 'dropdown_enum', 'radio'],
        ),
        VooFormSection(
          id: 'date_time_fields',
          title: 'Date & Time Fields',
          subtitle: 'Date and time pickers with proper callbacks',
          fieldIds: ['date', 'time'],
        ),
      ],
    );
    
    _controller = VooFormController(form: form);
  }
  
  List<VooFormField> _createFields() {
    return [
      // Text Fields
      VooField.text(
        name: 'text',
        label: 'Text Field',
        placeholder: 'Enter any text',
        helper: 'This demonstrates a basic text field with String? callback',
        prefixIcon: Icons.text_fields,
        onChanged: (String? value) {
          setState(() {
            _capturedValues['text'] = value;
          });
          debugPrint('Text field changed: $value (type: ${value.runtimeType})');
        },
      ),
      
      VooField.email(
        name: 'email',
        label: 'Email Field',
        placeholder: 'user@example.com',
        helper: 'Email field with validation and typed callback',
        prefixIcon: Icons.email,
        validators: [VooValidator.email()],
        onChanged: (String? value) {
          setState(() {
            _capturedValues['email'] = value;
          });
          debugPrint('Email field changed: $value');
        },
      ),
      
      VooField.password(
        name: 'password',
        label: 'Password Field',
        placeholder: 'Enter password',
        helper: 'Password field with obscured text',
        prefixIcon: Icons.lock,
        validators: [
          VooValidator.required(),
          VooValidator.minLength(8),
        ],
        onChanged: (String? value) {
          setState(() {
            _capturedValues['password'] = value?.isNotEmpty == true ? '***' : null;
          });
          debugPrint('Password field changed: ${value?.replaceAll(RegExp(r'.'), '*')}');
        },
      ),
      
      VooField.phone(
        name: 'phone',
        label: 'Phone Field',
        placeholder: '+1 (555) 123-4567',
        helper: 'Phone number with formatting',
        prefixIcon: Icons.phone,
        onChanged: (String? value) {
          setState(() {
            _capturedValues['phone'] = value;
          });
          debugPrint('Phone field changed: $value');
        },
      ),
      
      VooField.multiline(
        name: 'multiline',
        label: 'Multiline Text',
        placeholder: 'Enter multiple lines of text...',
        helper: 'Supports multiple lines',
        maxLines: 4,
        onChanged: (String? value) {
          setState(() {
            _capturedValues['multiline'] = value;
          });
          debugPrint('Multiline field changed: ${value?.replaceAll('\n', '\\n')}');
        },
      ),
      
      // Numeric Fields
      VooField.number(
        name: 'number',
        label: 'Number Field',
        placeholder: 'Enter a number',
        helper: 'Automatically parses to num type',
        prefixIcon: Icons.numbers,
        validators: [
          VooValidator.min(0),
          VooValidator.max(100),
        ],
        onChanged: (num? value) {
          setState(() {
            _capturedValues['number'] = value;
          });
          debugPrint('Number field changed: $value (type: ${value.runtimeType})');
        },
      ),
      
      VooField.slider(
        name: 'slider',
        label: 'Slider Field',
        helper: 'Drag to select a value',
        min: 0,
        max: 100,
        divisions: 20,
        initialValue: 50,
        onChanged: (double? value) {
          setState(() {
            _capturedValues['slider'] = value?.toStringAsFixed(1);
          });
          debugPrint('Slider field changed: $value');
        },
      ),
      
      // Boolean Fields
      VooField.boolean(
        name: 'switch',
        label: 'Switch Field',
        helper: 'Toggle on/off with bool callback',
        initialValue: false,
        onChanged: (bool? value) {
          setState(() {
            _capturedValues['switch'] = value;
          });
          debugPrint('Switch field changed: $value');
        },
      ),
      
      VooField.checkbox(
        name: 'checkbox',
        label: 'Checkbox Field',
        helper: 'Check/uncheck with bool callback',
        initialValue: false,
        onChanged: (bool? value) {
          setState(() {
            _capturedValues['checkbox'] = value;
          });
          debugPrint('Checkbox field changed: $value');
        },
      ),
      
      // Selection Fields
      VooField.dropdown<String>(
        name: 'dropdown',
        label: 'String Dropdown',
        helper: 'Dropdown with String values',
        prefixIcon: Icons.arrow_drop_down,
        options: ['Option A', 'Option B', 'Option C'],
        converter: (String option) => VooDropdownChild(
          label: option,
          value: option,
        ),
        onChanged: (String? value) {
          setState(() {
            _capturedValues['dropdown'] = value;
          });
          debugPrint('Dropdown field changed: $value');
        },
      ),
      
      VooField.dropdown<ExampleEnum>(
        name: 'dropdown_enum',
        label: 'Enum Dropdown',
        helper: 'Dropdown with typed enum values',
        prefixIcon: Icons.category,
        options: ExampleEnum.values,
        converter: (ExampleEnum value) => VooDropdownChild(
          label: value.displayName,
          value: value,
        ),
        onChanged: (ExampleEnum? value) {
          setState(() {
            _capturedValues['dropdown_enum'] = value?.displayName;
          });
          debugPrint('Enum dropdown changed: $value');
        },
      ),
      
      VooField.radio(
        name: 'radio',
        label: 'Radio Field',
        helper: 'Select one option',
        options: const ['Small', 'Medium', 'Large'],
        onChanged: (dynamic value) {
          setState(() {
            _capturedValues['radio'] = value;
          });
          debugPrint('Radio field changed: $value');
        },
      ),
      
      // Date & Time Fields
      VooField.date(
        name: 'date',
        label: 'Date Field',
        placeholder: 'Select a date',
        helper: 'Date picker with DateTime callback',
        prefixIcon: Icons.calendar_today,
        onChanged: (DateTime? value) {
          setState(() {
            _capturedValues['date'] = value?.toString().split(' ')[0];
          });
          debugPrint('Date field changed: $value');
        },
      ),
      
      VooField.time(
        name: 'time',
        label: 'Time Field',
        placeholder: 'Select a time',
        helper: 'Time picker with TimeOfDay callback',
        prefixIcon: Icons.access_time,
        onChanged: (TimeOfDay? value) {
          setState(() {
            _capturedValues['time'] = value?.format(context);
          });
          debugPrint('Time field changed: $value');
        },
      ),
    ];
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('VooForms Example'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: Row(
        children: [
          // Form Section
          Expanded(
            flex: 2,
            child: SingleChildScrollView(
              padding: const EdgeInsets.all(16.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Form Fields with Typed onChanged Callbacks',
                    style: Theme.of(context).textTheme.headlineSmall,
                  ),
                  const SizedBox(height: 8),
                  Text(
                    'This example demonstrates all field types with properly typed onChanged callbacks.',
                    style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                      color: Theme.of(context).colorScheme.onSurfaceVariant,
                    ),
                  ),
                  const SizedBox(height: 24),
                  VooFormBuilder(
                    form: _controller.form,
                    controller: _controller,
                  ),
                  const SizedBox(height: 24),
                  Row(
                    children: [
                      ElevatedButton(
                        onPressed: () {
                          if (_controller.validate()) {
                            final values = _controller.values;
                            ScaffoldMessenger.of(context).showSnackBar(
                              SnackBar(
                                content: Text('Form is valid! Values: $values'),
                                backgroundColor: Colors.green,
                              ),
                            );
                          } else {
                            ScaffoldMessenger.of(context).showSnackBar(
                              const SnackBar(
                                content: Text('Please fix validation errors'),
                                backgroundColor: Colors.red,
                              ),
                            );
                          }
                        },
                        child: const Text('Validate & Submit'),
                      ),
                      const SizedBox(width: 8),
                      TextButton(
                        onPressed: () {
                          _controller.reset();
                          setState(() {
                            _capturedValues.clear();
                          });
                        },
                        child: const Text('Reset Form'),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ),
          
          // Live Values Panel
          Container(
            width: 1,
            color: Theme.of(context).dividerColor,
          ),
          Expanded(
            flex: 1,
            child: Container(
              color: Theme.of(context).colorScheme.surfaceContainerHighest.withValues(alpha: 0.3),
              padding: const EdgeInsets.all(16.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Live onChanged Values',
                    style: Theme.of(context).textTheme.titleLarge,
                  ),
                  const SizedBox(height: 8),
                  Text(
                    'Values captured by onChanged callbacks:',
                    style: Theme.of(context).textTheme.bodySmall,
                  ),
                  const Divider(height: 24),
                  Expanded(
                    child: _capturedValues.isEmpty
                        ? Center(
                            child: Text(
                              'Start typing to see values...',
                              style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                                color: Theme.of(context).colorScheme.onSurfaceVariant,
                              ),
                            ),
                          )
                        : ListView(
                            children: _capturedValues.entries.map((entry) {
                              return Padding(
                                padding: const EdgeInsets.symmetric(vertical: 4.0),
                                child: Row(
                                  crossAxisAlignment: CrossAxisAlignment.start,
                                  children: [
                                    SizedBox(
                                      width: 120,
                                      child: Text(
                                        '${entry.key}:',
                                        style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                                          fontWeight: FontWeight.bold,
                                        ),
                                      ),
                                    ),
                                    Expanded(
                                      child: Text(
                                        entry.value?.toString() ?? 'null',
                                        style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                                          fontFamily: 'monospace',
                                          color: entry.value != null
                                              ? Theme.of(context).colorScheme.primary
                                              : Theme.of(context).colorScheme.onSurfaceVariant,
                                        ),
                                      ),
                                    ),
                                  ],
                                ),
                              );
                            }).toList(),
                          ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
  
  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }
}

enum ExampleEnum {
  small('Small Size'),
  medium('Medium Size'),
  large('Large Size');
  
  final String displayName;
  const ExampleEnum(this.displayName);
}
2
likes
0
points
2.14k
downloads

Publisher

verified publishervoostack.com

Weekly Downloads

A comprehensive cross-platform forms package with atomic design, clean architecture, and Material 3 support for Flutter applications.

Homepage
Repository (GitHub)
View/report issues

Topics

#flutter #form

License

unknown (license)

Dependencies

collection, equatable, flutter, flutter_hooks, intl, voo_ui_core

More

Packages that depend on voo_forms