reusable_editor 1.3.2 copy "reusable_editor: ^1.3.2" to clipboard
reusable_editor: ^1.3.2 copied to clipboard

A modular form and file editor package for Flutter with support for Form widgets. Includes Cubit-based state management, form field validation, and UI input widgets.

example/main.dart

import 'dart:io';

import 'package:cross_file/cross_file.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:i_validator/i_validator.dart';
import 'package:intl/intl.dart';
import 'package:reusable_editor/reusable_editor.dart';

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

/// Define a sample Enum for the multi-select demo
enum UserRole { admin, editor, viewer, guest }

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

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Reusable Editor Example',
      home: FormEditorDemo(),
    );
  }
}

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

  @override
  State<FormEditorDemo> createState() => _FormEditorDemoState();
}

class _FormEditorDemoState extends State<FormEditorDemo> {
  /// --- Cubit Instances ---

  final EnumOptionCubit<FileDataSourceType> _sourceCubit = EnumOptionCubit();

  /// Multi-select Cubit
  late final MultiEnumOptionCubit<UserRole> _rolesCubit;

  final FieldCubit<String> _genderCubit = FieldCubit<String>(
    initialValue: 'Male',
    validator: RequiredFieldValidator().call,
  );

  final ImageFieldCubit _imageCubit = ImageFieldCubit();
  final ListImageFieldCubit _listImageCubit = ListImageFieldCubit();

  final ToggleCubit _checkboxCubit = ToggleCubit(
    initialValue: false,
    validator: (val) => val == true ? null : 'You must accept the terms.',
  );

  final TextFieldCubit _textFieldCubit = TextFieldCubit(
    initialValue: '',
    validator: MinLengthValidator(3).call,
  );

  final FieldCubit<DateTime> _datePickerCubit = FieldCubit<DateTime>(
    validator: RequiredFieldValidator().call,
  );

  final FieldCubit<TimeOfDay> _timePickerCubit = FieldCubit<TimeOfDay>(
    validator: RequiredFieldValidator().call,
  );

  final FieldCubit<double> _sliderCubit = FieldCubit<double>(initialValue: 0.5);

  final ToggleCubit _switchCubit = ToggleCubit(
    initialValue: false,
    validator: (val) => val == true ? null : 'Feature must be enabled.',
  );

  @override
  void initState() {
    super.initState();

    /// MultiEnumOptionCubit no longer needs options
    _rolesCubit = MultiEnumOptionCubit<UserRole>();
  }

  @override
  void dispose() {
    _checkboxCubit.close();
    _textFieldCubit.close();
    _datePickerCubit.close();
    _timePickerCubit.close();
    _sliderCubit.close();
    _switchCubit.close();
    _sourceCubit.close();
    _imageCubit.close();
    _listImageCubit.close();
    _rolesCubit.close();
    _genderCubit.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final dropdownItems = [
      EnumOptionEntity(
        type: FileDataSourceType.fireStorage,
        icon: Icons.cloud,
        label: 'Firebase',
      ),
      EnumOptionEntity(
        type: FileDataSourceType.server,
        icon: Icons.storage,
        label: 'Server',
      ),
    ];

    final roleOptions = [
      EnumOptionEntity(
        type: UserRole.admin,
        label: 'Administrator',
        icon: Icons.security,
      ),
      EnumOptionEntity(
        type: UserRole.editor,
        label: 'Content Editor',
        icon: Icons.edit,
      ),
      EnumOptionEntity(
        type: UserRole.viewer,
        label: 'Viewer Only',
        icon: Icons.visibility,
      ),
      EnumOptionEntity(
        type: UserRole.guest,
        label: 'Guest',
        icon: Icons.person_outline,
      ),
    ];

    return Scaffold(
      appBar: AppBar(title: const Text('Reusable Editor Demo')),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'Single Selection (Dropdown)',
              style: Theme.of(context).textTheme.titleMedium,
            ),
            const SizedBox(height: 8),

            EnumOptionDropDownMenuFormField<FileDataSourceType>(
              selectedValue: _sourceCubit.state.selectedOption,
              onChanged: _sourceCubit.selectOption,
              hint: 'Choose data source',
              dropdownItems: dropdownItems,
            ),

            const SizedBox(height: 24),

            Text(
              'Multi-Selection (Checkboxes)',
              style: Theme.of(context).textTheme.titleMedium,
            ),
            const SizedBox(height: 8),

            Card(
              child: Padding(
                padding: const EdgeInsets.all(12),
                child: EnumMultiOptionCheckboxGroup<UserRole>(
                  cubit: _rolesCubit,
                  options: roleOptions,
                  label: 'User Roles',
                ),
              ),
            ),

            const SizedBox(height: 24),

            Text(
              'Image Picker',
              style: Theme.of(context).textTheme.titleMedium,
            ),
            const SizedBox(height: 8),

            Wrap(
              spacing: 10,
              children: [
                ElevatedButton.icon(
                  onPressed: () async {
                    final XFile? pickedFile = await 'assets/sample.png'
                        .loadAsXFile();
                    if (pickedFile != null) {
                      _imageCubit.selectImage(pickedFile);
                    }
                  },
                  icon: const Icon(Icons.image),
                  label: const Text('Single Pick'),
                ),
                ElevatedButton.icon(
                  onPressed: () async {
                    // Simulating multiple file selection
                    final results = await Future.wait([
                      'assets/sample.png'.loadAsXFile(),
                      'assets/sample.png'.loadAsXFile(),
                    ]);
                    final pickedFiles = results.whereType<XFile>().toList();

                    _listImageCubit.selectImages(pickedFiles);
                  },
                  icon: const Icon(Icons.photo_library),
                  label: const Text('Multi Pick'),
                ),
              ],
            ),

            const SizedBox(height: 16),

            Row(
              children: [
                Expanded(
                  child: BlocBuilder<ImageFieldCubit, ImageFieldState>(
                    bloc: _imageCubit,
                    builder: (context, state) {
                      final XFile? pickedFile = state.pickedFile;
                      if (pickedFile != null) {
                        return Column(
                          children: [
                            ClipRRect(
                              borderRadius: BorderRadius.circular(8),
                              child: Image.file(
                                File(pickedFile.path),
                                height: 80,
                                width: 80,
                                fit: BoxFit.cover,
                              ),
                            ),
                            const Text(
                              'Single',
                              style: TextStyle(fontSize: 10),
                            ),
                          ],
                        );
                      }
                      return const Text('No single image');
                    },
                  ),
                ),
                const SizedBox(width: 20),
                Expanded(
                  child: BlocBuilder<ListImageFieldCubit, ListImageFieldState>(
                    bloc: _listImageCubit,
                    builder: (context, state) {
                      final pickedFiles = state.pickedFiles;
                      if (pickedFiles != null && pickedFiles.isNotEmpty) {
                        return Column(
                          children: [
                            Wrap(
                              spacing: 4,
                              children: pickedFiles
                                  .map(
                                    (file) => ClipRRect(
                                      borderRadius: BorderRadius.circular(4),
                                      child: Image.file(
                                        File(file.path),
                                        height: 40,
                                        width: 40,
                                        fit: BoxFit.cover,
                                      ),
                                    ),
                                  )
                                  .toList(),
                            ),
                            const Text('Multi', style: TextStyle(fontSize: 10)),
                          ],
                        );
                      }
                      return const Text('No multi images');
                    },
                  ),
                ),
              ],
            ),

            const Divider(height: 40),

            Text(
              'Standard Form Fields',
              style: Theme.of(context).textTheme.headlineSmall,
            ),

            const SizedBox(height: 16),

            AppRadioGroup<String>(
              cubit: _genderCubit,
              label: 'Select Gender',
              values: const ['Male', 'Female', 'Other'],
              labels: const ['Male', 'Female', 'Other'],
              activeColor: Colors.deepPurple,
            ),

            const SizedBox(height: 16),

            AppCheckbox(
              cubit: _checkboxCubit,
              label: 'Accept Terms and Conditions',
            ),

            const SizedBox(height: 16),

            AppTextField(
              bloc: _textFieldCubit,
              label: 'Name',
              hintText: 'Enter your name',
            ),

            const SizedBox(height: 16),

            AppDatePicker(
              cubit: _datePickerCubit,
              labelText: 'Select Date',
              placeholderText: 'Tap to pick a date',
              formatter: DateFormat('yyyy-MM-dd'),
            ),

            const SizedBox(height: 16),

            AppTimePicker(
              cubit: _timePickerCubit,
              labelText: 'Select Time',
              hintText: 'Tap to pick a time',
            ),

            const SizedBox(height: 16),

            AppSlider(
              cubit: _sliderCubit,
              min: 0.0,
              max: 1.0,
              divisions: 10,
              labelText: 'Intensity Level',
              displayValue: (val) => '${(val * 100).toInt()}%',
            ),

            const SizedBox(height: 16),

            AppSwitch(cubit: _switchCubit, label: 'Enable Notifications'),

            const SizedBox(height: 32),

            SizedBox(
              width: double.infinity,
              child: ElevatedButton(
                onPressed: _validateForm,
                style: ElevatedButton.styleFrom(
                  padding: const EdgeInsets.all(16),
                ),
                child: const Text('Validate & Submit'),
              ),
            ),

            const SizedBox(height: 40),
          ],
        ),
      ),
    );
  }

  void _validateForm() {
    final isValid = [
      _genderCubit.validate(),
      _checkboxCubit.validate(),
      _textFieldCubit.validate(),
      _datePickerCubit.validate(),
      _timePickerCubit.validate(),
      _switchCubit.validate(),
    ].every((error) => error == null);

    final selectedRoles = _rolesCubit.selectedTypes
        .map((e) => e.name)
        .join(', ');

    final message = isValid
        ? 'Valid! Gender: ${_genderCubit.state.value}, Roles: ${selectedRoles.isEmpty ? 'None' : selectedRoles}'
        : 'Please correct the highlighted errors.';

    ScaffoldMessenger.of(
      context,
    ).showSnackBar(SnackBar(content: Text(message)));
  }
}
1
likes
0
points
629
downloads

Publisher

unverified uploader

Weekly Downloads

A modular form and file editor package for Flutter with support for Form widgets. Includes Cubit-based state management, form field validation, and UI input widgets.

Homepage
Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

bloc, cross_file, dartz, equatable, file_picker, flutter, flutter_bloc, get_it_di_global_variable, i_validator, intl, path_provider, provider

More

Packages that depend on reusable_editor