Skip to main content

expressive_kit 0.0.4 copy "expressive_kit: ^0.0.4" to clipboard
expressive_kit: ^0.0.4 copied to clipboard

A modern Flutter design system providing Material 3 Expressive components, spring motions, tactile feedback, and unified theme styles.

example/lib/main.dart

import 'package:expressive_kit/expressive_kit.dart';
import 'package:hugeicons/hugeicons.dart';
import 'package:material_ui/material_ui.dart';

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

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

  @override
  State<ExampleApp> createState() => _ExampleAppState();
}

class _ExampleAppState extends State<ExampleApp> {
  bool _isDarkMode = false;
  String _selectedFontFamily = ExpressiveFonts.googleSansFlex;

  void _toggleTheme() {
    setState(() => _isDarkMode = !_isDarkMode);
  }

  void _changeFont(String newFont) {
    setState(() => _selectedFontFamily = newFont);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Expressive Kit Gallery',
      debugShowCheckedModeBanner: false,
      theme: ExpressiveTheme.light(
        baseColor: const Color(0xFF1E88E5),
        fontFamily: _selectedFontFamily,
      ),
      darkTheme: ExpressiveTheme.dark(
        baseColor: const Color(0xFF1E88E5),
        fontFamily: _selectedFontFamily,
      ),
      themeMode: _isDarkMode ? ThemeMode.dark : ThemeMode.light,
      home: GalleryScreen(
        isDarkMode: _isDarkMode,
        onToggleTheme: _toggleTheme,
        selectedFont: _selectedFontFamily,
        onFontChanged: _changeFont,
      ),
    );
  }
}

class GalleryScreen extends StatefulWidget {
  const GalleryScreen({
    required this.isDarkMode,
    required this.onToggleTheme,
    required this.selectedFont,
    required this.onFontChanged,
    super.key,
  });

  final bool isDarkMode;
  final VoidCallback onToggleTheme;
  final String selectedFont;
  final ValueChanged<String> onFontChanged;

  @override
  State<GalleryScreen> createState() => _GalleryScreenState();
}

class _GalleryScreenState extends State<GalleryScreen> {
  int _selectedTab = 0;

  // Controls state
  bool _btnLoading = false;
  String _selectedFrequency = 'Weekly';
  bool _switchVal1 = true;
  bool _switchVal2 = false;
  double _sliderVal = 65.0;
  double _sliderStepVal = 3.0;
  int _badgeCount = 3;

  // Motion playground state
  double _scaleBouncy = 1.0;
  double _scaleGentle = 1.0;
  double _scaleSnappy = 1.0;

  // Typography test text controller
  final TextEditingController _customTextController = TextEditingController(
    text: 'The quick brown fox jumps over the lazy dog.',
  );

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

  void _showFeedback(String message) {
    ScaffoldMessenger.of(context).hideCurrentSnackBar();
    ScaffoldMessenger.of(context)
        .showSnackBar(SnackBar(content: Text(message)));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: ExpressiveTopAppBar(
        title: const Text('Expressive Kit Gallery'),
        actions: [
          ExpressiveIconButton.circle(
            icon: HugeIcon(
              icon: widget.isDarkMode
                  ? HugeIcons.strokeRoundedSun03
                  : HugeIcons.strokeRoundedMoon02,
              color: Theme.of(context).colorScheme.onSurface,
            ),
            tooltip: widget.isDarkMode ? 'Light mode' : 'Dark mode',
            onPressed: widget.onToggleTheme,
          ),
          ExpressiveIconButton.circle(
            icon: const HugeIcon(icon: HugeIcons.strokeRoundedNotification01),
            badgeText: '$_badgeCount',
            tooltip: 'Notifications',
            onPressed: () {
              setState(() => _badgeCount = (_badgeCount + 1) % 10);
              _showFeedback('Notifications: $_badgeCount');
            },
          ),
          const SizedBox(width: 8),
        ],
      ),
      body: IndexedStack(
        index: _selectedTab,
        children: [
          _buildControlsTab(),
          _buildContentTab(),
          _buildProgressAndMotionTab(),
          _buildNavigationAndFabTab(),
          _buildTypographyTab(),
        ],
      ),
      bottomNavigationBar: ExpressiveNavigationBar(
        selectedIndex: _selectedTab,
        onDestinationSelected: (index) {
          setState(() => _selectedTab = index);
        },
        destinations: const [
          ExpressiveNavigationDestination(
            icon: HugeIcon(icon: HugeIcons.strokeRoundedCursorPointer01),
            selectedIcon: HugeIcon(
              icon: HugeIcons.strokeRoundedCursorPointer01,
            ),
            label: 'Controls',
          ),
          ExpressiveNavigationDestination(
            icon: HugeIcon(icon: HugeIcons.strokeRoundedGridTable),
            selectedIcon: HugeIcon(
              icon: HugeIcons.strokeRoundedDashboardSquare01,
            ),
            label: 'Content',
          ),
          ExpressiveNavigationDestination(
            icon: HugeIcon(icon: HugeIcons.strokeRoundedDashboardSpeed01),
            selectedIcon: HugeIcon(
              icon: HugeIcons.strokeRoundedDashboardSpeed01,
            ),
            label: 'Progress',
          ),
          ExpressiveNavigationDestination(
            icon: HugeIcon(icon: HugeIcons.strokeRoundedNavigation03),
            selectedIcon: HugeIcon(icon: HugeIcons.strokeRoundedNavigation03),
            label: 'Navigation',
            showBadgeDot: true,
          ),
          ExpressiveNavigationDestination(
            icon: HugeIcon(icon: HugeIcons.strokeRoundedTextFont),
            selectedIcon: HugeIcon(icon: HugeIcons.strokeRoundedTextFont),
            label: 'Fonts',
          ),
        ],
      ),
      floatingActionButton: ExpressiveFabMenu(
        items: [
          ExpressiveFabMenuItem(
            icon: const HugeIcon(icon: HugeIcons.strokeRoundedShare01),
            label: 'Share',
            onTap: () => _showFeedback('Action: Share'),
          ),
          ExpressiveFabMenuItem(
            icon: const HugeIcon(icon: HugeIcons.strokeRoundedBookmark02),
            label: 'Save',
            onTap: () => _showFeedback('Action: Save'),
          ),
          ExpressiveFabMenuItem(
            icon: const HugeIcon(icon: HugeIcons.strokeRoundedAdd01),
            label: 'New item',
            onTap: () => _showFeedback('Action: New item'),
          ),
        ],
      ),
    );
  }

  // ---------------------------------------------------------------------------
  // TAB 1: BUTTONS AND CONTROLS
  // ---------------------------------------------------------------------------
  Widget _buildControlsTab() {
    return ListView(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      children: [
        _buildSectionHeader(
          'Buttons (ExpressiveButton)',
          'Material 3 variants with spring curves and responsive interactive states.',
        ),
        const SizedBox(height: 12),
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: [
            ExpressiveButton(
              label: 'Filled',
              variant: ExpressiveButtonVariant.filled,
              onPressed: () => _showFeedback('Filled button pressed'),
            ),
            ExpressiveButton(
              label: 'Tonal',
              variant: ExpressiveButtonVariant.tonal,
              onPressed: () => _showFeedback('Tonal button pressed'),
            ),
            ExpressiveButton(
              label: 'Outlined',
              variant: ExpressiveButtonVariant.outlined,
              onPressed: () => _showFeedback('Outlined button pressed'),
            ),
            ExpressiveButton(
              label: 'Elevated',
              variant: ExpressiveButtonVariant.elevated,
              onPressed: () => _showFeedback('Elevated button pressed'),
            ),
            ExpressiveButton(
              label: 'Text',
              variant: ExpressiveButtonVariant.text,
              onPressed: () => _showFeedback('Text button pressed'),
            ),
          ],
        ),
        const SizedBox(height: 12),
        Wrap(
          spacing: 8,
          runSpacing: 8,
          children: [
            ExpressiveButton(
              label: 'With Icon',
              icon: const HugeIcon(icon: HugeIcons.strokeRoundedStar, size: 20),
              variant: ExpressiveButtonVariant.filled,
              onPressed: () => _showFeedback('Button with icon pressed'),
            ),
            ExpressiveButton(
              label: 'Next',
              trailingIcon: const HugeIcon(
                icon: HugeIcons.strokeRoundedArrowRight01,
                size: 20,
              ),
              variant: ExpressiveButtonVariant.tonal,
              onPressed: () => _showFeedback('Next button pressed'),
            ),
            ExpressiveButton(
              label: _btnLoading ? 'Loading...' : 'Load Data',
              isLoading: _btnLoading,
              variant: ExpressiveButtonVariant.outlined,
              onPressed: () {
                setState(() => _btnLoading = true);
                Future.delayed(const Duration(seconds: 2), () {
                  if (mounted) setState(() => _btnLoading = false);
                });
              },
            ),
            const ExpressiveButton(label: 'Disabled', onPressed: null),
          ],
        ),
        const SizedBox(height: 12),
        ExpressiveButton(
          label: 'Expanded Button (Full Width)',
          isExpanded: true,
          icon: const HugeIcon(
            icon: HugeIcons.strokeRoundedCheckmarkCircle01,
            size: 20,
          ),
          variant: ExpressiveButtonVariant.filled,
          onPressed: () => _showFeedback('Expanded button pressed'),
        ),
        const SizedBox(height: 24),
        _buildSectionHeader(
          'Split Button (ExpressiveSplitButton)',
          'Continuous primary action with an expandable secondary dropdown.',
        ),
        const SizedBox(height: 12),
        Wrap(
          spacing: 12,
          runSpacing: 12,
          children: [
            ExpressiveSplitButton(
              label: 'Save File',
              leadingIcon: const HugeIcon(
                icon: HugeIcons.strokeRoundedFloppyDisk,
                size: 20,
              ),
              variant: ExpressiveButtonVariant.filled,
              onPressed: () => _showFeedback('Save file'),
              onTrailingPressed: () => _showFeedback('More save options'),
            ),
            ExpressiveSplitButton(
              label: 'Export Data',
              variant: ExpressiveButtonVariant.tonal,
              onPressed: () => _showFeedback('Export data'),
              onTrailingPressed: () => _showFeedback('Export format'),
            ),
            ExpressiveSplitButton(
              label: 'Send',
              variant: ExpressiveButtonVariant.outlined,
              onPressed: () => _showFeedback('Send'),
              onTrailingPressed: () => _showFeedback('Schedule send'),
            ),
          ],
        ),
        const SizedBox(height: 24),
        _buildSectionHeader(
          'Segmented Picker (ExpressiveButtonGroup)',
          'Button group with single selection and continuous sliding pill indicator.',
        ),
        const SizedBox(height: 12),
        ExpressiveButtonGroup<String>(
          selectedValue: _selectedFrequency,
          onSelected: (val) {
            setState(() => _selectedFrequency = val);
            _showFeedback('Selected frequency: $val');
          },
          items: const [
            ExpressiveButtonGroupItem(
              value: 'Daily',
              label: 'Daily',
              icon: HugeIcon(icon: HugeIcons.strokeRoundedCalendar01, size: 18),
            ),
            ExpressiveButtonGroupItem(
              value: 'Weekly',
              label: 'Weekly',
              icon: HugeIcon(icon: HugeIcons.strokeRoundedCalendar02, size: 18),
            ),
            ExpressiveButtonGroupItem(
              value: 'Monthly',
              label: 'Monthly',
              icon: HugeIcon(icon: HugeIcons.strokeRoundedCalendar03, size: 18),
            ),
          ],
        ),
        const SizedBox(height: 24),
        _buildSectionHeader(
          'Icon Buttons (ExpressiveIconButton)',
          'Shapes: circular, squircle, and rounded rectangle, with badge options.',
        ),
        const SizedBox(height: 12),
        Wrap(
          spacing: 12,
          runSpacing: 12,
          crossAxisAlignment: WrapCrossAlignment.center,
          children: [
            ExpressiveIconButton.circle(
              icon: const HugeIcon(icon: HugeIcons.strokeRoundedFavourite),
              variant: ExpressiveIconButtonVariant.filled,
              tooltip: 'Favorite (Filled Circle)',
              onPressed: () => _showFeedback('Favorite pressed'),
            ),
            ExpressiveIconButton.circle(
              icon: const HugeIcon(icon: HugeIcons.strokeRoundedShare01),
              variant: ExpressiveIconButtonVariant.tonal,
              tooltip: 'Share (Tonal Circle)',
              onPressed: () => _showFeedback('Share pressed'),
            ),
            ExpressiveIconButton.circle(
              icon: const HugeIcon(icon: HugeIcons.strokeRoundedBookmark02),
              variant: ExpressiveIconButtonVariant.outlined,
              tooltip: 'Bookmark (Outlined Circle)',
              onPressed: () => _showFeedback('Bookmark pressed'),
            ),
            ExpressiveIconButton.squircle(
              icon: const HugeIcon(icon: HugeIcons.strokeRoundedCamera01),
              variant: ExpressiveIconButtonVariant.filled,
              tooltip: 'Camera (Squircle Filled)',
              onPressed: () => _showFeedback('Camera pressed'),
            ),
            ExpressiveIconButton.squircle(
              icon: const HugeIcon(icon: HugeIcons.strokeRoundedImage01),
              variant: ExpressiveIconButtonVariant.tonal,
              tooltip: 'Gallery (Squircle Tonal)',
              onPressed: () => _showFeedback('Gallery pressed'),
            ),
            ExpressiveIconButton(
              icon: const HugeIcon(
                icon: HugeIcons.strokeRoundedSlidersHorizontal,
              ),
              shape: ExpressiveIconButtonShape.roundedRectangle,
              variant: ExpressiveIconButtonVariant.filled,
              tooltip: 'Settings (Rounded Rect)',
              onPressed: () => _showFeedback('Settings pressed'),
            ),
            ExpressiveIconButton.circle(
              icon: const HugeIcon(icon: HugeIcons.strokeRoundedMail01),
              variant: ExpressiveIconButtonVariant.tonal,
              badgeText: '9+',
              tooltip: 'Messages with Badge',
              onPressed: () => _showFeedback('Messages: 9+ unread'),
            ),
            ExpressiveIconButton.circle(
              icon: const HugeIcon(icon: HugeIcons.strokeRoundedNotification01),
              showBadgeDot: true,
              tooltip: 'Alert with Dot',
              onPressed: () => _showFeedback('Active alert'),
            ),
          ],
        ),
        const SizedBox(height: 24),
        _buildSectionHeader(
          'Switches (ExpressiveSwitch)',
          'Capsule track with check/cross icons in the thumb and spring physics.',
        ),
        const SizedBox(height: 12),
        Row(
          children: [
            ExpressiveSwitch(
              value: _switchVal1,
              onChanged: (val) => setState(() => _switchVal1 = val),
            ),
            const SizedBox(width: 12),
            Text(
              _switchVal1 ? 'Enabled (With icons)' : 'Disabled (With icons)',
              style: const TextStyle(fontWeight: FontWeight.w500),
            ),
          ],
        ),
        const SizedBox(height: 12),
        Row(
          children: [
            ExpressiveSwitch(
              value: _switchVal2,
              showIcons: false,
              onChanged: (val) => setState(() => _switchVal2 = val),
            ),
            const SizedBox(width: 12),
            Text(
              _switchVal2 ? 'Enabled (No icons)' : 'Disabled (No icons)',
              style: const TextStyle(fontWeight: FontWeight.w500),
            ),
          ],
        ),
        const SizedBox(height: 24),
        _buildSectionHeader(
          'Sliders (ExpressiveSlider)',
          'Thick capsule track with pill thumb indicator and stepper buttons (+/-).',
        ),
        const SizedBox(height: 8),
        ExpressiveSlider(
          value: _sliderVal,
          min: 0,
          max: 100,
          title: 'Media volume',
          subtitle: 'Current level: ${_sliderVal.toInt()}%',
          onChanged: (val) => setState(() => _sliderVal = val),
        ),
        const SizedBox(height: 12),
        ExpressiveSlider(
          value: _sliderStepVal,
          min: 1,
          max: 5,
          divisions: 4,
          title: 'Graphics quality (Discrete)',
          subtitle: 'Level: ${_sliderStepVal.toInt()} of 5 stars',
          onChanged: (val) => setState(() => _sliderStepVal = val),
        ),
        const SizedBox(height: 80),
      ],
    );
  }

  // ---------------------------------------------------------------------------
  // TAB 2: CONTENT, CARDS AND LISTS
  // ---------------------------------------------------------------------------
  Widget _buildContentTab() {
    return ListView(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      children: [
        _buildSectionHeader(
          'Search Bar (ExpressiveSearchBar)',
          '28dp pill container with search icon and embedded trailing actions.',
        ),
        const SizedBox(height: 12),
        ExpressiveSearchBar(
          hintText: 'Search components, colors, or settings...',
          margin: EdgeInsets.zero,
          trailing: ExpressiveIconButton.circle(
            icon: const HugeIcon(icon: HugeIcons.strokeRoundedMic01, size: 20),
            tooltip: 'Voice search',
            onPressed: () => _showFeedback('Voice search activated'),
          ),
          onSubmitted: (query) => _showFeedback('Searching: "$query"'),
        ),
        const SizedBox(height: 12),
        ExpressiveSearchBar(
          hintText: 'Read-only mode (Tap to explore)',
          readOnly: true,
          margin: EdgeInsets.zero,
          trailing: const HugeIcon(
            icon: HugeIcons.strokeRoundedArrowRight01,
            size: 16,
          ),
          onTap: () => _showFeedback('Search modal opened'),
        ),
        const SizedBox(height: 24),
        _buildSectionHeader(
          'Multi-Browse Carousel (ExpressiveCarousel)',
          'Smooth horizontal scrolling with 24dp adaptive corners and spring physics.',
        ),
        const SizedBox(height: 12),
        ExpressiveCarousel(
          height: 160,
          itemExtent: 220,
          items: [
            ExpressiveCarouselItem(
              onTap: () => _showFeedback('Card 1 selected'),
              child: Container(
                decoration: BoxDecoration(
                  gradient: const LinearGradient(
                    colors: [Color(0xFF1E88E5), Color(0xFF42A5F5)],
                  ),
                  borderRadius: BorderRadius.circular(24),
                ),
                padding: const EdgeInsets.all(16),
                child: const Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    HugeIcon(
                      icon: HugeIcons.strokeRoundedPaintBoard,
                      color: Colors.white,
                      size: 32,
                    ),
                    Text(
                      'Material 3\nExpressive Theme',
                      style: TextStyle(
                        color: Colors.white,
                        fontWeight: FontWeight.bold,
                        fontSize: 16,
                      ),
                    ),
                  ],
                ),
              ),
            ),
            ExpressiveCarouselItem(
              onTap: () => _showFeedback('Card 2 selected'),
              child: Container(
                decoration: BoxDecoration(
                  gradient: const LinearGradient(
                    colors: [Color(0xFF7B1FA2), Color(0xFFBA68C8)],
                  ),
                  borderRadius: BorderRadius.circular(24),
                ),
                padding: const EdgeInsets.all(16),
                child: const Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    HugeIcon(
                      icon: HugeIcons.strokeRoundedMagicWand01,
                      color: Colors.white,
                      size: 32,
                    ),
                    Text(
                      'Spring Bouncy\nPhysics Motion',
                      style: TextStyle(
                        color: Colors.white,
                        fontWeight: FontWeight.bold,
                        fontSize: 16,
                      ),
                    ),
                  ],
                ),
              ),
            ),
            ExpressiveCarouselItem(
              onTap: () => _showFeedback('Card 3 selected'),
              child: Container(
                decoration: BoxDecoration(
                  gradient: const LinearGradient(
                    colors: [Color(0xFF00897B), Color(0xFF4DB6AC)],
                  ),
                  borderRadius: BorderRadius.circular(24),
                ),
                padding: const EdgeInsets.all(16),
                child: const Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    HugeIcon(
                      icon: HugeIcons.strokeRoundedCube,
                      color: Colors.white,
                      size: 32,
                    ),
                    Text(
                      '15 Native\nComponents & Tests',
                      style: TextStyle(
                        color: Colors.white,
                        fontWeight: FontWeight.bold,
                        fontSize: 16,
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ],
        ),
        const SizedBox(height: 24),
        _buildSectionHeader(
          'Card Group & List Tiles (ExpressiveCardGroup & ExpressiveListTile)',
          'Pixel Settings list style with vibrant circular badges and automatic dividers.',
        ),
        const SizedBox(height: 12),
        ExpressiveCardGroup(
          title: 'Connectivity Preferences',
          margin: EdgeInsets.zero,
          children: [
            ExpressiveListTile(
              leading: const ExpressiveBadge(
                icon: HugeIcon(
                  icon: HugeIcons.strokeRoundedWifi01,
                  color: Colors.white,
                  size: 20,
                ),
                backgroundColor: Color(0xFF1E88E5),
                foregroundColor: Colors.white,
              ),
              title: const Text('Internet & network'),
              subtitle: const Text('Home Wi-Fi 5GHz connected'),
              trailing: const HugeIcon(
                icon: HugeIcons.strokeRoundedArrowRight01,
                size: 20,
              ),
              onTap: () => _showFeedback('Wi-Fi settings'),
            ),
            ExpressiveListTile(
              leading: const ExpressiveBadge(
                icon: HugeIcon(
                  icon: HugeIcons.strokeRoundedBluetooth,
                  color: Colors.white,
                  size: 20,
                ),
                backgroundColor: Color(0xFF5E35B1),
                foregroundColor: Colors.white,
              ),
              title: const Text('Connected devices'),
              subtitle: const Text('Pixel Buds Pro headphones'),
              trailing: const HugeIcon(
                icon: HugeIcons.strokeRoundedArrowRight01,
                size: 20,
              ),
              onTap: () => _showFeedback('Bluetooth settings'),
            ),
            ExpressiveListTile(
              leading: const ExpressiveBadge(
                icon: HugeIcon(
                  icon: HugeIcons.strokeRoundedShield01,
                  color: Colors.white,
                  size: 20,
                ),
                backgroundColor: Color(0xFF2E7D32),
                foregroundColor: Colors.white,
              ),
              title: const Text('Security & privacy'),
              subtitle: const Text('Device protected and up to date'),
              trailing: const HugeIcon(
                icon: HugeIcons.strokeRoundedArrowRight01,
                size: 20,
              ),
              onTap: () => _showFeedback('Security settings'),
            ),
            ExpressiveListTile(
              leading: const ExpressiveBadge(
                icon: HugeIcon(
                  icon: HugeIcons.strokeRoundedMoon02,
                  color: Colors.white,
                  size: 20,
                ),
                backgroundColor: Color(0xFFEF6C00),
                foregroundColor: Colors.white,
              ),
              title: const Text('Dark theme'),
              subtitle: const Text('Toggle visual appearance'),
              showSplitDivider: true,
              onTap: widget.onToggleTheme,
              secondaryTrailing: ExpressiveSwitch(
                value: widget.isDarkMode,
                onChanged: (_) => widget.onToggleTheme(),
              ),
            ),
          ],
        ),
        const SizedBox(height: 24),
        _buildSectionHeader(
          'Standalone Card (ExpressiveCard)',
          'Elevated surface with smooth 24dp corners and responsive touch reaction.',
        ),
        const SizedBox(height: 12),
        ExpressiveCard(
          margin: EdgeInsets.zero,
          onTap: () => _showFeedback('Standalone card pressed'),
          child: Row(
            children: [
              const ExpressiveBadge(
                icon: HugeIcon(
                  icon: HugeIcons.strokeRoundedStar,
                  color: Colors.black87,
                  size: 20,
                ),
                backgroundColor: Colors.amber,
                foregroundColor: Colors.black87,
              ),
              const SizedBox(width: 16),
              const Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'Expressive Experience',
                      style: TextStyle(
                        fontWeight: FontWeight.bold,
                        fontSize: 16,
                      ),
                    ),
                    SizedBox(height: 4),
                    Text(
                      'Modern design language inspired by Material Design 3 Expressive guidelines.',
                      style: TextStyle(fontSize: 13),
                    ),
                  ],
                ),
              ),
              const HugeIcon(
                icon: HugeIcons.strokeRoundedArrowRight01,
                size: 20,
              ),
            ],
          ),
        ),
        const SizedBox(height: 80),
      ],
    );
  }

  // ---------------------------------------------------------------------------
  // TAB 3: PROGRESS AND MOTION TOKENS
  // ---------------------------------------------------------------------------
  Widget _buildProgressAndMotionTab() {
    return ListView(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      children: [
        _buildSectionHeader(
          'Material 3 Expressive Progress Indicators',
          'Wavy progress indicators in Determinate (Column 1) and Indeterminate (Column 2) modes.',
        ),
        const SizedBox(height: 16),
        Center(
          child: ConstrainedBox(
            constraints: const BoxConstraints(maxWidth: 760),
            child: Card(
              elevation: 0,
              color: Theme.of(context).colorScheme.surfaceContainerLowest,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(24),
                side: BorderSide(
                  color: Theme.of(context).colorScheme.outlineVariant
                      .withValues(alpha: 0.5),
                ),
              ),
              child: Padding(
                padding: const EdgeInsets.symmetric(
                  horizontal: 28,
                  vertical: 36,
                ),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    // Column 1: Determinate
                    Expanded(
                      child: Column(
                        children: [
                          Padding(
                            padding: const EdgeInsets.symmetric(horizontal: 16),
                            child: ExpressiveLinearProgressIndicator(
                              value: _sliderVal / 100,
                            ),
                          ),
                          const SizedBox(height: 36),
                          ExpressiveCircularProgressIndicator(
                            value: _sliderVal / 100,
                          ),
                          const SizedBox(height: 28),
                          Container(
                            width: 28,
                            height: 28,
                            decoration: BoxDecoration(
                              color: Theme.of(context).colorScheme.onSurface,
                              shape: BoxShape.circle,
                            ),
                            alignment: Alignment.center,
                            child: Text(
                              '1',
                              style: TextStyle(
                                color: Theme.of(context).colorScheme.surface,
                                fontWeight: FontWeight.bold,
                                fontSize: 13,
                              ),
                            ),
                          ),
                          const SizedBox(height: 8),
                          Text(
                            'Determinate (${_sliderVal.toInt()}%)',
                            style: Theme.of(context).textTheme.labelMedium
                                ?.copyWith(fontWeight: FontWeight.w600),
                          ),
                        ],
                      ),
                    ),
                    const SizedBox(width: 24),
                    // Column 2: Indeterminate
                    Expanded(
                      child: Column(
                        children: [
                          const Padding(
                            padding: EdgeInsets.symmetric(horizontal: 16),
                            child: ExpressiveLinearProgressIndicator(),
                          ),
                          const SizedBox(height: 36),
                          const ExpressiveCircularProgressIndicator(),
                          const SizedBox(height: 28),
                          Container(
                            width: 28,
                            height: 28,
                            decoration: BoxDecoration(
                              color: Theme.of(context).colorScheme.onSurface,
                              shape: BoxShape.circle,
                            ),
                            alignment: Alignment.center,
                            child: Text(
                              '2',
                              style: TextStyle(
                                color: Theme.of(context).colorScheme.surface,
                                fontWeight: FontWeight.bold,
                                fontSize: 13,
                              ),
                            ),
                          ),
                          const SizedBox(height: 8),
                          Text(
                            'Indeterminate',
                            style: Theme.of(context).textTheme.labelMedium
                                ?.copyWith(fontWeight: FontWeight.w600),
                          ),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            ),
          ),
        ),
        const SizedBox(height: 16),
        Text('Determinate Progress Control (${_sliderVal.toInt()}%):'),
        Slider(
          value: _sliderVal,
          max: 100,
          divisions: 100,
          label: '${_sliderVal.toInt()}%',
          onChanged: (v) => setState(() => _sliderVal = v),
        ),
        Wrap(
          spacing: 8,
          runSpacing: 8,
          alignment: WrapAlignment.center,
          children: [0.0, 25.0, 50.0, 75.0, 95.0, 100.0].map((preset) {
            return OutlinedButton(
              onPressed: () => setState(() => _sliderVal = preset),
              style: OutlinedButton.styleFrom(
                visualDensity: VisualDensity.compact,
              ),
              child: Text('${preset.toInt()}%'),
            );
          }).toList(),
        ),
        const SizedBox(height: 28),
        _buildSectionHeader(
          'Motion Physics & Curves (ExpressiveMotion & ExpressivePressable)',
          'Official animation tokens with spring bounce physics (Spring Physics).',
        ),
        const SizedBox(height: 12),
        _buildMotionCard(
          title: 'ExpressiveMotion.springBouncy',
          subtitle:
              'Cubic(0.34, 1.35, 0.64, 1) — Smooth and energetic overshoot.',
          scale: _scaleBouncy,
          curve: ExpressiveMotion.springBouncy,
          color: const Color(0xFF1E88E5),
          onTap: () async {
            setState(() => _scaleBouncy = 0.85);
            await Future.delayed(ExpressiveMotion.durationShort);
            if (mounted) setState(() => _scaleBouncy = 1.0);
          },
        ),
        const SizedBox(height: 12),
        _buildMotionCard(
          title: 'ExpressiveMotion.springGentle',
          subtitle: 'Cubic(0.2, 0, 0, 1) — Smooth and progressive damping.',
          scale: _scaleGentle,
          curve: ExpressiveMotion.springGentle,
          color: const Color(0xFF7B1FA2),
          onTap: () async {
            setState(() => _scaleGentle = 0.85);
            await Future.delayed(ExpressiveMotion.durationShort);
            if (mounted) setState(() => _scaleGentle = 1.0);
          },
        ),
        const SizedBox(height: 12),
        _buildMotionCard(
          title: 'ExpressiveMotion.emphasized',
          subtitle: 'Curves.easeInOutCubicEmphasized — State transition and morphing.',
          scale: _scaleSnappy,
          curve: ExpressiveMotion.emphasized,
          color: const Color(0xFF00897B),
          onTap: () async {
            setState(() => _scaleSnappy = 0.85);
            await Future.delayed(ExpressiveMotion.durationShort);
            if (mounted) setState(() => _scaleSnappy = 1.0);
          },
        ),
        const SizedBox(height: 16),
        ExpressivePressable(
          onTap: () => _showFeedback(
            'ExpressivePressable pressed with tactile feedback',
          ),
          child: Container(
            padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
            decoration: BoxDecoration(
              color: Theme.of(context).colorScheme.primaryContainer,
              borderRadius: BorderRadius.circular(16),
            ),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                HugeIcon(
                  icon: HugeIcons.strokeRoundedCursorPointer01,
                  color: Theme.of(context).colorScheme.onPrimaryContainer,
                ),
                const SizedBox(width: 8),
                Text(
                  'ExpressivePressable (Spring press effect)',
                  style: TextStyle(
                    fontWeight: FontWeight.bold,
                    color: Theme.of(context).colorScheme.onPrimaryContainer,
                  ),
                ),
              ],
            ),
          ),
        ),
        const SizedBox(height: 80),
      ],
    );
  }

  // ---------------------------------------------------------------------------
  // TAB 4: NAVIGATION AND FLOATING ACTION BUTTONS (FAB)
  // ---------------------------------------------------------------------------
  Widget _buildNavigationAndFabTab() {
    return ListView(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      children: [
        _buildSectionHeader(
          'Floating Action Buttons (ExpressiveFab & ExpressiveExtendedFab)',
          'Sizes: small (44dp), regular (56dp), large (80dp), and extended.',
        ),
        const SizedBox(height: 16),
        Wrap(
          spacing: 16,
          runSpacing: 16,
          crossAxisAlignment: WrapCrossAlignment.center,
          children: [
            ExpressiveFab(
              icon: const HugeIcon(icon: HugeIcons.strokeRoundedAdd01),
              size: ExpressiveFabSize.small,
              tooltip: 'Small FAB (44dp)',
              onPressed: () => _showFeedback('Small FAB pressed'),
            ),
            ExpressiveFab(
              icon: const HugeIcon(icon: HugeIcons.strokeRoundedEdit02),
              size: ExpressiveFabSize.regular,
              tooltip: 'Regular FAB (56dp)',
              onPressed: () => _showFeedback('Regular FAB pressed'),
            ),
            ExpressiveFab(
              icon: const HugeIcon(icon: HugeIcons.strokeRoundedPaintBrush01),
              size: ExpressiveFabSize.large,
              tooltip: 'Large FAB (80dp)',
              onPressed: () => _showFeedback('Large FAB pressed'),
            ),
          ],
        ),
        const SizedBox(height: 16),
        ExpressiveExtendedFab(
          icon: const HugeIcon(icon: HugeIcons.strokeRoundedTaskAdd01),
          label: 'New Expressive Task',
          onPressed: () => _showFeedback('Extended FAB pressed'),
        ),
        const SizedBox(height: 24),
        _buildSectionHeader(
          'Expandable Floating Menu (ExpressiveFabMenu)',
          'Quick action speed dial menu animated with spring bounce physics.',
        ),
        const SizedBox(height: 8),
        const ExpressiveCard(
          margin: EdgeInsets.zero,
          child: Row(
            children: [
              HugeIcon(icon: HugeIcons.strokeRoundedCursorPointer01, size: 28),
              SizedBox(width: 12),
              Expanded(
                child: Text(
                  'The "+" floating button located at the bottom-right corner expands an interactive ExpressiveFabMenu with quick action options.',
                  style: TextStyle(fontSize: 14),
                ),
              ),
            ],
          ),
        ),
        const SizedBox(height: 24),
        _buildSectionHeader(
          'Bottom Navigation Bar (ExpressiveNavigationBar)',
          'Pill indicator animated with spring bounce, badges, and tonal elevation.',
        ),
        const SizedBox(height: 8),
        const ExpressiveCard(
          margin: EdgeInsets.zero,
          child: Row(
            children: [
              HugeIcon(icon: HugeIcons.strokeRoundedLayers01, size: 28),
              SizedBox(width: 12),
              Expanded(
                child: Text(
                  'The active bottom navigation bar is an ExpressiveNavigationBar instance controlling the 4 tabs of this gallery.',
                  style: TextStyle(fontSize: 14),
                ),
              ),
            ],
          ),
        ),
        const SizedBox(height: 24),
        _buildSectionHeader(
          'Expressive Top App Bar (ExpressiveTopAppBar)',
          'Flat surface without unnecessary elevation, action support, and adaptive navigation back button.',
        ),
        const SizedBox(height: 8),
        ExpressiveCard(
          margin: EdgeInsets.zero,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              const Text(
                'Standard Variant vs Large Variant',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 15),
              ),
              const SizedBox(height: 6),
              const Text(
                'Allows configuring large titles, custom leading widgets, and automatic navigation buttons.',
                style: TextStyle(fontSize: 13),
              ),
              const SizedBox(height: 12),
              ExpressiveButton(
                label: 'View Large TopAppBar Demo',
                variant: ExpressiveButtonVariant.tonal,
                onPressed: () {
                  Navigator.of(context).push(
                    MaterialPageRoute(
                      builder: (context) => Scaffold(
                        appBar: ExpressiveTopAppBar.large(
                          titleText: 'Large TopAppBar',
                          actions: [
                            ExpressiveIconButton.circle(
                              icon: const HugeIcon(
                                icon: HugeIcons.strokeRoundedInformationCircle,
                              ),
                              onPressed: () => _showFeedback('Info modal'),
                            ),
                          ],
                        ),
                        body: const Center(
                          child: Text('Secondary screen with TopAppBar.large'),
                        ),
                      ),
                    ),
                  );
                },
              ),
            ],
          ),
        ),
        const SizedBox(height: 80),
      ],
    );
  }

  // ---------------------------------------------------------------------------
  // TAB 5: TYPOGRAPHY AND VARIABLE FONTS
  // ---------------------------------------------------------------------------
  Widget _buildTypographyTab() {
    final theme = Theme.of(context);
    const availableFonts = ExpressiveFonts.availableFonts;

    const fontDescriptions = <String, String>{
      'Google Sans Flex': "Modern geometric UI font with adaptive optical weights. Google's signature typeface.",
      'Merriweather': 'Designed to be a pleasant, highly readable serif on screens with mild contrast.',
      'Montserrat': 'Urban geometric sans-serif inspired by posters and signage of Buenos Aires.',
      'Nunito': 'Well-balanced rounded terminal sans-serif, warm, approachable, and playful.',
      'Open Sans': 'Neutral yet friendly humanist sans-serif optimized for mobile and web interfaces.',
      'Orbitron':
          'Geometric sci-fi display typeface with futuristic angular curves.',
      'Playfair Display':
          'High-contrast transitional serif with classical editorial elegance.',
      'Roboto': "Google's versatile neo-grotesque sans-serif with natural reading rhythm and clear shapes.",
      'Source Code Pro': 'Monospaced font family designed specifically for code editing and UI terminals.',
    };

    const fontCategories = <String, String>{
      'Google Sans Flex': 'Sans-Serif • Default',
      'Merriweather': 'Serif • Editorial',
      'Montserrat': 'Geometric • Punchy',
      'Nunito': 'Rounded • Friendly',
      'Open Sans': 'Humanist • Neutral',
      'Orbitron': 'Display • Sci-Fi',
      'Playfair Display': 'Serif • Classical',
      'Roboto': 'Neo-Grotesque • Workhorse',
      'Source Code Pro': 'Monospace • Code',
    };

    return ListView(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      children: [
        _buildSectionHeader(
          'Typography & Variable Fonts',
          '9 curated variable fonts bundled with zero network latency. Tap any font to apply it to the entire application.',
        ),
        const SizedBox(height: 16),

        // Active Font Status & Quick Switcher
        Container(
          padding: const EdgeInsets.all(16),
          decoration: BoxDecoration(
            color: theme.colorScheme.primaryContainer.withValues(alpha: 0.35),
            borderRadius: BorderRadius.circular(24),
            border: Border.all(
              color: theme.colorScheme.primary.withValues(alpha: 0.2),
            ),
          ),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Row(
                children: [
                  Container(
                    padding: const EdgeInsets.all(8),
                    decoration: BoxDecoration(
                      color: theme.colorScheme.primary,
                      shape: BoxShape.circle,
                    ),
                    child: const HugeIcon(
                      icon: HugeIcons.strokeRoundedTextFont,
                      color: Colors.white,
                      size: 20,
                    ),
                  ),
                  const SizedBox(width: 12),
                  Expanded(
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          'Active Application Font',
                          style: theme.textTheme.labelMedium?.copyWith(
                            color: theme.colorScheme.onSurfaceVariant,
                          ),
                        ),
                        Text(
                          availableFonts.entries
                              .firstWhere(
                                (e) => e.value == widget.selectedFont,
                                orElse: () => availableFonts.entries.first,
                              )
                              .key,
                          style: theme.textTheme.titleMedium?.copyWith(
                            fontWeight: FontWeight.bold,
                            color: theme.colorScheme.primary,
                          ),
                        ),
                      ],
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 14),
              Text(
                'Quick switcher:',
                style: theme.textTheme.labelSmall?.copyWith(
                  fontWeight: FontWeight.w600,
                ),
              ),
              const SizedBox(height: 8),
              Wrap(
                spacing: 6,
                runSpacing: 6,
                children: availableFonts.entries.map((entry) {
                  final isSelected = widget.selectedFont == entry.value;
                  return ChoiceChip(
                    label: Text(entry.key),
                    selected: isSelected,
                    onSelected: (_) {
                      widget.onFontChanged(entry.value);
                      _showFeedback('Theme font set to ${entry.key}');
                    },
                  );
                }).toList(),
              ),
            ],
          ),
        ),
        const SizedBox(height: 20),

        // Interactive Custom Text Input
        _buildSectionHeader(
          'Live Preview Text',
          'Type custom text below to preview it dynamically across all 9 fonts.',
        ),
        const SizedBox(height: 10),
        TextField(
          controller: _customTextController,
          decoration: InputDecoration(
            hintText: 'Type sample text...',
            filled: true,
            fillColor: theme.colorScheme.surfaceContainerHighest.withValues(
              alpha: 0.5,
            ),
            border: OutlineInputBorder(
              borderRadius: BorderRadius.circular(16),
              borderSide: BorderSide.none,
            ),
            prefixIconConstraints: const BoxConstraints(
              minWidth: 40,
              minHeight: 40,
            ),
            prefixIcon: const SizedBox(
              width: 40,
              height: 40,
              child: Center(
                child: HugeIcon(icon: HugeIcons.strokeRoundedEdit02, size: 20),
              ),
            ),
            suffixIcon: IconButton(
              icon: const HugeIcon(
                icon: HugeIcons.strokeRoundedCancel01,
                size: 18,
              ),
              onPressed: () {
                setState(() {
                  _customTextController.text =
                      'The quick brown fox jumps over the lazy dog.';
                });
              },
            ),
          ),
          onChanged: (_) => setState(() {}),
        ),
        const SizedBox(height: 24),

        // Specimen cards for each font
        _buildSectionHeader(
          'Bundled Font Specimens',
          'Inspect letterforms, optical weights, and glyph details for each variable font family.',
        ),
        const SizedBox(height: 12),

        ...availableFonts.entries.map((entry) {
          final fontName = entry.key;
          final fontFamily = entry.value;
          final isSelected = widget.selectedFont == fontFamily;
          final category = fontCategories[fontName] ?? 'Variable Font';
          final description = fontDescriptions[fontName] ?? '';
          final previewText = _customTextController.text.trim().isEmpty
              ? 'The quick brown fox jumps over the lazy dog.'
              : _customTextController.text;

          return Container(
            margin: const EdgeInsets.only(bottom: 16),
            padding: const EdgeInsets.all(18),
            decoration: BoxDecoration(
              color: isSelected
                  ? theme.colorScheme.primaryContainer.withValues(alpha: 0.25)
                  : theme.colorScheme.surfaceContainerLow,
              borderRadius: BorderRadius.circular(24),
              border: Border.all(
                color: isSelected
                    ? theme.colorScheme.primary
                    : theme.colorScheme.outlineVariant.withValues(alpha: 0.4),
                width: isSelected ? 2 : 1,
              ),
            ),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                // Header with font name, category, and active badge
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    Expanded(
                      child: Column(
                        crossAxisAlignment: CrossAxisAlignment.start,
                        children: [
                          Text(
                            fontName,
                            style: TextStyle(
                              fontFamily: fontFamily,
                              fontSize: 20,
                              fontWeight: FontWeight.bold,
                              color: isSelected
                                  ? theme.colorScheme.primary
                                  : theme.colorScheme.onSurface,
                            ),
                          ),
                          const SizedBox(height: 2),
                          Text(
                            category,
                            style: theme.textTheme.labelSmall?.copyWith(
                              color: theme.colorScheme.onSurfaceVariant,
                            ),
                          ),
                        ],
                      ),
                    ),
                    if (isSelected)
                      Container(
                        padding: const EdgeInsets.symmetric(
                          horizontal: 10,
                          vertical: 4,
                        ),
                        decoration: BoxDecoration(
                          color: theme.colorScheme.primary,
                          borderRadius: BorderRadius.circular(20),
                        ),
                        child: const Row(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            HugeIcon(
                              icon: HugeIcons.strokeRoundedCheckmarkBadge01,
                              color: Colors.white,
                              size: 14,
                            ),
                            SizedBox(width: 4),
                            Text(
                              'Active',
                              style: TextStyle(
                                color: Colors.white,
                                fontSize: 11,
                                fontWeight: FontWeight.bold,
                              ),
                            ),
                          ],
                        ),
                      ),
                  ],
                ),
                const SizedBox(height: 8),
                Text(
                  description,
                  style: theme.textTheme.bodySmall?.copyWith(
                    color: theme.colorScheme.onSurfaceVariant,
                  ),
                ),
                const Divider(height: 24),

                // Live Preview in this font
                Text(
                  previewText,
                  style: TextStyle(
                    fontFamily: fontFamily,
                    fontSize: 18,
                    fontWeight: FontWeight.w600,
                  ),
                ),
                const SizedBox(height: 12),

                // Character spectrum
                Container(
                  padding: const EdgeInsets.symmetric(
                    horizontal: 12,
                    vertical: 8,
                  ),
                  decoration: BoxDecoration(
                    color: theme.colorScheme.surfaceContainerHighest.withValues(
                      alpha: 0.4,
                    ),
                    borderRadius: BorderRadius.circular(12),
                  ),
                  child: Text(
                    r'Aa Bb Cc Gg Jj Qq Ww Zz 0123456789 &€$@#%',
                    style: TextStyle(
                      fontFamily: fontFamily,
                      fontSize: 13,
                      letterSpacing: 0.5,
                      color: theme.colorScheme.onSurfaceVariant,
                    ),
                  ),
                ),
                const SizedBox(height: 16),

                // Weights Showcase
                Text(
                  'Weight variations:',
                  style: theme.textTheme.labelSmall?.copyWith(
                    fontWeight: FontWeight.w600,
                  ),
                ),
                const SizedBox(height: 6),
                Wrap(
                  spacing: 12,
                  runSpacing: 6,
                  children: [
                    Text(
                      'Light (300)',
                      style: TextStyle(
                        fontFamily: fontFamily,
                        fontWeight: FontWeight.w300,
                        fontSize: 13,
                      ),
                    ),
                    Text(
                      'Regular (400)',
                      style: TextStyle(
                        fontFamily: fontFamily,
                        fontWeight: FontWeight.w400,
                        fontSize: 13,
                      ),
                    ),
                    Text(
                      'Medium (500)',
                      style: TextStyle(
                        fontFamily: fontFamily,
                        fontWeight: FontWeight.w500,
                        fontSize: 13,
                      ),
                    ),
                    Text(
                      'Bold (700)',
                      style: TextStyle(
                        fontFamily: fontFamily,
                        fontWeight: FontWeight.w700,
                        fontSize: 13,
                      ),
                    ),
                    Text(
                      'Italic',
                      style: TextStyle(
                        fontFamily: fontFamily,
                        fontStyle: FontStyle.italic,
                        fontSize: 13,
                      ),
                    ),
                  ],
                ),
                const SizedBox(height: 16),

                // Action to set as app font
                Align(
                  alignment: Alignment.centerRight,
                  child: ExpressiveButton(
                    label: isSelected ? 'Active App Font' : 'Apply to Gallery',
                    icon: HugeIcon(
                      icon: isSelected
                          ? HugeIcons.strokeRoundedCheckmarkBadge01
                          : HugeIcons.strokeRoundedCheckmarkCircle02,
                      size: 16,
                    ),
                    variant: isSelected
                        ? ExpressiveButtonVariant.filled
                        : ExpressiveButtonVariant.tonal,
                    onPressed: () {
                      widget.onFontChanged(fontFamily);
                      _showFeedback('$fontName applied to the gallery theme');
                    },
                  ),
                ),
              ],
            ),
          );
        }),

        const SizedBox(height: 16),

        // Type Hierarchy in Current Theme
        _buildSectionHeader(
          'Active Type Scale Hierarchy',
          'Standard Material 3 Expressive typography scale rendered in the active font.',
        ),
        const SizedBox(height: 12),
        Container(
          padding: const EdgeInsets.all(18),
          decoration: BoxDecoration(
            color: theme.colorScheme.surfaceContainerLow,
            borderRadius: BorderRadius.circular(24),
            border: Border.all(
              color: theme.colorScheme.outlineVariant.withValues(alpha: 0.4),
            ),
          ),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(
                'Display Large',
                style: theme.textTheme.displayLarge?.copyWith(fontSize: 32),
              ),
              const Divider(height: 20),
              Text('Headline Medium', style: theme.textTheme.headlineMedium),
              const Divider(height: 20),
              Text('Title Large', style: theme.textTheme.titleLarge),
              const Divider(height: 20),
              Text('Body Large', style: theme.textTheme.bodyLarge),
              const Divider(height: 20),
              Text('Label Medium', style: theme.textTheme.labelMedium),
            ],
          ),
        ),
        const SizedBox(height: 80),
      ],
    );
  }

  // ---------------------------------------------------------------------------
  // HELPERS
  // ---------------------------------------------------------------------------
  Widget _buildSectionHeader(String title, String subtitle) {
    final theme = Theme.of(context);
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          title,
          style: theme.textTheme.titleMedium?.copyWith(
            fontWeight: FontWeight.bold,
          ),
        ),
        const SizedBox(height: 2),
        Text(
          subtitle,
          style: theme.textTheme.bodySmall?.copyWith(
            color: theme.colorScheme.onSurfaceVariant,
          ),
        ),
      ],
    );
  }

  Widget _buildMotionCard({
    required String title,
    required String subtitle,
    required double scale,
    required Curve curve,
    required Color color,
    required VoidCallback onTap,
  }) {
    return GestureDetector(
      onTap: onTap,
      child: AnimatedScale(
        scale: scale,
        duration: ExpressiveMotion.durationShort,
        curve: curve,
        child: Container(
          padding: const EdgeInsets.all(16),
          decoration: BoxDecoration(
            color: color.withValues(alpha: 0.12),
            borderRadius: BorderRadius.circular(20),
            border: Border.all(color: color.withValues(alpha: 0.3)),
          ),
          child: Row(
            children: [
              Container(
                width: 40,
                height: 40,
                decoration: BoxDecoration(color: color, shape: BoxShape.circle),
                child: const HugeIcon(
                  icon: HugeIcons.strokeRoundedCursorPointer01,
                  color: Colors.white,
                  size: 20,
                ),
              ),
              const SizedBox(width: 14),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      title,
                      style: TextStyle(
                        fontWeight: FontWeight.bold,
                        fontSize: 14,
                        color: color,
                      ),
                    ),
                    const SizedBox(height: 2),
                    Text(subtitle, style: const TextStyle(fontSize: 12)),
                  ],
                ),
              ),
              const Text(
                'Tap!',
                style: TextStyle(fontWeight: FontWeight.bold, fontSize: 12),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
1
likes
0
points
435
downloads

Publisher

unverified uploader

Weekly Downloads

A modern Flutter design system providing Material 3 Expressive components, spring motions, tactile feedback, and unified theme styles.

Repository (GitHub)
View/report issues

Topics

#design-system #material3 #ui-components #expressive #widget

License

unknown (license)

Dependencies

flutter, flutter_localizations, hugeicons, intl, material_ui

More

Packages that depend on expressive_kit