auto_form_builder 1.0.0 copy "auto_form_builder: ^1.0.0" to clipboard
auto_form_builder: ^1.0.0 copied to clipboard

Auto Form Builder is a robust Flutter package for building dynamic, configuration-driven forms with advanced features such as multi-step wizards, conditional field visibility, computed fields, input m [...]

example/lib/main.dart

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Auto Form Builder Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      darkTheme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.blue,
          brightness: Brightness.dark,
        ),
        useMaterial3: true,
      ),
      home: const HomePage(),
    );
  }
}

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int _selectedIndex = 0;

  final List<Widget> _pages = [
    const SimpleFormPage(),
    const AdvancedFormPage(),
    const WizardFormPage(),
    const ConditionalFormPage(),
    const FormThemingPage(),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Auto Form Builder Demo'),
        backgroundColor: Theme.of(context).colorScheme.primaryContainer,
      ),
      drawer: Drawer(
        child: ListView(
          padding: EdgeInsets.zero,
          children: [
            DrawerHeader(
              decoration: BoxDecoration(
                color: Theme.of(context).colorScheme.primaryContainer,
              ),
              child: const Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Auto Form Builder',
                    style: TextStyle(
                      fontSize: 24,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  SizedBox(height: 8),
                  Text('Powerful forms with minimal code'),
                ],
              ),
            ),
            ListTile(
              leading: const Icon(Icons.text_fields),
              title: const Text('Simple Form'),
              selected: _selectedIndex == 0,
              onTap: () {
                setState(() {
                  _selectedIndex = 0;
                });
                Navigator.pop(context);
              },
            ),
            ListTile(
              leading: const Icon(Icons.design_services),
              title: const Text('Advanced Form'),
              selected: _selectedIndex == 1,
              onTap: () {
                setState(() {
                  _selectedIndex = 1;
                });
                Navigator.pop(context);
              },
            ),
            ListTile(
              leading: const Icon(Icons.list_alt),
              title: const Text('Multi-Step Wizard'),
              selected: _selectedIndex == 2,
              onTap: () {
                setState(() {
                  _selectedIndex = 2;
                });
                Navigator.pop(context);
              },
            ),
            ListTile(
              leading: const Icon(Icons.visibility),
              title: const Text('Conditional Fields'),
              selected: _selectedIndex == 3,
              onTap: () {
                setState(() {
                  _selectedIndex = 3;
                });
                Navigator.pop(context);
              },
            ),
            ListTile(
              leading: const Icon(Icons.color_lens),
              title: const Text('Theming'),
              selected: _selectedIndex == 4,
              onTap: () {
                setState(() {
                  _selectedIndex = 4;
                });
                Navigator.pop(context);
              },
            ),
            const Divider(),
            const Padding(
              padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
              child: Text(
                'Auto Form Builder v0.1.0',
                style: TextStyle(fontSize: 12, color: Colors.grey),
              ),
            ),
          ],
        ),
      ),
      body: _pages[_selectedIndex],
    );
  }
}

// 1. Simple Form Example
class SimpleFormPage extends StatefulWidget {
  const SimpleFormPage({super.key});

  @override
  State<SimpleFormPage> createState() => _SimpleFormPageState();
}

class _SimpleFormPageState extends State<SimpleFormPage> {
  final _formController = AutoFormController();
  String _result = '';

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

  @override
  Widget build(BuildContext context) {
    // Simple contact form configuration
    final formConfig = FormConfig(
      fields: [
        FormFieldConfig(
          name: 'name',
          type: FormFieldType.text,
          label: 'Full Name',
          hint: 'Enter your full name',
          validators: [
            FieldValidator(type: 'required', message: 'Name is required'),
          ],
        ),
        FormFieldConfig(
          name: 'email',
          type: FormFieldType.text,
          label: 'Email Address',
          hint: 'Enter your email address',
          validators: [
            FieldValidator(type: 'required', message: 'Email is required'),
            FieldValidator(type: 'email', message: 'Enter a valid email address'),
          ],
        ),
        FormFieldConfig(
          name: 'phone',
          type: FormFieldType.text,
          label: 'Phone Number',
          hint: 'Enter your phone number',
          mask: '###-###-####',
        ),
        FormFieldConfig(
          name: 'message',
          type: FormFieldType.text,
          label: 'Message',
          hint: 'Enter your message',
        ),
      ],
      submitButtonText: 'Send Message',
    );

    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text(
            'Simple Contact Form',
            style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 8),
          const Text(
            'This example shows a basic form with validation and masking.',
            style: TextStyle(fontSize: 16, color: Colors.grey),
          ),
          const SizedBox(height: 24),
          Expanded(
            child: AutoForm(
              config: formConfig,
              controller: _formController,
              validateOnChange: true,
              onSubmit: (data) async {
                // Simulate API call
                await Future.delayed(const Duration(seconds: 1));
                setState(() {
                  _result = 'Form submitted with data:\n${data.entries.map((e) => '${e.key}: ${e.value}').join('\n')}';
                });
                // Don't return the data when Future<void> is expected
              },
              showLoadingDuringSubmission: true,
              useCardContainer: true,
            ),
          ),
          if (_result.isNotEmpty)
            Container(
              margin: const EdgeInsets.only(top: 16),
              padding: const EdgeInsets.all(16),
              decoration: BoxDecoration(
                color: Colors.green.withAlpha(25), // 0.1 opacity ≈ alpha 25
                borderRadius: BorderRadius.circular(8),
                border: Border.all(color: Colors.green),
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const Text(
                    'Success!',
                    style: TextStyle(
                      fontWeight: FontWeight.bold,
                      color: Colors.green,
                    ),
                  ),
                  const SizedBox(height: 8),
                  Text(_result),
                ],
              ),
            ),
        ],
      ),
    );
  }
}

// 2. Advanced Form Example
class AdvancedFormPage extends StatefulWidget {
  const AdvancedFormPage({super.key});

  @override
  State<AdvancedFormPage> createState() => _AdvancedFormPageState();
}

class _AdvancedFormPageState extends State<AdvancedFormPage> {
  final _formController = AutoFormController();

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

  @override
  Widget build(BuildContext context) {
    // Advanced form with multiple field types and computed values
    final formConfig = FormConfig(
      formId: 'advanced_form',
      autoSave: true,
      fields: [
        FormFieldConfig(
          name: 'title',
          type: FormFieldType.dropdown,
          label: 'Title',
          options: [
            FieldOption(label: 'Mr.', value: 'Mr.'),
            FieldOption(label: 'Mrs.', value: 'Mrs.'),
            FieldOption(label: 'Ms.', value: 'Ms.'),
            FieldOption(label: 'Dr.', value: 'Dr.'),
          ],
        ),
        FormFieldConfig(
          name: 'firstName',
          type: FormFieldType.text,
          label: 'First Name',
          validators: [
            FieldValidator(type: 'required', message: 'First name is required'),
          ],
        ),
        FormFieldConfig(
          name: 'lastName',
          type: FormFieldType.text,
          label: 'Last Name',
          validators: [
            FieldValidator(type: 'required', message: 'Last name is required'),
          ],
        ),
        FormFieldConfig(
          name: 'fullName',
          type: FormFieldType.text,
          label: 'Full Name',
          computeFrom: ['title', 'firstName', 'lastName'],
          computeValue: (data) {
            final title = data['title'] ?? '';
            final firstName = data['firstName'] ?? '';
            final lastName = data['lastName'] ?? '';
            return '$title $firstName $lastName'.trim();
          },
        ),
        FormFieldConfig(
          name: 'birthDate',
          type: FormFieldType.datePicker,
          label: 'Date of Birth',
        ),
        FormFieldConfig(
          name: 'newsletterOptIn',
          type: FormFieldType.checkbox,
          label: 'Subscribe to newsletter',
          defaultValue: true,
        ),
        FormFieldConfig(
          name: 'acceptTerms',
          type: FormFieldType.checkbox,
          label: 'I accept the terms and conditions',
          validators: [
            FieldValidator(
              type: 'required',
              message: 'You must accept the terms to continue',
            ),
          ],
        ),
      ],
      layout: FormLayout.grid,
      gridColumns: 2,
      submitButtonText: 'Register',
    );

    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text(
            'Advanced Form',
            style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 8),
          const Text(
            'Demonstrates multiple field types, computed values, and auto-saving drafts.',
            style: TextStyle(fontSize: 16, color: Colors.grey),
          ),
          const SizedBox(height: 24),
          Expanded(
            child: AutoForm(
              config: formConfig,
              controller: _formController,
              onSubmit: (data) async {
                // Simulate API call
                await Future.delayed(const Duration(seconds: 1));
                
                // Show success dialog
                if (context.mounted) {
                  showDialog(
                    context: context,
                    builder: (context) => AlertDialog(
                      title: const Text('Registration Complete'),
                      content: SingleChildScrollView(
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            const Text('Thank you for registering!'),
                            const SizedBox(height: 16),
                            const Text('Submitted data:'),
                            const SizedBox(height: 8),
                            ...data.entries.map((e) => Text('${e.key}: ${e.value}')),
                          ],
                        ),
                      ),
                      actions: [
                        TextButton(
                          onPressed: () {
                            Navigator.of(context).pop();
                            _formController.resetForm();
                          },
                          child: const Text('OK'),
                        ),
                      ],
                    ),
                  );
                }
              },
              showResetButton: true,
              useCardContainer: true,
            ),
          ),
        ],
      ),
    );
  }
}

// 3. Wizard Form Example
class WizardFormPage extends StatefulWidget {
  const WizardFormPage({super.key});

  @override
  State<WizardFormPage> createState() => _WizardFormPageState();
}

class _WizardFormPageState extends State<WizardFormPage> {
  final _formController = AutoFormController();

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

  @override
  Widget build(BuildContext context) {
    // Multi-step wizard form configuration
    final personalInfoSection = FormConfig(
      sectionTitle: 'Personal Information',
      description: 'Please provide your basic personal information',
      icon: Icons.person,
      fields: [
        FormFieldConfig(
          name: 'firstName',
          type: FormFieldType.text,
          label: 'First Name',
          validators: [
            FieldValidator(type: 'required', message: 'First name is required'),
          ],
        ),
        FormFieldConfig(
          name: 'lastName',
          type: FormFieldType.text,
          label: 'Last Name',
          validators: [
            FieldValidator(type: 'required', message: 'Last name is required'),
          ],
        ),
        FormFieldConfig(
          name: 'email',
          type: FormFieldType.text,
          label: 'Email',
          validators: [
            FieldValidator(type: 'required', message: 'Email is required'),
            FieldValidator(type: 'email', message: 'Enter a valid email'),
          ],
        ),
      ],
    );

    final addressSection = FormConfig(
      sectionTitle: 'Address',
      description: 'Please provide your address details',
      icon: Icons.home,
      fields: [
        FormFieldConfig(
          name: 'street',
          type: FormFieldType.text,
          label: 'Street Address',
          validators: [
            FieldValidator(type: 'required', message: 'Street address is required'),
          ],
        ),
        FormFieldConfig(
          name: 'city',
          type: FormFieldType.text,
          label: 'City',
          validators: [
            FieldValidator(type: 'required', message: 'City is required'),
          ],
        ),
        FormFieldConfig(
          name: 'state',
          type: FormFieldType.dropdown,
          label: 'State',
          options: [
            FieldOption(label: 'California', value: 'CA'),
            FieldOption(label: 'New York', value: 'NY'),
            FieldOption(label: 'Texas', value: 'TX'),
            FieldOption(label: 'Florida', value: 'FL'),
            // Add more states
          ],
          validators: [
            FieldValidator(type: 'required', message: 'State is required'),
          ],
        ),
        FormFieldConfig(
          name: 'zipCode',
          type: FormFieldType.text,
          label: 'ZIP Code',
          validators: [
            FieldValidator(type: 'required', message: 'ZIP code is required'),
          ],
        ),
      ],
    );

    final preferencesSection = FormConfig(
      sectionTitle: 'Preferences',
      description: 'Tell us about your preferences',
      icon: Icons.settings,
      fields: [
        FormFieldConfig(
          name: 'preferredContact',
          type: FormFieldType.dropdown,
          label: 'Preferred Contact Method',
          options: [
            FieldOption(label: 'Email', value: 'email'),
            FieldOption(label: 'Phone', value: 'phone'),
            FieldOption(label: 'Mail', value: 'mail'),
          ],
        ),
        FormFieldConfig(
          name: 'receiveUpdates',
          type: FormFieldType.checkbox,
          label: 'Receive Updates',
          defaultValue: true,
        ),
        FormFieldConfig(
          name: 'privacyPolicy',
          type: FormFieldType.checkbox,
          label: 'I agree to the Privacy Policy',
          validators: [
            FieldValidator(type: 'required', message: 'You must agree to the privacy policy'),
          ],
        ),
      ],
    );

    final wizardConfig = FormConfig(
      formId: 'wizard_form',
      autoSave: true,
      fields: [], // Required parameter even when using sections
      sections: [personalInfoSection, addressSection, preferencesSection],
      submitButtonText: 'Complete',
    );

    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text(
            'Multi-Step Form Wizard',
            style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 8),
          const Text(
            'This example shows a multi-step form with progress tracking.',
            style: TextStyle(fontSize: 16, color: Colors.grey),
          ),
          const SizedBox(height: 24),
          Expanded(
            child: AutoFormWizard(
              steps: wizardConfig.sections!,
              controller: _formController,
              formId: wizardConfig.formId,
              autosaveProgress: wizardConfig.autoSave,
              showStepIndicator: true,
              stepTitles: wizardConfig.sections!.map((s) => s.sectionTitle ?? '').toList(),
              validationMode: StepValidationMode.onNext,
              animateTransitions: true,
              showSummaryStep: true,
              submitButtonText: wizardConfig.submitButtonText ?? 'Complete',
              onComplete: (data) async {
                // Simulate API call
                await Future.delayed(const Duration(seconds: 1));
                
                // Show success snackbar
                if (context.mounted) {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(
                      content: const Text('Form submitted successfully!'),
                      backgroundColor: Colors.green,
                      duration: const Duration(seconds: 3),
                      action: SnackBarAction(
                        label: 'View',
                        textColor: Colors.white,
                        onPressed: () {
                          // Show form data dialog
                          showDialog(
                            context: context,
                            builder: (context) => AlertDialog(
                              title: const Text('Submitted Data'),
                              content: SingleChildScrollView(
                                child: Column(
                                  crossAxisAlignment: CrossAxisAlignment.start,
                                  mainAxisSize: MainAxisSize.min,
                                  children: data.entries.map((e) => 
                                    Padding(
                                      padding: const EdgeInsets.symmetric(vertical: 4.0),
                                      child: Text('${e.key}: ${e.value}'),
                                    )
                                  ).toList(),
                                ),
                              ),
                              actions: [
                                TextButton(
                                  onPressed: () => Navigator.of(context).pop(),
                                  child: const Text('Close'),
                                ),
                              ],
                            ),
                          );
                        },
                      ),
                    ),
                  );
                }
              },
            ),
          ),
        ],
      ),
    );
  }
}

// 4. Conditional Form Example
class ConditionalFormPage extends StatefulWidget {
  const ConditionalFormPage({super.key});

  @override
  State<ConditionalFormPage> createState() => _ConditionalFormPageState();
}

class _ConditionalFormPageState extends State<ConditionalFormPage> {
  final _formController = AutoFormController();

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

  @override
  Widget build(BuildContext context) {
    // Form with conditional visibility based on field values
    final formConfig = FormConfig(
      fields: [
        FormFieldConfig(
          name: 'hasAccount',
          type: FormFieldType.dropdown,
          label: 'Do you have an account?',
          options: [
            FieldOption(label: 'Yes', value: 'yes'),
            FieldOption(label: 'No', value: 'no'),
          ],
          defaultValue: 'no',
        ),
        // Fields shown only when hasAccount = 'yes'
        FormFieldConfig(
          name: 'username',
          type: FormFieldType.text,
          label: 'Username',
          validators: [
            FieldValidator(type: 'required', message: 'Username is required'),
          ],
          visibleWhen: [
            VisibilityCondition(
              fieldName: 'hasAccount',
              operator: 'equals',
              value: 'yes',
            ),
          ],
        ),
        FormFieldConfig(
          name: 'password',
          type: FormFieldType.text,
          label: 'Password',
          validators: [
            FieldValidator(type: 'required', message: 'Password is required'),
          ],
          visibleWhen: [
            VisibilityCondition(
              fieldName: 'hasAccount',
              operator: 'equals',
              value: 'yes',
            ),
          ],
        ),
        // Fields shown only when hasAccount = 'no'
        FormFieldConfig(
          name: 'contactMethod',
          type: FormFieldType.dropdown,
          label: 'Preferred Contact Method',
          options: [
            FieldOption(label: 'Email', value: 'email'),
            FieldOption(label: 'Phone', value: 'phone'),
          ],
          visibleWhen: [
            VisibilityCondition(
              fieldName: 'hasAccount',
              operator: 'equals',
              value: 'no',
            ),
          ],
        ),
        // Email field shown when contactMethod = 'email'
        FormFieldConfig(
          name: 'email',
          type: FormFieldType.text,
          label: 'Email Address',
          validators: [
            FieldValidator(type: 'required', message: 'Email is required'),
            FieldValidator(type: 'email', message: 'Enter a valid email'),
          ],
          visibleWhen: [
            VisibilityCondition(
              fieldName: 'hasAccount',
              operator: 'equals',
              value: 'no',
            ),
            VisibilityCondition(
              fieldName: 'contactMethod',
              operator: 'equals',
              value: 'email',
            ),
          ],
        ),
        // Phone field shown when contactMethod = 'phone'
        FormFieldConfig(
          name: 'phone',
          type: FormFieldType.text,
          label: 'Phone Number',
          mask: '###-###-####',
          validators: [
            FieldValidator(type: 'required', message: 'Phone number is required'),
          ],
          visibleWhen: [
            VisibilityCondition(
              fieldName: 'hasAccount',
              operator: 'equals',
              value: 'no',
            ),
            VisibilityCondition(
              fieldName: 'contactMethod',
              operator: 'equals',
              value: 'phone',
            ),
          ],
        ),
      ],
      submitButtonText: 'Continue',
    );

    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text(
            'Conditional Fields',
            style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 8),
          const Text(
            'Fields appear or disappear based on the values of other fields.',
            style: TextStyle(fontSize: 16, color: Colors.grey),
          ),
          const SizedBox(height: 24),
          Expanded(
            child: AutoForm(
              config: formConfig,
              controller: _formController,
              validateOnChange: true,
              onSubmit: (data) async {
                await Future.delayed(const Duration(seconds: 1));
                if (context.mounted) {
                  showDialog(
                    context: context,
                    builder: (context) => AlertDialog(
                      title: const Text('Form Submitted'),
                      content: SingleChildScrollView(
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          mainAxisSize: MainAxisSize.min,
                          children: data.entries
                              .where((e) => e.value != null)
                              .map((e) => Text('${e.key}: ${e.value}'))
                              .toList(),
                        ),
                      ),
                      actions: [
                        TextButton(
                          onPressed: () {
                            Navigator.of(context).pop();
                          },
                          child: const Text('OK'),
                        ),
                      ],
                    ),
                  );
                }
                // Don't return anything for Future<void>
              },
              useCardContainer: true,
            ),
          ),
        ],
      ),
    );
  }
}

// 5. Theming Example
class FormThemingPage extends StatefulWidget {
  const FormThemingPage({super.key});

  @override
  State<FormThemingPage> createState() => _FormThemingPageState();
}

class _FormThemingPageState extends State<FormThemingPage> {
  final _formController = AutoFormController();
  bool _useDarkTheme = false;
  FormTheme _customTheme = FormTheme();

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

  @override
  Widget build(BuildContext context) {
    // Basic form for theme demonstration
    final formConfig = FormConfig(
      fields: [
        FormFieldConfig(
          name: 'name',
          type: FormFieldType.text,
          label: 'Full Name',
          validators: [
            FieldValidator(type: 'required', message: 'Name is required'),
          ],
        ),
        FormFieldConfig(
          name: 'email',
          type: FormFieldType.text,
          label: 'Email Address',
          validators: [
            FieldValidator(type: 'required', message: 'Email is required'),
            FieldValidator(type: 'email', message: 'Enter a valid email'),
          ],
        ),
        FormFieldConfig(
          name: 'category',
          type: FormFieldType.dropdown,
          label: 'Category',
          options: [
            FieldOption(label: 'Business', value: 'business'),
            FieldOption(label: 'Personal', value: 'personal'),
            FieldOption(label: 'Education', value: 'education'),
          ],
        ),
        FormFieldConfig(
          name: 'rememberMe',
          type: FormFieldType.checkbox,
          label: 'Remember me',
          defaultValue: true,
        ),
      ],
      submitButtonText: 'Submit Form',
    );

    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const Text(
            'Form Theming',
            style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
          ),
          const SizedBox(height: 8),
          const Text(
            'Customize the appearance of your forms with themes.',
            style: TextStyle(fontSize: 16, color: Colors.grey),
          ),
          const SizedBox(height: 16),
          
          // Theme controls
          Card(
            child: Padding(
              padding: const EdgeInsets.all(16.0),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const Text(
                    'Theme Options',
                    style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                  ),
                  const SizedBox(height: 8),
                  Row(
                    children: [
                      const Text('Use Dark Theme:'),
                      Switch(
                        value: _useDarkTheme,
                        onChanged: (value) {
                          setState(() {
                            _useDarkTheme = value;
                            _updateTheme();
                          });
                        },
                      ),
                    ],
                  ),
                  const SizedBox(height: 8),
                  Row(
                    children: [
                      const Text('Primary Color:'),
                      const SizedBox(width: 8),
                      PopupMenuButton<Color>(
                        child: Container(
                          width: 30,
                          height: 30,
                          decoration: BoxDecoration(
                            color: _customTheme.primaryColor,
                            border: Border.all(color: Colors.grey),
                            borderRadius: BorderRadius.circular(4),
                          ),
                        ),
                        itemBuilder: (context) => [
                          PopupMenuItem(
                            value: Colors.blue,
                            child: colorOption(Colors.blue),
                          ),
                          PopupMenuItem(
                            value: Colors.red,
                            child: colorOption(Colors.red),
                          ),
                          PopupMenuItem(
                            value: Colors.green,
                            child: colorOption(Colors.green),
                          ),
                          PopupMenuItem(
                            value: Colors.purple,
                            child: colorOption(Colors.purple),
                          ),
                          PopupMenuItem(
                            value: Colors.orange,
                            child: colorOption(Colors.orange),
                          ),
                        ],
                        onSelected: (color) {
                          setState(() {
                            _customTheme = _customTheme.copyWith(
                              primaryColor: color,
                              successColor: HSLColor.fromColor(color)
                                  .withLightness((HSLColor.fromColor(color).lightness - 0.2).clamp(0.0, 1.0))
                                  .toColor(),
                            );
                          });
                        },
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ),
          
          const SizedBox(height: 16),
          
          // Form with applied theme
          Expanded(
            child: AutoForm(
              config: formConfig,
              controller: _formController,
              theme: _customTheme,
              onSubmit: (data) async {
                await Future.delayed(const Duration(seconds: 1));
                if (context.mounted) {
                  ScaffoldMessenger.of(context).showSnackBar(
                    SnackBar(
                      content: const Text('Form submitted successfully!'),
                      backgroundColor: _customTheme.primaryColor,
                    ),
                  );
                }
                // Don't return anything for Future<void>
              },
              useCardContainer: true,
              boldLabels: true,
            ),
          ),
        ],
      ),
    );
  }

  Widget colorOption(Color color) {
    return Row(
      children: [
        Container(
          width: 20,
          height: 20,
          color: color,
        ),
        const SizedBox(width: 8),
        Text(colorName(color)),
      ],
    );
  }

  String colorName(Color color) {
    if (color == Colors.blue) return 'Blue';
    if (color == Colors.red) return 'Red';
    if (color == Colors.green) return 'Green';
    if (color == Colors.purple) return 'Purple';
    if (color == Colors.orange) return 'Orange';
    return 'Custom';
  }

  void _updateTheme() {
    if (_useDarkTheme) {
      _customTheme = FormTheme(
        primaryColor: _customTheme.primaryColor,
        successColor: _customTheme.successColor,
        inputStyle: const TextStyle(color: Colors.white),
        labelStyle: const TextStyle(color: Colors.white70),
        errorStyle: const TextStyle(color: Colors.redAccent),
        inputDecoration: InputDecoration(
          filled: true,
          fillColor: Colors.grey.shade800,
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(8),
            borderSide: BorderSide.none,
          ),
        ),
      );
    } else {
      _customTheme = FormTheme(
        primaryColor: _customTheme.primaryColor,
        successColor: _customTheme.successColor,
      );
    }
  }
}
1
likes
140
points
15
downloads

Publisher

verified publishernocorps.org

Weekly Downloads

Auto Form Builder is a robust Flutter package for building dynamic, configuration-driven forms with advanced features such as multi-step wizards, conditional field visibility, computed fields, input masking, validation, and persistent draft saving—all with minimal boilerplate.

Homepage
Repository (GitHub)
View/report issues

Documentation

API reference

License

Apache-2.0 (license)

Dependencies

flutter, intl, shared_preferences

More

Packages that depend on auto_form_builder