leo_easy_ui_kit 0.7.6 copy "leo_easy_ui_kit: ^0.7.6" to clipboard
leo_easy_ui_kit: ^0.7.6 copied to clipboard

Leo Easy UI Kit: effortless yet powerful Flutter UI components.

example/lib/main.dart

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Leo Easy UI Kit - Comprehensive Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: const Color(0xFF6200EE),
        brightness: Brightness.light,
      ),
      darkTheme: ThemeData(
        useMaterial3: true,
        colorSchemeSeed: const Color(0xFF6200EE),
        brightness: Brightness.dark,
      ),
      home: const ExampleHomePage(),
    );
  }
}

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

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

class _ExampleHomePageState extends State<ExampleHomePage>
    with SingleTickerProviderStateMixin {
  late TabController _tabController;

  // Form demo data
  String? role = 'Developer';
  List<String> selectedInterests = ['Flutter', 'UI/UX'];
  String? experienceLevel = 'Intermediate';
  String? employmentStatus = 'Employed';
  bool receiveNotifications = true;
  int? yearsExperience = 3;
  DateTime? startDate = DateTime.now();
  List<String> selectedSkills = const [];
  String? country = 'United States';
  String? selectedPerson;
  List<String> uploadedFiles = const [];
  List<String> profileImages = const [];
  int onboardingStep = 0;
  List<String> technologyTags = ['Flutter', 'Dart'];

  // Managed form demo data (EasyFormController)
  final EasyFormController _profileForm = EasyFormController(<String, Object?>{
    'fullName': 'Alex Flutter',
    'headline': 'Senior Flutter Developer',
  });

  // Table demo data
  List<Project> selectedProjects = [];
  int tablePageIndex = 0;
  int tablePageSize = 3;
  String projectStatusFilter = 'All';

  @override
  void initState() {
    super.initState();
    _tabController = TabController(length: 4, vsync: this);
  }

  @override
  void dispose() {
    _tabController.dispose();
    _profileForm.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return EasyResponsiveScaffold(
      appBar: AppBar(
        elevation: 0,
        title: const Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              'Leo Easy UI Kit',
              style: TextStyle(fontWeight: FontWeight.bold),
            ),
            Text(
              'Effortless Flutter UI Components',
              style: TextStyle(fontSize: 12, fontWeight: FontWeight.normal),
            ),
          ],
        ),
        bottom: TabBar(
          controller: _tabController,
          isScrollable: true,
          tabs: const [
            Tab(icon: Icon(Icons.dynamic_form), text: 'Forms'),
            Tab(icon: Icon(Icons.menu), text: 'Selectors'),
            Tab(icon: Icon(Icons.table_chart), text: 'Data'),
            Tab(icon: Icon(Icons.widgets), text: 'Basic'),
          ],
        ),
      ),
      navigationRail: NavigationRail(
        selectedIndex: _tabController.index,
        onDestinationSelected: (index) {
          setState(() {
            _tabController.index = index;
          });
        },
        labelType: NavigationRailLabelType.all,
        destinations: const [
          NavigationRailDestination(
            icon: Icon(Icons.dynamic_form),
            label: Text('Forms'),
          ),
          NavigationRailDestination(
            icon: Icon(Icons.menu),
            label: Text('Selectors'),
          ),
          NavigationRailDestination(
            icon: Icon(Icons.table_chart),
            label: Text('Data'),
          ),
          NavigationRailDestination(
            icon: Icon(Icons.widgets),
            label: Text('Basic'),
          ),
        ],
      ),
      bottomNavigationBar: NavigationBar(
        selectedIndex: _tabController.index,
        onDestinationSelected: (index) {
          setState(() {
            _tabController.index = index;
          });
        },
        destinations: const [
          NavigationDestination(
            icon: Icon(Icons.dynamic_form),
            label: 'Forms',
          ),
          NavigationDestination(
            icon: Icon(Icons.menu),
            label: 'Selectors',
          ),
          NavigationDestination(
            icon: Icon(Icons.table_chart),
            label: 'Data',
          ),
          NavigationDestination(
            icon: Icon(Icons.widgets),
            label: 'Basic',
          ),
        ],
      ),
      body: TabBarView(
        controller: _tabController,
        children: [
          _buildFormsTab(),
          _buildSelectorsTab(),
          _buildDataTab(),
          _buildBasicTab(),
        ],
      ),
    );
  }

  Widget _buildFormsTab() {
    return ListView(
      padding: const EdgeInsets.all(24),
      children: [
        _SectionHeader(
          icon: Icons.person,
          title: 'Professional Profile Form',
          subtitle: 'Showcase of various form components',
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Onboarding Stepper',
          description:
              'Horizontal, tappable indicator for multi-step flows and wizards',
          child: easyStepperOf<int>(
            steps: const [
              EasyStepConfig(id: 0, title: 'Profile', subtitle: 'Basic info'),
              EasyStepConfig(id: 1, title: 'Skills', subtitle: 'Expertise'),
              EasyStepConfig(id: 2, title: 'Review', subtitle: 'Summary'),
            ],
            currentStep: onboardingStep,
            onStepChanged: (step) => setState(() => onboardingStep = step),
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Dropdown Selection',
          description: 'Simple single-select dropdown',
          child: easyDropdownOf<String>(
            items: const ['Developer', 'Designer', 'Manager', 'Student'],
            value: role,
            onChanged: (value) => setState(() => role = value),
            itemLabel: (v) => v,
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Button Group (Radio Alternative)',
          description: 'Visually rich single-choice selector',
          child: easyButtonGroupOf<String>(
            items: const ['Beginner', 'Intermediate', 'Advanced', 'Expert'],
            value: experienceLevel,
            onChanged: (value) => setState(() => experienceLevel = value),
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Multi-Select Checkboxes',
          description: 'Select multiple interests',
          child: easyCheckboxOf<String>(
            items: const [
              'Flutter',
              'UI/UX',
              'Backend',
              'DevOps',
              'Mobile',
              'Web'
            ],
            values: selectedInterests,
            onChangedMany: (values) =>
                setState(() => selectedInterests = values),
            multiSelect: true,
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Radio Group',
          description: 'Traditional radio button selection',
          child: easyRadioOf<String>(
            items: const ['Employed', 'Freelance', 'Student', 'Seeking'],
            value: employmentStatus,
            onChanged: (value) => setState(() => employmentStatus = value),
            itemLabel: (v) => v,
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Switch Toggle',
          description: 'Binary on/off controls',
          child: easySwitchOf<String>(
            items: const ['Enable Notifications'],
            value: receiveNotifications ? 'Enable Notifications' : null,
            onChanged: (value) =>
                setState(() => receiveNotifications = value != null),
            itemLabel: (v) => v,
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Numeric Input',
          description: 'Type-safe integer field with validation',
          child: easyIntFieldOf(
            value: yearsExperience,
            onChanged: (value) => setState(() => yearsExperience = value),
            hintText: 'Years of experience',
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Date Picker',
          description: 'Native date picker integration',
          child: easyDateFieldOf(
            value: startDate,
            onChanged: (value) => setState(() => startDate = value),
            itemLabel: (date) => '${date.year}-${date.month}-${date.day}',
            firstDate: DateTime(2000),
            lastDate: DateTime(2030),
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'File Picker',
          description:
              'Bottom-sheet category picker wired to your own file API',
          child: EasyFilePicker<String>(
            label: 'Attach files',
            allowMultiple: true,
            values: uploadedFiles,
            onChangedMany: (files) => setState(() => uploadedFiles = files),
            onPick: (category, allowMultiple) async {
              // Integrate with `file_picker` or any custom implementation.
              await Future.delayed(const Duration(milliseconds: 300));
              return [
                '${category.label} sample ${uploadedFiles.length + 1}.txt',
              ];
            },
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Image Picker',
          description:
              'Camera / gallery picker that delegates to your own image source',
          child: EasyImagePicker<String>(
            allowMultiple: true,
            values: profileImages,
            onChangedMany: (images) => setState(() => profileImages = images),
            onPick: (source, allowMultiple) async {
              // Integrate with `image_picker` or any other source.
              await Future.delayed(const Duration(milliseconds: 300));
              return [
                '${source.label} image ${profileImages.length + 1}',
              ];
            },
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Managed Form (EasyFormController)',
          description:
              'Name and headline fields bound to EasyFormController by name.',
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              easyTextFormFieldOf<String>(
                form: _profileForm,
                name: 'fullName',
                hintText: 'Full name',
              ),
              const SizedBox(height: 12),
              easyTextFormFieldOf<String>(
                form: _profileForm,
                name: 'headline',
                hintText: 'Headline (e.g. Flutter developer)',
              ),
              const SizedBox(height: 12),
              Builder(
                builder: (context) {
                  final values = _profileForm.rawValues;
                  return Text(
                    'Form values: '
                    "${values['fullName'] ?? ''} · "
                    "${values['headline'] ?? ''}",
                    style: Theme.of(context).textTheme.bodySmall,
                  );
                },
              ),
            ],
          ),
        ),
        const SizedBox(height: 24),
        _buildSummaryCard(),
      ],
    );
  }

  Widget _buildSelectorsTab() {
    return ListView(
      padding: const EdgeInsets.all(24),
      children: [
        _SectionHeader(
          icon: Icons.touch_app,
          title: 'Advanced Selectors',
          subtitle: 'Chips, segmented controls, and modal selectors',
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Choice Chips',
          description: 'Tag-style multi-select with visual feedback',
          child: easyChipsOf<String>(
            items: const [
              'TypeScript',
              'Dart',
              'Python',
              'Rust',
              'Go',
              'Swift'
            ],
            values: selectedSkills,
            onChangedMany: (values) => setState(() => selectedSkills = values),
            multiSelect: true,
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Tag Input',
          description: 'Free-form tags backed by strongly-typed values',
          child: easyTagInputOf(
            tags: technologyTags,
            onChanged: (tags) => setState(() => technologyTags = tags),
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Segmented Button',
          description: 'Material 3 segmented control',
          child: easySegmentedOf<String>(
            items: const ['Day', 'Week', 'Month', 'Year'],
            value: 'Week',
            onChanged: (value) {},
            itemLabel: (v) => v,
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Bottom Sheet Selector',
          description: 'Modal sheet for picking from a large list',
          child: easyBottomSheetSelectOf<String>(
            items: const [
              'United States',
              'Canada',
              'United Kingdom',
              'Germany',
              'France',
              'Japan',
              'Australia',
              'Brazil',
            ],
            value: country,
            onChanged: (value) => setState(() => country = value),
            itemLabel: (v) => v,
            sheetTitle: const Text('Select Country'),
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Popup Dialog Selector',
          description: 'Dialog-based selection with search',
          child: easyPopupSelectOf<String>(
            items: const [
              'Light Theme',
              'Dark Theme',
              'System Default',
              'High Contrast'
            ],
            value: 'System Default',
            onChanged: (value) {},
            itemLabel: (v) => v,
            title: const Text('Select Theme'),
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Search with Autocomplete',
          description: 'Real-time search and recommendation',
          child: easySearchOf<String>(
            items: const [
              'Alice Johnson',
              'Bob Smith',
              'Charlie Brown',
              'Diana Prince',
              'Eve Davis',
              'Frank Miller',
              'Grace Hopper',
            ],
            itemLabel: (v) => v,
            onSelected: (value) {
              setState(() => selectedPerson = value);
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(
                  content: Text('Selected: $value'),
                  behavior: SnackBarBehavior.floating,
                ),
              );
            },
            hintText: 'Search for a person...',
          ),
        ),
      ],
    );
  }

  Widget _buildDataTab() {
    final filteredProjects = projectStatusFilter == 'All'
        ? _sampleProjects
        : _sampleProjects
            .where((p) => p.status == projectStatusFilter)
            .toList(growable: false);

    final start = tablePageIndex * tablePageSize;
    final end = (start + tablePageSize).clamp(0, filteredProjects.length);
    final paginatedProjects = filteredProjects.sublist(start, end);

    return ListView(
      padding: const EdgeInsets.all(24),
      children: [
        _SectionHeader(
          icon: Icons.table_rows,
          title: 'Data Tables & Lists',
          subtitle: 'Display and select structured data',
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Filter Bar',
          description:
              'Responsive container for filters (chips, buttons, search)',
          child: easyFilterBarOf(
            leading: const Icon(Icons.filter_list),
            filters: [
              easyButtonGroupOf<String>(
                items: const ['All', 'Completed', 'In Progress', 'Planned'],
                value: projectStatusFilter,
                onChanged: (value) => setState(() {
                  projectStatusFilter = value ?? 'All';
                  tablePageIndex = 0;
                }),
              ),
            ],
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Selectable Data Table',
          description: 'Multi-select table with easy row selection',
          child: SingleChildScrollView(
            scrollDirection: Axis.horizontal,
            child: easyTableOf<Project>(
              items: paginatedProjects,
              columns: const [
                DataColumn(label: Text('Project')),
                DataColumn(label: Text('Status')),
                DataColumn(label: Text('Progress')),
              ],
              cellBuilder: (context, project, colIndex, selected) {
                switch (colIndex) {
                  case 0:
                    return Text(project.name);
                  case 1:
                    return _StatusBadge(project.status);
                  case 2:
                    return Text('${project.progress}%');
                  default:
                    return const Text('');
                }
              },
              selectedValues: selectedProjects,
              onChangedMany: (values) =>
                  setState(() => selectedProjects = values),
              multiSelect: true,
            ),
          ),
        ),
        const SizedBox(height: 16),
        Text(
          'Selected projects: ${selectedProjects.length}',
          style: Theme.of(context).textTheme.bodyMedium,
        ),
        const SizedBox(height: 16),
        easyPaginationOf(
          itemCount: filteredProjects.length,
          pageIndex: tablePageIndex,
          pageSize: tablePageSize,
          onPageChanged: (index) => setState(() => tablePageIndex = index),
          onPageSizeChanged: (size) => setState(() {
            tablePageSize = size;
            tablePageIndex = 0;
          }),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Selectable List',
          description: 'Simple list with item selection',
          child: easyListOf<String>(
            items: const ['Inbox', 'Starred', 'Sent', 'Drafts', 'Trash'],
            value: 'Inbox',
            onChanged: (value) {},
            itemLabel: (v) => v,
            divided: true,
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Menu (Dropdown Alternative)',
          description: 'Popup menu for actions or selections',
          child: easyMenuOf<String>(
            items: const ['Profile', 'Settings', 'Help', 'Logout'],
            value: null,
            onChanged: (value) {
              ScaffoldMessenger.of(context).showSnackBar(
                SnackBar(
                  content: Text('Menu action: $value'),
                  behavior: SnackBarBehavior.floating,
                ),
              );
            },
            itemLabel: (v) => v,
            tooltip: 'Open menu',
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Grid View',
          description: 'Card-style grid selection using easyGridOf',
          child: SizedBox(
            height: 220,
            child: easyGridOf<Project>(
              items: _sampleProjects,
              values: selectedProjects,
              onChangedMany: (values) =>
                  setState(() => selectedProjects = values),
              multiSelect: true,
              itemLabel: (p) => p.name,
              crossAxisCount: 2,
              childAspectRatio: 4 / 3,
            ),
          ),
        ),
        const SizedBox(height: 24),
        _FormSection(
          title: 'Searchable Grid',
          description: 'Search + grid of projects with custom tiles',
          child: easySearchableGridOf<Project>(
            items: _sampleProjects,
            itemLabel: (p) => p.name,
            itemBuilder: (context, project, selected) => Card(
              elevation: selected ? 4 : 1,
              child: Padding(
                padding: const EdgeInsets.all(12),
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      project.name,
                      style: Theme.of(context).textTheme.titleSmall,
                    ),
                    const SizedBox(height: 4),
                    _StatusBadge(project.status),
                    const Spacer(),
                    Text('${project.progress}% complete'),
                  ],
                ),
              ),
            ),
            values: selectedProjects,
            onChangedMany: (values) =>
                setState(() => selectedProjects = values),
            multiSelect: true,
            hintText: 'Search projects...',
            crossAxisCount: 2,
            childAspectRatio: 4 / 3,
          ),
        ),
      ],
    );
  }

  Widget _buildBasicTab() {
    return EasyScroll(
      padding: const EdgeInsets.all(24),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const _SectionHeader(
            icon: Icons.widgets,
            title: 'Basic Building Blocks',
            subtitle: 'Simplified wrappers for common widgets',
          ),
          const SizedBox(height: 24),
          _FormSection(
            title: 'EasyText',
            description: 'Simplified text styling',
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const EasyText(
                  'Headline Text',
                  size: 24,
                  weight: FontWeight.bold,
                  color: Colors.deepPurple,
                ),
                const SizedBox(height: 8),
                EasyText(
                  'This is a body text with some custom styling like letter spacing and line height.',
                  size: 16,
                  color: Colors.grey[700],
                  height: 1.5,
                  letterSpacing: 0.5,
                ),
              ],
            ),
          ),
          const SizedBox(height: 24),
          _FormSection(
            title: 'EasyContainer',
            description: 'Simplified container with shadow and tap',
            child: Row(
              children: [
                EasyContainer(
                  width: 100,
                  height: 100,
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(16),
                  elevation: 4,
                  alignment: Alignment.center,
                  onTap: () {
                    ScaffoldMessenger.of(context).showSnackBar(
                      const SnackBar(
                        content: Text('Container Tapped!'),
                        behavior: SnackBarBehavior.floating,
                      ),
                    );
                  },
                  child: const EasyText('Tap Me', weight: FontWeight.bold),
                ),
                const SizedBox(width: 16),
                EasyContainer(
                  width: 100,
                  height: 100,
                  color: Colors.blue.withValues(alpha: 0.1),
                  borderColor: Colors.blue,
                  borderWidth: 2,
                  shape: BoxShape.circle,
                  alignment: Alignment.center,
                  child: const Icon(Icons.star, color: Colors.blue, size: 32),
                ),
              ],
            ),
          ),
          const SizedBox(height: 24),
          _FormSection(
            title: 'EasyImage',
            description: 'Simplified image loading',
            child: Row(
              children: [
                const EasyContainer(
                  width: 100,
                  height: 100,
                  borderRadius: BorderRadius.all(Radius.circular(12)),
                  clipBehavior: Clip.antiAlias,
                  child: EasyImage(
                    image: 'https://picsum.photos/200',
                    fit: BoxFit.cover,
                    isNetwork: true,
                    placeholder: Center(child: CircularProgressIndicator()),
                    errorWidget: Center(child: Icon(Icons.error)),
                  ),
                ),
                const SizedBox(width: 16),
                EasyContainer(
                  width: 100,
                  height: 100,
                  shape: BoxShape.circle,
                  clipBehavior: Clip.antiAlias,
                  child: const EasyImage(
                    image: 'https://picsum.photos/201',
                    fit: BoxFit.cover,
                    isNetwork: true,
                  ),
                ),
              ],
            ),
          ),
          const SizedBox(height: 24),
          _FormSection(
            title: 'EasyScroll',
            description: 'Simplified scroll view (used for this tab)',
            child: EasyContainer(
              height: 150,
              color: Colors.grey[100],
              borderRadius: BorderRadius.circular(12),
              padding: const EdgeInsets.all(12),
              child: EasyScroll(
                showScrollbar: true,
                child: Column(
                  children: List.generate(
                    20,
                    (i) => Padding(
                      padding: const EdgeInsets.symmetric(vertical: 4),
                      child: Text('Scroll Item $i'),
                    ),
                  ),
                ),
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildSummaryCard() {
    return Card(
      elevation: 0,
      color: Theme.of(context).colorScheme.primaryContainer,
      child: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Icon(
                  Icons.check_circle,
                  color: Theme.of(context).colorScheme.onPrimaryContainer,
                ),
                const SizedBox(width: 12),
                Text(
                  'Profile Summary',
                  style: Theme.of(context).textTheme.titleLarge?.copyWith(
                        color: Theme.of(context).colorScheme.onPrimaryContainer,
                      ),
                ),
              ],
            ),
            const SizedBox(height: 16),
            _SummaryRow('Role', role ?? 'Not selected'),
            _SummaryRow(
              'Experience Level',
              experienceLevel ?? 'Not selected',
            ),
            _SummaryRow(
              'Interests',
              selectedInterests.join(', ').ifEmpty,
            ),
            _SummaryRow(
              'Employment Status',
              employmentStatus ?? 'Not selected',
            ),
            _SummaryRow(
              'Notifications',
              receiveNotifications ? 'Enabled' : 'Disabled',
            ),
            _SummaryRow(
              'Years Experience',
              yearsExperience?.toString() ?? 'Not specified',
            ),
            _SummaryRow(
              'Files',
              uploadedFiles.isEmpty
                  ? 'No files attached'
                  : '${uploadedFiles.length} file(s) attached',
            ),
            _SummaryRow(
              'Images',
              profileImages.isEmpty
                  ? 'No images selected'
                  : '${profileImages.length} image(s) selected',
            ),
          ],
        ),
      ),
    );
  }
}

// Helper Widgets

class _SectionHeader extends StatelessWidget {
  final IconData icon;
  final String title;
  final String subtitle;

  const _SectionHeader({
    required this.icon,
    required this.title,
    required this.subtitle,
  });

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        Container(
          padding: const EdgeInsets.all(12),
          decoration: BoxDecoration(
            color: Theme.of(context).colorScheme.primaryContainer,
            borderRadius: BorderRadius.circular(12),
          ),
          child: Icon(
            icon,
            color: Theme.of(context).colorScheme.onPrimaryContainer,
          ),
        ),
        const SizedBox(width: 16),
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                title,
                style: Theme.of(context).textTheme.titleLarge?.copyWith(
                      fontWeight: FontWeight.bold,
                    ),
              ),
              Text(
                subtitle,
                style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                      color: Theme.of(context)
                          .textTheme
                          .bodySmall
                          ?.color
                          ?.withValues(alpha: 0.6),
                    ),
              ),
            ],
          ),
        ),
      ],
    );
  }
}

class _FormSection extends StatelessWidget {
  final String title;
  final String description;
  final Widget child;

  const _FormSection({
    required this.title,
    required this.description,
    required this.child,
  });

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          title,
          style: Theme.of(context).textTheme.titleMedium?.copyWith(
                fontWeight: FontWeight.w600,
              ),
        ),
        const SizedBox(height: 4),
        Text(
          description,
          style: Theme.of(context).textTheme.bodySmall?.copyWith(
                color: Theme.of(context)
                    .textTheme
                    .bodySmall
                    ?.color
                    ?.withValues(alpha: 0.6),
              ),
        ),
        const SizedBox(height: 12),
        child,
      ],
    );
  }
}

class _SummaryRow extends StatelessWidget {
  final String label;
  final String value;

  const _SummaryRow(this.label, this.value);

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 4),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          SizedBox(
            width: 140,
            child: Text(
              '$label:',
              style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                    fontWeight: FontWeight.w600,
                    color: Theme.of(context)
                        .colorScheme
                        .onPrimaryContainer
                        .withValues(alpha: 0.8),
                  ),
            ),
          ),
          Expanded(
            child: Text(
              value,
              style: Theme.of(context).textTheme.bodyMedium?.copyWith(
                    color: Theme.of(context).colorScheme.onPrimaryContainer,
                  ),
            ),
          ),
        ],
      ),
    );
  }
}

class _StatusBadge extends StatelessWidget {
  final String status;

  const _StatusBadge(this.status);

  @override
  Widget build(BuildContext context) {
    Color color;
    switch (status) {
      case 'Completed':
        color = Colors.green;
        break;
      case 'In Progress':
        color = Colors.orange;
        break;
      case 'Planned':
        color = Colors.blue;
        break;
      default:
        color = Colors.grey;
    }

    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
      decoration: BoxDecoration(
        color: color.withValues(alpha: 0.2),
        borderRadius: BorderRadius.circular(4),
      ),
      child: Text(
        status,
        style: TextStyle(
          color: color,
          fontSize: 12,
          fontWeight: FontWeight.w600,
        ),
      ),
    );
  }
}

// Data models

class Project {
  final String name;
  final String status;
  final int progress;

  Project(this.name, this.status, this.progress);

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is Project &&
          runtimeType == other.runtimeType &&
          name == other.name;

  @override
  int get hashCode => name.hashCode;
}

final _sampleProjects = [
  Project('Mobile App Redesign', 'In Progress', 65),
  Project('API Integration', 'Completed', 100),
  Project('User Dashboard', 'In Progress', 40),
  Project('Analytics Module', 'Planned', 0),
  Project('Payment Gateway', 'Completed', 100),
];

extension on String {
  String get ifEmpty => isEmpty ? 'None' : this;
}
8
likes
160
points
641
downloads

Publisher

unverified uploader

Weekly Downloads

Leo Easy UI Kit: effortless yet powerful Flutter UI components.

Homepage

Topics

#flutter #ui-kit #widget #form

Documentation

API reference

License

MIT (license)

Dependencies

equatable, flutter, flutter_bloc, flutter_hooks, provider

More

Packages that depend on leo_easy_ui_kit