s_modal 1.1.0 copy "s_modal: ^1.1.0" to clipboard
s_modal: ^1.1.0 copied to clipboard

A package for displaying highly customizable modal overlays including sheets, dialogs, and snackbars... with independent lifecycle management.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:s_ink_button/s_ink_button.dart';
import 'package:s_modal/s_modal.dart';
import 'package:states_rebuilder/states_rebuilder.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return Modal.activator(
      child: MaterialApp(
        title: 'S Modal Example',
        theme: ThemeData(
          colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
          useMaterial3: true,
        ),
        home: const MyHomePage(),
      ),
    );
  }
}

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

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  Alignment _selectedPosition = Alignment.bottomCenter;
  SnackbarDisplayMode _selectedDisplayMode = SnackbarDisplayMode.replace;
  String _selectedSnackbarType = 'Simple';
  int _snackbarCounter = 1;
  double? _snackbarWidth = 260.0; // null = default width

  // Snackbar Dismissal & Barrier Config
  bool _snackbarIsDismissible = true; // default: true
  Color _snackbarBarrierColor =
      Colors.transparent; // default: none (transparent)

  // Duration Indicator Config
  bool _showDurationTimer = true;
  DurationIndicatorDirection _durationTimerDirection =
      DurationIndicatorDirection.leftToRight;
  Color? _durationTimerColor;

  // Snackbar Offset Config (for 'With Offset' type) - using ValueNotifier for decoupled state
  late final ValueNotifier<SnackbarOffset> _snackbarOffsetNotifier =
      ValueNotifier(SnackbarOffset(offsetX: 100.0, offsetY: 260.0));

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

  // Bottom Sheet Config
  String _selectedSheetContentType = 'Default';
  bool _sheetExpandable = false;
  SheetPosition _sheetPosition = SheetPosition.bottom;
  double _sheetSize = 300;
  // Sheet background color selector
  Color _sheetBackgroundColor = Colors.white;

  // Dialog Config
  String _selectedDialogType = 'Info';
  bool _dialogDraggable = false;
  bool _dialogWithOffset = false;

  // Global Modal Config
  bool _globalDismissable = true;
  bool _globalBlockBackgroundInteraction = false;
  Color _globalBarrierColor = Colors.black.withValues(alpha: 0.3);
  bool _globalBlur = true;
  double _globalBlurAmount = 3.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('S Modal Package Examples'),
      ),
      body: SingleChildScrollView(
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              // Global Modal Settings Section
              Text(
                'Global Modal Settings',
                style: Theme.of(context).textTheme.headlineSmall,
              ),
              const SizedBox(height: 12),

              Card(
                elevation: 2,
                color: Theme.of(context)
                    .colorScheme
                    .primaryContainer
                    .withValues(alpha: 0.3),
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        'These settings apply to all modals shown from configurators',
                        style: TextStyle(
                          color: Colors.grey.shade600,
                          fontSize: 12,
                        ),
                      ),
                      const SizedBox(height: 12),
                      SwitchListTile(
                        title: const Text('Dismissable on Background Tap'),
                        subtitle: Text(
                          _globalDismissable
                              ? 'Tapping background dismisses modal'
                              : 'Background tap does nothing',
                          style: TextStyle(
                              fontSize: 12, color: Colors.grey.shade600),
                        ),
                        value: _globalDismissable,
                        onChanged: (value) {
                          setState(() {
                            _globalDismissable = value;
                          });
                          // Live update if any modal is active
                          if (Modal.isDialogActive) {
                            Modal.updateParams(
                                id: Modal.activeDialogId!,
                                isDismissable: value);
                          }
                          if (Modal.isSheetActive) {
                            Modal.updateParams(
                                id: Modal.activeSheetId!, isDismissable: value);
                          }
                        },
                        dense: true,
                        contentPadding: EdgeInsets.zero,
                      ),
                      SwitchListTile(
                        title: const Text('Block Background Interaction'),
                        subtitle: Text(
                          _globalBlockBackgroundInteraction
                              ? 'Buttons behind modal are blocked'
                              : 'Buttons behind modal can still be tapped',
                          style: TextStyle(
                              fontSize: 12, color: Colors.grey.shade600),
                        ),
                        value: _globalBlockBackgroundInteraction,
                        onChanged: (value) {
                          setState(() {
                            _globalBlockBackgroundInteraction = value;
                          });
                          // Live update if any modal is active
                          if (Modal.isDialogActive) {
                            Modal.updateParams(
                                id: Modal.activeDialogId!,
                                blockBackgroundInteraction: value);
                          }
                          if (Modal.isSheetActive) {
                            Modal.updateParams(
                                id: Modal.activeSheetId!,
                                blockBackgroundInteraction: value);
                          }
                        },
                        dense: true,
                        contentPadding: EdgeInsets.zero,
                      ),
                      SwitchListTile(
                        title: const Text('Background Blur'),
                        value: _globalBlur,
                        onChanged: (value) {
                          setState(() {
                            _globalBlur = value;
                          });
                          // Live update if any modal is active
                          if (Modal.isDialogActive) {
                            Modal.updateParams(
                              id: Modal.activeDialogId!,
                              shouldBlurBackground: value,
                              blurAmount: value ? _globalBlurAmount : 0,
                            );
                          }
                          if (Modal.isSheetActive) {
                            Modal.updateParams(
                              id: Modal.activeSheetId!,
                              shouldBlurBackground: value,
                              blurAmount: value ? _globalBlurAmount : 0,
                            );
                          }
                        },
                        dense: true,
                        contentPadding: EdgeInsets.zero,
                      ),
                      if (_globalBlur)
                        Padding(
                          padding: const EdgeInsets.only(bottom: 8.0),
                          child: Row(
                            children: [
                              const Text('Blur Amount: '),
                              Expanded(
                                child: Slider(
                                  value: _globalBlurAmount.clamp(0.0, 10.0),
                                  min: 0.0,
                                  max: 10.0,
                                  onChanged: (value) {
                                    setState(() {
                                      _globalBlurAmount = value;
                                    });
                                    // Live update if any modal is active
                                    if (Modal.isDialogActive) {
                                      Modal.updateParams(
                                          id: Modal.activeDialogId!,
                                          blurAmount: value);
                                    }
                                    if (Modal.isSheetActive) {
                                      Modal.updateParams(
                                          id: Modal.activeSheetId!,
                                          blurAmount: value);
                                    }
                                  },
                                ),
                              ),
                              Text(_globalBlurAmount.toStringAsFixed(1)),
                            ],
                          ),
                        ),
                      const SizedBox(height: 8),
                      Text('Barrier Color',
                          style: Theme.of(context).textTheme.bodyMedium),
                      const SizedBox(height: 4),
                      Wrap(
                        spacing: 8,
                        children: [
                          _buildColorChoice(
                            color: Colors.black.withValues(alpha: 0.3),
                            label: 'Default',
                            selectedColor: _globalBarrierColor,
                            onSelect: (c) {
                              setState(() => _globalBarrierColor = c);
                              if (Modal.isDialogActive) {
                                Modal.updateParams(
                                    id: Modal.activeDialogId!, barrierColor: c);
                              }
                              if (Modal.isSheetActive) {
                                Modal.updateParams(
                                    id: Modal.activeSheetId!, barrierColor: c);
                              }
                            },
                          ),
                          _buildColorChoice(
                            color: Colors.blue.withValues(alpha: 0.3),
                            label: 'Blue',
                            selectedColor: _globalBarrierColor,
                            onSelect: (c) {
                              setState(() => _globalBarrierColor = c);
                              if (Modal.isDialogActive) {
                                Modal.updateParams(
                                    id: Modal.activeDialogId!, barrierColor: c);
                              }
                              if (Modal.isSheetActive) {
                                Modal.updateParams(
                                    id: Modal.activeSheetId!, barrierColor: c);
                              }
                            },
                          ),
                          _buildColorChoice(
                            color: Colors.red.withValues(alpha: 0.3),
                            label: 'Red',
                            selectedColor: _globalBarrierColor,
                            onSelect: (c) {
                              setState(() => _globalBarrierColor = c);
                              if (Modal.isDialogActive) {
                                Modal.updateParams(
                                    id: Modal.activeDialogId!, barrierColor: c);
                              }
                              if (Modal.isSheetActive) {
                                Modal.updateParams(
                                    id: Modal.activeSheetId!, barrierColor: c);
                              }
                            },
                          ),
                          _buildColorChoice(
                            color: Colors.transparent,
                            label: 'None',
                            selectedColor: _globalBarrierColor,
                            onSelect: (c) {
                              setState(() => _globalBarrierColor = c);
                              if (Modal.isDialogActive) {
                                Modal.updateParams(
                                    id: Modal.activeDialogId!, barrierColor: c);
                              }
                              if (Modal.isSheetActive) {
                                Modal.updateParams(
                                    id: Modal.activeSheetId!, barrierColor: c);
                              }
                            },
                          ),
                        ],
                      ),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 24),

              // Sheet Configurator Section (unified for all sheet types)
              Text(
                'Sheet Configurator',
                style: Theme.of(context).textTheme.headlineSmall,
              ),
              const SizedBox(height: 12),

              Card(
                elevation: 2,
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      // Position Selector (Top, Bottom, Left, Right)
                      Text('Position',
                          style: Theme.of(context).textTheme.titleSmall),
                      const SizedBox(height: 8),
                      Wrap(
                        spacing: 8,
                        runSpacing: 8,
                        children: SheetPosition.values.map((position) {
                          return ChoiceChip(
                            label: Text(position.name.toUpperCase()),
                            selected: _sheetPosition == position,
                            onSelected: (selected) {
                              if (selected) {
                                setState(() => _sheetPosition = position);
                                // Live update if any sheet is active
                                if (Modal.isSheetActive ||
                                    Modal.isSideSheetActive ||
                                    Modal.isTopSheetActive) {
                                  _showConfiguredSheet();
                                }
                              }
                            },
                          );
                        }).toList(),
                      ),
                      const SizedBox(height: 16),

                      // Content Type Selector
                      Text('Content Type',
                          style: Theme.of(context).textTheme.titleSmall),
                      const SizedBox(height: 8),
                      Wrap(
                        spacing: 8,
                        runSpacing: 8,
                        children: [
                          'Default',
                          'Menu',
                          'Settings',
                          'Profile',
                          'Custom',
                          'List'
                        ].map((type) {
                          return ChoiceChip(
                            label: Text(type),
                            selected: _selectedSheetContentType == type,
                            onSelected: (selected) {
                              if (selected) {
                                setState(
                                    () => _selectedSheetContentType = type);
                                // Live update if any sheet is active
                                if (Modal.isSheetActive ||
                                    Modal.isSideSheetActive ||
                                    Modal.isTopSheetActive) {
                                  _showConfiguredSheet();
                                }
                              }
                            },
                          );
                        }).toList(),
                      ),
                      const SizedBox(height: 16),

                      // Size Slider
                      Text(
                          _sheetPosition == SheetPosition.left ||
                                  _sheetPosition == SheetPosition.right
                              ? 'Width'
                              : 'Height',
                          style: Theme.of(context).textTheme.titleSmall),
                      const SizedBox(height: 8),
                      Row(
                        children: [
                          Expanded(
                            child: Slider(
                              value: _sheetSize,
                              min: 150,
                              max: 500,
                              divisions: 14,
                              label: '${_sheetSize.toInt()}px',
                              onChanged: (value) {
                                setState(() => _sheetSize = value);
                                // Update the active sheet size in real-time
                                if (Modal.isSheetActive ||
                                    Modal.isSideSheetActive ||
                                    Modal.isTopSheetActive) {
                                  Modal.updateParams(
                                    id: 'configured-sheet',
                                    size: value,
                                  );
                                }
                              },
                            ),
                          ),
                          Text('${_sheetSize.toInt()}px'),
                        ],
                      ),
                      const SizedBox(height: 16),

                      // Expandable Toggle
                      SwitchListTile(
                        title: const Text('Expandable'),
                        subtitle: Text(_sheetExpandable
                            ? 'Can expand by dragging'
                            : 'Fixed size'),
                        value: _sheetExpandable,
                        onChanged: (value) {
                          setState(() => _sheetExpandable = value);
                          if (Modal.isSheetActive ||
                              Modal.isSideSheetActive ||
                              Modal.isTopSheetActive) {
                            Modal.updateParams(
                              id: 'configured-sheet',
                              isExpandable: value,
                              expandedPercentageSize: value ? 85.0 : 0.0,
                            );
                          }
                        },
                        dense: true,
                        contentPadding: EdgeInsets.zero,
                      ),
                      const SizedBox(height: 12),

                      // Background Color Selector
                      Text('Background Color',
                          style: Theme.of(context).textTheme.titleSmall),
                      const SizedBox(height: 8),
                      Wrap(
                        spacing: 8,
                        runSpacing: 8,
                        children: [
                          _buildColorChoice(
                            color: Colors.white,
                            label: 'White',
                            selectedColor: _sheetBackgroundColor,
                            onSelect: (c) {
                              setState(() => _sheetBackgroundColor = c);
                              // Live update if configured sheet is open
                              Modal.updateParams(
                                  id: 'configured-sheet', backgroundColor: c);
                            },
                          ),
                          _buildColorChoice(
                            color: Colors.grey.shade50,
                            label: 'Light',
                            selectedColor: _sheetBackgroundColor,
                            onSelect: (c) {
                              setState(() => _sheetBackgroundColor = c);
                              Modal.updateParams(
                                  id: 'configured-sheet', backgroundColor: c);
                            },
                          ),
                          _buildColorChoice(
                            color: Colors.blue.shade50,
                            label: 'Blue',
                            selectedColor: _sheetBackgroundColor,
                            onSelect: (c) {
                              setState(() => _sheetBackgroundColor = c);
                              Modal.updateParams(
                                  id: 'configured-sheet', backgroundColor: c);
                            },
                          ),
                          _buildColorChoice(
                            color: Colors.green.shade50,
                            label: 'Green',
                            selectedColor: _sheetBackgroundColor,
                            onSelect: (c) {
                              setState(() => _sheetBackgroundColor = c);
                              Modal.updateParams(
                                  id: 'configured-sheet', backgroundColor: c);
                            },
                          ),
                          _buildColorChoice(
                            color: Colors.purple.shade50,
                            label: 'Purple',
                            selectedColor: _sheetBackgroundColor,
                            onSelect: (c) {
                              setState(() => _sheetBackgroundColor = c);
                              Modal.updateParams(
                                  id: 'configured-sheet', backgroundColor: c);
                            },
                          ),
                          _buildColorChoice(
                            color: Colors.amber.shade50,
                            label: 'Amber',
                            selectedColor: _sheetBackgroundColor,
                            onSelect: (c) {
                              setState(() => _sheetBackgroundColor = c);
                              Modal.updateParams(
                                  id: 'configured-sheet', backgroundColor: c);
                            },
                          ),
                        ],
                      ),
                      const SizedBox(height: 16),

                      // Show Button
                      SizedBox(
                        width: double.infinity,
                        child: ElevatedButton.icon(
                          icon: Icon(_getSheetIcon(_sheetPosition)),
                          label: Text(
                              'SHOW ${_getSheetTypeName(_sheetPosition).toUpperCase()}'),
                          style: ElevatedButton.styleFrom(
                            backgroundColor:
                                Theme.of(context).colorScheme.primary,
                            foregroundColor:
                                Theme.of(context).colorScheme.onPrimary,
                            padding: const EdgeInsets.symmetric(vertical: 16),
                          ),
                          onPressed: _showConfiguredSheet,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 24),

              // Dialog Configurator Section
              Text(
                'Dialog Configurator',
                style: Theme.of(context).textTheme.headlineSmall,
              ),
              const SizedBox(height: 12),

              Card(
                elevation: 2,
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      // Type Selector
                      Text('Content Type',
                          style: Theme.of(context).textTheme.titleSmall),
                      const SizedBox(height: 8),
                      Wrap(
                        spacing: 8,
                        runSpacing: 8,
                        children: ['Info', 'Confirm', 'Form', 'Callbacks']
                            .map((type) {
                          return ChoiceChip(
                            label: Text(type),
                            selected: _selectedDialogType == type,
                            onSelected: (selected) {
                              if (selected) {
                                setState(() => _selectedDialogType = type);
                                // Live update if dialog is active
                                if (Modal.isDialogActive) {
                                  _showConfiguredDialog();
                                }
                              }
                            },
                          );
                        }).toList(),
                      ),
                      const SizedBox(height: 16),

                      // Options (live-update active dialog)
                      Text('Options',
                          style: Theme.of(context).textTheme.titleSmall),
                      const SizedBox(height: 8),
                      SwitchListTile(
                        title: const Text('Draggable'),
                        value: _dialogDraggable,
                        onChanged: (value) {
                          setState(() {
                            _dialogDraggable = value;
                          });
                          // Live update if dialog is active
                          if (Modal.isDialogActive) {
                            Modal.updateParams(
                                id: 'configured-dialog', isDraggable: value);
                          }
                        },
                        dense: true,
                        contentPadding: EdgeInsets.zero,
                      ),
                      SwitchListTile(
                        title: const Text('With Offset'),
                        subtitle: Text(_dialogWithOffset
                            ? 'Dialog positioned with offset (50, 30)'
                            : 'Dialog uses default position'),
                        value: _dialogWithOffset,
                        onChanged: (value) {
                          setState(() {
                            _dialogWithOffset = value;
                          });
                          // Live update if dialog is active
                          if (Modal.isDialogActive) {
                            Modal.updateParams(
                              id: 'configured-dialog',
                              offset: value ? const Offset(50, 30) : null,
                            );
                          }
                        },
                        dense: true,
                        contentPadding: EdgeInsets.zero,
                      ),
                      const SizedBox(height: 16),

                      // Show Button
                      SizedBox(
                        width: double.infinity,
                        child: ElevatedButton.icon(
                          icon: const Icon(Icons.chat_bubble),
                          label: const Text('SHOW DIALOG'),
                          style: ElevatedButton.styleFrom(
                            backgroundColor:
                                Theme.of(context).colorScheme.secondary,
                            foregroundColor:
                                Theme.of(context).colorScheme.onSecondary,
                            padding: const EdgeInsets.symmetric(vertical: 16),
                          ),
                          onPressed: _showConfiguredDialog,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 24),

              // Snackbar Configurator Section
              Text(
                'Snackbar Configurator',
                style: Theme.of(context).textTheme.headlineSmall,
              ),
              const SizedBox(height: 12),

              Card(
                elevation: 2,
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      // Position Selector (hidden when 'With Offset' is selected since offset replaces alignment)
                      if (_selectedSnackbarType != 'With Offset') ...[
                        Text('Position',
                            style: Theme.of(context).textTheme.titleSmall),
                        const SizedBox(height: 8),
                        _buildPositionSelector(),
                        const SizedBox(height: 16),
                      ],

                      // Display Mode Selector
                      Text('Display Mode',
                          style: Theme.of(context).textTheme.titleSmall),
                      const SizedBox(height: 8),
                      Wrap(
                        spacing: 8,
                        runSpacing: 8,
                        children: SnackbarDisplayMode.values.map((mode) {
                          return ChoiceChip(
                            label: Text(mode.name),
                            selected: _selectedDisplayMode == mode,
                            onSelected: (selected) {
                              if (selected) {
                                // Dismiss all snackbars when changing display mode
                                Modal.dismissAllSnackbars();
                                setState(() => _selectedDisplayMode = mode);
                              }
                            },
                          );
                        }).toList(),
                      ),
                      const SizedBox(height: 16),

                      // Type Selector
                      Text('Content Type',
                          style: Theme.of(context).textTheme.titleSmall),
                      const SizedBox(height: 8),
                      Wrap(
                        spacing: 8,
                        runSpacing: 8,
                        children: [
                          'Simple',
                          'Success',
                          'Error',
                          'Custom',
                          'With Offset'
                        ].map((type) {
                          return ChoiceChip(
                            label: Text(type),
                            selected: _selectedSnackbarType == type,
                            onSelected: (selected) {
                              if (selected) {
                                // Dismiss all snackbars when changing content type
                                Modal.dismissAllSnackbars();
                                setState(() => _selectedSnackbarType = type);
                              }
                            },
                          );
                        }).toList(),
                      ),
                      const SizedBox(height: 24),

                      // Offset Controls (only show when 'With Offset' is selected)
                      // Replaces the alignment-based Position selector
                      if (_selectedSnackbarType == 'With Offset') ...[
                        Text('Position (Offset)',
                            style: Theme.of(context).textTheme.titleSmall),
                        const SizedBox(height: 8),
                        Text(
                          'Drag the pin to set snackbar position',
                          style: TextStyle(
                            color: Colors.grey.shade600,
                            fontSize: 12,
                          ),
                        ),
                        const SizedBox(height: 12),
                        // Draggable Position Picker
                        _buildOffsetPositionPicker(),
                        const SizedBox(height: 8),
                        // Show current coordinates
                        ValueListenableBuilder<SnackbarOffset>(
                          valueListenable: _snackbarOffsetNotifier,
                          builder: (context, offset, _) => Text(
                            'Position: (${offset.offsetX.toInt()}, ${offset.offsetY.toInt()})',
                            style: TextStyle(
                              color: Colors.grey.shade700,
                              fontSize: 13,
                              fontWeight: FontWeight.w500,
                            ),
                          ),
                        ),
                        const SizedBox(height: 24),
                      ],

                      // Duration Indicator Settings
                      Text('Duration Indicator',
                          style: Theme.of(context).textTheme.titleSmall),
                      const SizedBox(height: 8),

                      // Show/Hide Toggle
                      SwitchListTile(
                        title: const Text('Show Duration Timer'),
                        subtitle: const Text('Linear progress bar at bottom'),
                        value: _showDurationTimer,
                        onChanged: (value) {
                          setState(() => _showDurationTimer = value);
                        },
                        contentPadding: EdgeInsets.zero,
                        dense: true,
                      ),

                      // Direction selector (only show if timer is enabled)
                      if (_showDurationTimer) ...[
                        const SizedBox(height: 8),
                        Row(
                          children: [
                            const Text('Direction: '),
                            const SizedBox(width: 8),
                            SizedBox(
                              width: 100,
                              child: ChoiceChip(
                                label: const Text('← Left'),
                                selected: _durationTimerDirection ==
                                    DurationIndicatorDirection.leftToRight,
                                onSelected: (selected) {
                                  if (selected) {
                                    setState(() => _durationTimerDirection =
                                        DurationIndicatorDirection.leftToRight);
                                  }
                                },
                              ),
                            ),
                            const SizedBox(width: 8),
                            SizedBox(
                              width: 100,
                              child: ChoiceChip(
                                label: const Text('Right →'),
                                selected: _durationTimerDirection ==
                                    DurationIndicatorDirection.rightToLeft,
                                onSelected: (selected) {
                                  if (selected) {
                                    setState(() => _durationTimerDirection =
                                        DurationIndicatorDirection.rightToLeft);
                                  }
                                },
                              ),
                            ),
                          ],
                        ),
                        const SizedBox(height: 8),
                        // Color selector
                        Row(
                          children: [
                            const Text('Color: '),
                            const SizedBox(width: 8),
                            _buildColorChip(null, 'Default'),
                            const SizedBox(width: 4),
                            _buildColorChip(Colors.grey.shade300, 'Grey'),
                            const SizedBox(width: 4),
                            _buildColorChip(Colors.cyan, 'Cyan'),
                            const SizedBox(width: 4),
                            _buildColorChip(Colors.pink, 'Pink'),
                          ],
                        ),
                      ],

                      const SizedBox(height: 24),

                      // Width Slider
                      Text('Snackbar Width',
                          style: Theme.of(context).textTheme.titleSmall),
                      const SizedBox(height: 8),
                      Row(
                        children: [
                          Expanded(
                            child: Slider(
                              value: _snackbarWidth ?? 260.0, // 260 = default
                              min: 260.0,
                              max: 600.0,
                              divisions: 17,
                              label: _snackbarWidth == null ||
                                      _snackbarWidth == 260.0
                                  ? 'Default'
                                  : '${_snackbarWidth!.toInt()}px',
                              onChanged: (value) {
                                setState(() {
                                  // 260 means default (null)
                                  _snackbarWidth =
                                      value == 260.0 ? 260.0 : value;
                                });
                              },
                            ),
                          ),
                          SizedBox(
                            width: 80,
                            child: Text(
                              _snackbarWidth == null
                                  ? 'Default'
                                  : '${_snackbarWidth!.toInt()}px',
                              textAlign: TextAlign.right,
                            ),
                          ),
                        ],
                      ),
                      const SizedBox(height: 24),

                      // Dismissible Toggle
                      SwitchListTile(
                        title: const Text('Dismissible'),
                        subtitle: Text(
                          _snackbarIsDismissible
                              ? 'Can be swiped to dismiss'
                              : 'Cannot be dismissed (no auto-dismiss)',
                          style: TextStyle(
                            fontSize: 12,
                            color: Colors.grey.shade600,
                          ),
                        ),
                        value: _snackbarIsDismissible,
                        onChanged: (value) {
                          setState(() => _snackbarIsDismissible = value);
                        },
                        contentPadding: EdgeInsets.zero,
                        dense: true,
                      ),
                      const SizedBox(height: 16),

                      // Barrier Color Selector
                      Text('Barrier Color',
                          style: Theme.of(context).textTheme.titleSmall),
                      const SizedBox(height: 8),
                      Row(
                        children: [
                          _buildBarrierColorChip(
                              Colors.transparent, 'None', true),
                          const SizedBox(width: 4),
                          _buildBarrierColorChip(
                              Colors.black.withValues(alpha: 0.3),
                              'Black',
                              false),
                          const SizedBox(width: 4),
                          _buildBarrierColorChip(
                              Colors.blue.withValues(alpha: 0.2),
                              'Blue',
                              false),
                          const SizedBox(width: 4),
                          _buildBarrierColorChip(
                              Colors.red.withValues(alpha: 0.2), 'Red', false),
                          const SizedBox(width: 4),
                          _buildBarrierColorChip(
                              Colors.green.withValues(alpha: 0.2),
                              'Green',
                              false),
                        ],
                      ),
                      const SizedBox(height: 24),

                      // Show Button
                      SizedBox(
                        width: double.infinity,
                        child: ElevatedButton.icon(
                          icon: const Icon(Icons.visibility),
                          label: const Text('SHOW SNACKBAR'),
                          style: ElevatedButton.styleFrom(
                            backgroundColor:
                                Theme.of(context).colorScheme.primary,
                            foregroundColor:
                                Theme.of(context).colorScheme.onPrimary,
                            padding: const EdgeInsets.symmetric(vertical: 16),
                          ),
                          onPressed: _showConfiguredSnackbar,
                        ),
                      ),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 24),

              // Modal Mixing Configurator Section
              Text(
                'Modal Mixing Configurator',
                style: Theme.of(context).textTheme.headlineSmall,
              ),
              const SizedBox(height: 12),

              Card(
                elevation: 2,
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(
                        'Test how different modal types interact',
                        style: TextStyle(
                          color: Colors.grey.shade600,
                          fontSize: 12,
                        ),
                      ),
                      const SizedBox(height: 16),

                      // Bottom Sheet + Snackbar
                      GestureDetector(
                        onTap: () =>
                            _showMixedModalDemo('BottomSheet + Snackbar'),
                        child: _buildCardButton(
                          icon: Icons.layers,
                          text: 'Bottom Sheet + Snackbar',
                          subtitle: 'Show sheet, then trigger snackbar',
                        ),
                      ),
                      const SizedBox(height: 8),

                      // Dialog + Snackbar
                      GestureDetector(
                        onTap: () => _showMixedModalDemo('Dialog + Snackbar'),
                        child: _buildCardButton(
                          icon: Icons.chat_bubble,
                          text: 'Dialog + Snackbar',
                          subtitle: 'Show dialog, then trigger snackbar',
                        ),
                      ),
                      const SizedBox(height: 8),

                      // Snackbar + Bottom Sheet
                      GestureDetector(
                        onTap: () =>
                            _showMixedModalDemo('Snackbar + Bottom Sheet'),
                        child: _buildCardButton(
                          icon: Icons.vertical_align_bottom,
                          text: 'Snackbar + Bottom Sheet',
                          subtitle:
                              'Show snackbar, then automatically open bottomsheet',
                        ),
                      ),
                      const SizedBox(height: 8),

                      // Snackbar with Modal Buttons
                      GestureDetector(
                        onTap: () =>
                            _showMixedModalDemo('Snackbar with Modal Buttons'),
                        child: _buildCardButton(
                          icon: Icons.touch_app,
                          text: 'Snackbar with Modal Buttons',
                          subtitle: 'Snackbar with buttons to open modals',
                        ),
                      ),
                      const SizedBox(height: 8),

                      // Side Sheet + Snackbar
                      GestureDetector(
                        onTap: () =>
                            _showMixedModalDemo('SideSheet + Snackbar'),
                        child: _buildCardButton(
                          icon: Icons.view_sidebar,
                          text: 'Side Sheet + Snackbar',
                          subtitle: 'Show side sheet, then trigger snackbar',
                        ),
                      ),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 24),

              // Reactive State Examples Section
              Text(
                'Reactive State Examples',
                style: Theme.of(context).textTheme.headlineSmall,
              ),
              const SizedBox(height: 12),

              Card(
                elevation: 2,
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('Demo Type',
                          style: Theme.of(context).textTheme.titleSmall),
                      const SizedBox(height: 12),

                      // Reactive Counter Modal using ModalBuilder (with hot reload)
                      ModalBuilder(
                        builder: () => _buildReactiveCounterContent(),
                        size: 350,
                        shouldBlurBackground: _globalBlur,
                        isDismissable: _globalDismissable,
                        blockBackgroundInteraction:
                            _globalBlockBackgroundInteraction,
                        child: _buildCardButton(
                          icon: Icons.add_circle,
                          text: 'Reactive Counter (ModalBuilder)',
                          subtitle: 'Uses ModalBuilder with hot reload support',
                        ),
                      ),
                      const SizedBox(height: 8),

                      // Reactive Counter Modal using Modal.show()
                      GestureDetector(
                        onTap: _showReactiveCounterWithModalShow,
                        child: _buildCardButton(
                          icon: Icons.refresh,
                          text: 'Reactive Counter (Modal.show)',
                          subtitle: 'Uses Modal.show() with OnReactive',
                        ),
                      ),
                      const SizedBox(height: 8),

                      // Reactive Form Modal
                      ModalBuilder.dialog(
                        builder: () => _buildReactiveFormContent(),
                        size: 400,
                        shouldBlurBackground: _globalBlur,
                        isDismissable: _globalDismissable,
                        blockBackgroundInteraction:
                            _globalBlockBackgroundInteraction,
                        child: _buildCardButton(
                          icon: Icons.edit,
                          text: 'Reactive Form Dialog',
                          subtitle: 'Form validation with reactive state',
                        ),
                      ),
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 24),

              // Live Update Examples Section
              Text(
                'Live Update Examples',
                style: Theme.of(context).textTheme.headlineSmall,
              ),
              const SizedBox(height: 12),

              Card(
                elevation: 2,
                child: Padding(
                  padding: const EdgeInsets.all(16.0),
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text('Modal.updateParams() Demos',
                          style: Theme.of(context).textTheme.titleSmall),
                      const SizedBox(height: 8),
                      Text(
                        'Update modal properties live without recreation',
                        style: TextStyle(
                          color: Colors.grey.shade600,
                          fontSize: 12,
                        ),
                      ),
                      const SizedBox(height: 16),

                      // Dynamic Parameter Updates
                      GestureDetector(
                        onTap: _showDynamicBottomSheet,
                        child: _buildCardButton(
                          icon: Icons.tune,
                          text: 'Dynamic Bottom Sheet Updates',
                          subtitle: 'Blur, height, color, dismissable',
                        ),
                      ),
                      const SizedBox(height: 8),

                      // Modal Type Morphing
                      GestureDetector(
                        onTap: _showMorphingModal,
                        child: _buildCardButton(
                          icon: Icons.transform,
                          text: 'Modal Type Morphing',
                          subtitle: 'Switch between bottom sheet and dialog',
                        ),
                      ),
                      const SizedBox(height: 8),

                      // Position Animation
                      GestureDetector(
                        onTap: _showPositionUpdatingDialog,
                        child: _buildCardButton(
                          icon: Icons.open_with,
                          text: 'Dialog Position Updates',
                          subtitle: 'Move dialog to different positions',
                        ),
                      ),
                      const SizedBox(height: 8),

                      // Snackbar Live Updates
                      GestureDetector(
                        onTap: _showLiveUpdatingSnackbar,
                        child: _buildCardButton(
                          icon: Icons.notifications_active,
                          text: 'Live Updating Snackbar',
                          subtitle: 'Countdown with color changes',
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  /// Builds a consistent button style for card sections
  Widget _buildCardButton({
    required IconData icon,
    required String text,
    String? subtitle,
  }) {
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
      decoration: BoxDecoration(
        color: Colors.grey.shade50,
        borderRadius: BorderRadius.circular(8),
        border: Border.all(color: Colors.grey.shade200),
      ),
      child: Row(
        children: [
          Container(
            padding: const EdgeInsets.all(8),
            decoration: BoxDecoration(
              color: Theme.of(context).colorScheme.primaryContainer,
              borderRadius: BorderRadius.circular(8),
            ),
            child: Icon(
              icon,
              size: 20,
              color: Theme.of(context).colorScheme.onPrimaryContainer,
            ),
          ),
          const SizedBox(width: 12),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  text,
                  style: const TextStyle(
                    fontWeight: FontWeight.w500,
                    fontSize: 14,
                  ),
                ),
                if (subtitle != null) ...[
                  const SizedBox(height: 2),
                  Text(
                    subtitle,
                    style: TextStyle(
                      color: Colors.grey.shade600,
                      fontSize: 12,
                    ),
                  ),
                ],
              ],
            ),
          ),
          Icon(
            Icons.chevron_right,
            color: Colors.grey.shade400,
          ),
        ],
      ),
    );
  }

  // ============================================
  // Builder methods for modal content (hot-reloadable!)
  // ============================================

  /// Builds blurred bottom sheet content
  // ignore: unused_element
  Widget _buildBlurredBottomSheetContent() {
    return Padding(
      padding: const EdgeInsets.all(20),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          const Icon(Icons.blur_on, size: 48, color: Colors.blue),
          const SizedBox(height: 16),
          Text(
            'Blurred Background',
            style: Theme.of(context).textTheme.headlineSmall,
          ),
          const SizedBox(height: 12),
          const Text(
            'Notice how the content behind is blurred! This creates a nice depth effect.',
            textAlign: TextAlign.center,
          ),
          const SizedBox(height: 24),
          ElevatedButton(
            onPressed: () => Modal.dismissBottomSheet(),
            child: const Text('Close'),
          ),
        ],
      ),
    );
  }

  /// Builds custom blur slider content
  Widget _buildCustomBlurContent() {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 3),
      child: StatefulBuilder(
        builder: (context, setModalState) {
          return Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              // Header with icon and title
              Row(
                children: [
                  Container(
                    padding: const EdgeInsets.all(12),
                    decoration: BoxDecoration(
                      gradient: LinearGradient(
                        colors: [
                          Colors.purple.shade400,
                          Colors.purple.shade600
                        ],
                        begin: Alignment.topLeft,
                        end: Alignment.bottomRight,
                      ),
                      borderRadius: BorderRadius.circular(12),
                    ),
                    child:
                        const Icon(Icons.tune, size: 28, color: Colors.white),
                  ),
                  const SizedBox(width: 16),
                  Expanded(
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text(
                          'Blur Intensity',
                          style:
                              Theme.of(context).textTheme.titleLarge?.copyWith(
                                    fontWeight: FontWeight.bold,
                                  ),
                        ),
                        const SizedBox(height: 4),
                        Text(
                          'Adjust background blur effect',
                          style:
                              Theme.of(context).textTheme.bodySmall?.copyWith(
                                    color: Colors.grey.shade600,
                                  ),
                        ),
                      ],
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 28),

              // Blur value display
              Container(
                padding:
                    const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
                decoration: BoxDecoration(
                  color: Colors.purple.shade50,
                  borderRadius: BorderRadius.circular(16),
                  border: Border.all(color: Colors.purple.shade100),
                ),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Icon(Icons.blur_on,
                        color: Colors.purple.shade400, size: 32),
                    const SizedBox(width: 12),
                    Text(
                      _globalBlurAmount.toStringAsFixed(1),
                      style: TextStyle(
                        fontSize: 40,
                        fontWeight: FontWeight.bold,
                        color: Colors.purple.shade700,
                      ),
                    ),
                    const SizedBox(width: 4),
                    Text(
                      'px',
                      style: TextStyle(
                        fontSize: 18,
                        color: Colors.purple.shade400,
                      ),
                    ),
                  ],
                ),
              ),
              const SizedBox(height: 24),

              // Slider with labels
              Row(
                children: [
                  Text('0',
                      style:
                          TextStyle(color: Colors.grey.shade700, fontSize: 12)),
                  Expanded(
                    child: SliderTheme(
                      data: SliderTheme.of(context).copyWith(
                        activeTrackColor: Colors.purple.shade400,
                        inactiveTrackColor: Colors.purple.shade100,
                        thumbColor: Colors.purple.shade600,
                        overlayColor: Colors.purple.withValues(alpha: 0.2),
                        trackHeight: 6,
                        thumbShape:
                            const RoundSliderThumbShape(enabledThumbRadius: 10),
                      ),
                      child: Slider(
                        value: _globalBlurAmount,
                        min: 0.0,
                        max: 15.0,
                        onChanged: (value) {
                          setModalState(() => _globalBlurAmount = value);
                          setState(() => _globalBlurAmount = value);
                          Modal.updateParams(
                              id: 'configured-bottom-sheet', blurAmount: value);
                        },
                      ),
                    ),
                  ),
                  Text('15',
                      style:
                          TextStyle(color: Colors.grey.shade700, fontSize: 12)),
                ],
              ),
              const SizedBox(height: 28),

              // Action buttons
              Row(
                children: [
                  Expanded(
                    child: OutlinedButton.icon(
                      icon: const Icon(Icons.refresh, size: 18),
                      label: const Text('Reset'),
                      style: OutlinedButton.styleFrom(
                        foregroundColor: Colors.purple.shade600,
                        side: BorderSide(color: Colors.purple.shade300),
                        padding: const EdgeInsets.symmetric(vertical: 12),
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(10),
                        ),
                      ),
                      onPressed: () {
                        setModalState(() => _globalBlurAmount = 3.0);
                        setState(() => _globalBlurAmount = 3.0);
                        Modal.updateParams(
                            id: 'configured-bottom-sheet', blurAmount: 3.0);
                      },
                    ),
                  ),
                  const SizedBox(width: 12),
                  Expanded(
                    child: ElevatedButton(
                      style: ElevatedButton.styleFrom(
                        backgroundColor: Colors.purple.shade600,
                        foregroundColor: Colors.white,
                        padding: const EdgeInsets.symmetric(vertical: 12),
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(10),
                        ),
                      ),
                      onPressed: () => Modal.dismissBottomSheet(),
                      child: const Text('Done'),
                    ),
                  ),
                ],
              ),
            ],
          );
        },
      ),
    );
  }

  /// Builds expandable sheet content
  Widget _buildExpandableSheetContent() {
    return ListView(
      shrinkWrap: true,
      children: [
        const Padding(
          padding: EdgeInsets.all(16),
          child: Text(
            'Drag Up to Expand',
            textAlign: TextAlign.center,
            style: TextStyle(fontWeight: FontWeight.bold),
          ),
        ),
        ...List.generate(
          8,
          (index) => ListTile(
            leading: CircleAvatar(
              child: Text('${index + 1}'),
            ),
            title: Text('Option ${index + 1}'),
            subtitle: const Text('Select this option'),
            onTap: () {
              Modal.dismissBottomSheet();
            },
          ),
        ),
      ],
    );
  }

  /// Builds the dialog content - extracted to a method for hot reload support
  Widget _buildDialogContent() {
    return AnimatedContainer(
      duration: const Duration(milliseconds: 300),
      decoration: BoxDecoration(
        color: Colors.brown.shade100,
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: Colors.black, width: 0.5),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withValues(alpha: 0.1),
            blurRadius: 8,
            offset: const Offset(0, 2),
          ),
        ],
      ),
      child: Padding(
        padding: const EdgeInsets.all(24),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Icon(Icons.info, size: 48, color: Colors.blue),
            const SizedBox(height: 16),
            Text(
              'Information',
              style: Theme.of(context).textTheme.headlineSmall,
            ),
            const SizedBox(height: 12),
            const Text(
              'This is a dialog modal. It appears centered on the screen.',
              textAlign: TextAlign.center,
            ),
            const SizedBox(height: 24),
            ElevatedButton(
              onPressed: () => Modal.dismissDialog(),
              child: const Text('Got it'),
            ),
          ],
        ),
      ),
    );
  }

  /// Builds confirmation dialog content
  Widget _buildConfirmationDialogContent() {
    return Dialog(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),
        side: BorderSide(
          color: Colors.black.withValues(alpha: 0.2),
          width: 1,
        ),
      ),
      shadowColor: Colors.black,
      child: Padding(
        padding: const EdgeInsets.all(15),
        child: SizedBox(
          width: 350,
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              const Icon(Icons.question_mark, size: 48, color: Colors.orange),
              const SizedBox(height: 16),
              Text(
                'Confirm Action',
                style: Theme.of(context).textTheme.headlineSmall,
              ),
              const SizedBox(height: 12),
              const Text(
                'Are you sure you want to proceed with this action?',
                textAlign: TextAlign.center,
              ),
              const SizedBox(height: 24),
              Row(
                mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                children: [
                  TextButton(
                    onPressed: () {
                      Modal.dismissDialog();
                    },
                    child: const Text('Cancel'),
                  ),
                  ElevatedButton(
                    onPressed: () {
                      Modal.dismissDialog();
                    },
                    child: const Text('Confirm'),
                  ),
                ],
              ),
            ],
          ),
        ),
      ),
    );
  }

  /// Builds callbacks demo content
  Widget _buildCallbacksContent() {
    return Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        border: Border.all(color: Colors.black.withValues(alpha: 0.3)),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withValues(alpha: 0.1),
            blurRadius: 8,
            offset: const Offset(0, 2),
          ),
        ],
      ),
      padding: const EdgeInsets.all(16),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Text(
            'Callbacks Demo',
            style: Theme.of(context).textTheme.headlineSmall,
          ),
          const Icon(Icons.notifications_active, size: 48, color: Colors.green),
          const SizedBox(height: 16),
          const SizedBox(height: 12),
          const Text(
            'Close this dialog to trigger onDismiss callback',
            textAlign: TextAlign.center,
          ),
          const SizedBox(height: 16),
          const Text(
            'The example callback calls to show a Modal snackbar',
            textAlign: TextAlign.center,
            style: TextStyle(fontSize: 12, fontStyle: FontStyle.italic),
          ),
          const SizedBox(height: 15),
          ElevatedButton(
            onPressed: () => Modal.dismissDialog(),
            child: const Text('Close'),
          ),
        ],
      ),
    );
  }

  // ============================================
  // Snackbar Examples
  // ============================================

  /// Builds a success snackbar
  Widget _buildSuccessSnackbarContent(String snackbarId) {
    return Container(
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      decoration: BoxDecoration(
        gradient: LinearGradient(
          colors: [Colors.green.shade600, Colors.green.shade700],
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
        ),
        borderRadius: BorderRadius.circular(12),
        boxShadow: [
          BoxShadow(
            color: Colors.green.withValues(alpha: 0.3),
            blurRadius: 8,
            offset: const Offset(0, 2),
          ),
        ],
      ),
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
        child: Row(
          children: [
            Container(
              padding: const EdgeInsets.all(6),
              decoration: BoxDecoration(
                color: Colors.white.withValues(alpha: 0.2),
                shape: BoxShape.circle,
              ),
              child: const Icon(Icons.check, color: Colors.white, size: 20),
            ),
            const SizedBox(width: 14),
            const Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text(
                    'Success!',
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: 15,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  SizedBox(height: 2),
                  Text(
                    'Your changes have been saved',
                    style: TextStyle(color: Colors.white70, fontSize: 13),
                  ),
                ],
              ),
            ),
            GestureDetector(
              onTap: () => Modal.dismissById(snackbarId),
              child: const Icon(Icons.close, color: Colors.white70, size: 20),
            ),
          ],
        ),
      ),
    );
  }

  /// Builds an error snackbar
  Widget _buildErrorSnackbarContent(String snackbarId) {
    return Container(
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      decoration: BoxDecoration(
        gradient: LinearGradient(
          colors: [Colors.red.shade600, Colors.red.shade700],
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
        ),
        borderRadius: BorderRadius.circular(12),
        boxShadow: [
          BoxShadow(
            color: Colors.red.withValues(alpha: 0.3),
            blurRadius: 8,
            offset: const Offset(0, 2),
          ),
        ],
      ),
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
        child: Row(
          children: [
            Container(
              padding: const EdgeInsets.all(6),
              decoration: BoxDecoration(
                color: Colors.white.withValues(alpha: 0.2),
                shape: BoxShape.circle,
              ),
              child: const Icon(Icons.error_outline,
                  color: Colors.white, size: 20),
            ),
            const SizedBox(width: 14),
            const Expanded(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                mainAxisSize: MainAxisSize.min,
                children: [
                  Text(
                    'Error',
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: 15,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  SizedBox(height: 2),
                  Text(
                    'Something went wrong. Please try again.',
                    style: TextStyle(color: Colors.white70, fontSize: 13),
                  ),
                ],
              ),
            ),
            GestureDetector(
              onTap: () => Modal.dismissById(snackbarId),
              child: const Icon(Icons.close, color: Colors.white70, size: 20),
            ),
          ],
        ),
      ),
    );
  }

  /// Builds a snackbar with action button
  Widget _buildActionSnackbarContent(String snackbarId) {
    return Container(
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      decoration: BoxDecoration(
        color: Colors.grey.shade900,
        borderRadius: BorderRadius.circular(12),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withValues(alpha: 0.3),
            blurRadius: 8,
            offset: const Offset(0, 2),
          ),
        ],
      ),
      child: Padding(
        padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
        child: Row(
          children: [
            const Icon(Icons.delete_outline, color: Colors.white, size: 24),
            const SizedBox(width: 12),
            const Expanded(
              child: Text(
                'Item deleted',
                style: TextStyle(color: Colors.white, fontSize: 14),
              ),
            ),
            TextButton(
              onPressed: () {
                // Dismiss this specific snackbar by its ID
                Modal.dismissById(snackbarId);
              },
              style: TextButton.styleFrom(
                foregroundColor: Colors.amber,
                padding: const EdgeInsets.symmetric(horizontal: 12),
              ),
              child: const Text(
                'UNDO',
                style: TextStyle(fontWeight: FontWeight.bold),
              ),
            ),
          ],
        ),
      ),
    );
  }

  // ============================================
  // Mixed Modal Demo Builders (for hot reload support)
  // ============================================

  /// Builds the bottom sheet content for 'BottomSheet + Snackbar' demo
  Widget _buildBottomSheetWithSnackbarContent() {
    return Padding(
      padding: const EdgeInsets.all(20),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          const Icon(Icons.layers, size: 48, color: Colors.blue),
          const SizedBox(height: 16),
          Text(
            'Bottom Sheet Actives',
            style: Theme.of(context).textTheme.headlineSmall,
          ),
          const SizedBox(height: 12),
          const Text(
            'Now click the button below to show a snackbar on top!',
            textAlign: TextAlign.center,
          ),
          const SizedBox(height: 24),
          ElevatedButton.icon(
            icon: const Icon(Icons.notifications),
            label: const Text('Show Snackbar'),
            onPressed: () {
              Modal.showSnackbar(
                text: 'I am a snackbar on top of the bottom sheet!',
                position: Alignment.topCenter,
                backgroundColor: Colors.blue.shade800,
                duration: const Duration(seconds: 3),
              );
            },
          ),
          const SizedBox(height: 12),
          TextButton(
            onPressed: () => Modal.dismissBottomSheet(),
            child: const Text('Close Sheet'),
          ),
        ],
      ),
    );
  }

  /// Builds the dialog content for 'Dialog + Snackbar' demo
  Widget _buildDialogWithSnackbarContent() {
    return Container(
      padding: const EdgeInsets.all(24),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: Colors.purple.shade100),
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          const Icon(Icons.chat_bubble, size: 48, color: Colors.purple),
          const SizedBox(height: 16),
          Text(
            'Dialog Active',
            style: Theme.of(context).textTheme.headlineSmall,
          ),
          const SizedBox(height: 12),
          const Text(
            'Click below to show a snackbar. It should appear above this dialog.',
            textAlign: TextAlign.center,
          ),
          const SizedBox(height: 24),
          ElevatedButton.icon(
            icon: const Icon(Icons.notifications),
            label: const Text('Show Snackbar'),
            onPressed: () {
              Modal.showSnackbar(
                text: 'I am a snackbar on top of the dialog!',
                position: Alignment.bottomCenter,
                backgroundColor: Colors.purple.shade800,
                duration: const Duration(seconds: 3),
              );
            },
          ),
          const SizedBox(height: 12),
          TextButton(
            onPressed: () => Modal.dismissDialog(),
            child: const Text('Close Dialog'),
          ),
        ],
      ),
    );
  }

  /// Builds the bottom sheet content for 'Snackbar + Bottom Sheet' demo
  Widget _buildBottomSheetAfterSnackbarContent() {
    return Padding(
      padding: const EdgeInsets.all(20),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          const Icon(Icons.vertical_align_bottom,
              size: 48, color: Colors.orange),
          const SizedBox(height: 16),
          Text(
            'Bottom Sheet Opened',
            style: Theme.of(context).textTheme.headlineSmall,
          ),
          const SizedBox(height: 12),
          const Text(
            'The snackbar should still be visible (or have finished).',
            textAlign: TextAlign.center,
          ),
          const SizedBox(height: 24),
          ElevatedButton(
            onPressed: () => Modal.dismissBottomSheet(),
            child: const Text('Close Sheet'),
          ),
        ],
      ),
    );
  }

  /// Builds bottom sheet content opened from snackbar button
  Widget _buildBottomSheetFromSnackbarContent() {
    return Padding(
      padding: const EdgeInsets.all(20),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          const Icon(Icons.layers, size: 48, color: Colors.indigo),
          const SizedBox(height: 16),
          Text(
            'Opened from Snackbar!',
            style: Theme.of(context).textTheme.headlineSmall,
          ),
          const SizedBox(height: 12),
          const Text(
            'This bottom sheet was triggered by a snackbar button. The snackbar should still be visible above.',
            textAlign: TextAlign.center,
          ),
          const SizedBox(height: 24),
          ElevatedButton(
            onPressed: () => Modal.dismissBottomSheet(),
            child: const Text('Close Sheet'),
          ),
        ],
      ),
    );
  }

  /// Builds dialog content opened from snackbar button
  Widget _buildDialogFromSnackbarContent() {
    return Container(
      padding: const EdgeInsets.all(24),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: Colors.indigo.shade100),
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          const Icon(Icons.chat_bubble, size: 48, color: Colors.indigo),
          const SizedBox(height: 16),
          Text(
            'Opened from Snackbar!',
            style: Theme.of(context).textTheme.headlineSmall,
          ),
          const SizedBox(height: 12),
          const Text(
            'This dialog was triggered by a snackbar button. The snackbar should still be visible.',
            textAlign: TextAlign.center,
          ),
          const SizedBox(height: 24),
          ElevatedButton(
            onPressed: () => Modal.dismissDialog(),
            child: const Text('Close Dialog'),
          ),
        ],
      ),
    );
  }

  /// Builds the interactive snackbar content with modal buttons
  Widget _buildInteractiveSnackbarContent() {
    return Container(
      margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      padding: const EdgeInsets.all(16),
      decoration: BoxDecoration(
        gradient: LinearGradient(
          colors: [Colors.indigo.shade600, Colors.indigo.shade800],
          begin: Alignment.topLeft,
          end: Alignment.bottomRight,
        ),
        borderRadius: BorderRadius.circular(16),
        boxShadow: [
          BoxShadow(
            color: Colors.indigo.withValues(alpha: 0.4),
            blurRadius: 12,
            offset: const Offset(0, 4),
          ),
        ],
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          Row(
            children: [
              Container(
                padding: const EdgeInsets.all(8),
                decoration: BoxDecoration(
                  color: Colors.white.withValues(alpha: 0.2),
                  shape: BoxShape.circle,
                ),
                child:
                    const Icon(Icons.touch_app, color: Colors.white, size: 24),
              ),
              const SizedBox(width: 12),
              const Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'Interactive Snackbar',
                      style: TextStyle(
                        color: Colors.white,
                        fontSize: 16,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    SizedBox(height: 2),
                    Text(
                      'Tap a button to open a modal',
                      style: TextStyle(color: Colors.white70, fontSize: 13),
                    ),
                  ],
                ),
              ),
              GestureDetector(
                onTap: () => Modal.dismissById('snackbar_with_buttons'),
                child: const Icon(Icons.close, color: Colors.white70, size: 20),
              ),
            ],
          ),
          const SizedBox(height: 16),
          Row(
            children: [
              Expanded(
                child: ElevatedButton.icon(
                  icon: const Icon(Icons.vertical_align_bottom, size: 18),
                  label: const Text('Bottom Sheet'),
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Colors.white,
                    foregroundColor: Colors.indigo.shade700,
                    padding: const EdgeInsets.symmetric(vertical: 12),
                  ),
                  onPressed: () {
                    // Show bottom sheet - snackbar stays visible!
                    Modal.show(
                      id: 'bottom-sheet-from-snackbar',
                      builder: () => _buildBottomSheetFromSnackbarContent(),
                      modalType: ModalType.sheet,
                      size: 300,
                      shouldBlurBackground: _globalBlur,
                      isDismissable: _globalDismissable,
                      blockBackgroundInteraction:
                          _globalBlockBackgroundInteraction,
                    );
                  },
                ),
              ),
              const SizedBox(width: 12),
              Expanded(
                child: ElevatedButton.icon(
                  icon: const Icon(Icons.chat_bubble, size: 18),
                  label: const Text('Dialog'),
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Colors.white,
                    foregroundColor: Colors.indigo.shade700,
                    padding: const EdgeInsets.symmetric(vertical: 12),
                  ),
                  onPressed: () {
                    // Show dialog - snackbar stays visible!
                    Modal.show(
                      id: 'dialog-from-snackbar',
                      builder: () => _buildDialogFromSnackbarContent(),
                      modalType: ModalType.dialog,
                      modalPosition: Alignment.center,
                      shouldBlurBackground: _globalBlur,
                      isDismissable: _globalDismissable,
                      blockBackgroundInteraction:
                          _globalBlockBackgroundInteraction,
                    );
                  },
                ),
              ),
            ],
          ),
        ],
      ),
    );
  }

  /// Builds the side sheet content for 'SideSheet + Snackbar' demo
  Widget _buildSideSheetWithSnackbarContent() {
    return Padding(
      padding: const EdgeInsets.all(20),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const SizedBox(height: 40),
          Row(
            children: [
              Container(
                padding: const EdgeInsets.all(12),
                decoration: BoxDecoration(
                  color: Colors.teal.shade100,
                  borderRadius: BorderRadius.circular(12),
                ),
                child: Icon(Icons.view_sidebar,
                    size: 32, color: Colors.teal.shade700),
              ),
              const SizedBox(width: 12),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(
                      'Side Sheet Active',
                      style: Theme.of(context).textTheme.titleLarge?.copyWith(
                            fontWeight: FontWeight.bold,
                          ),
                    ),
                    Text(
                      'Slides in from the side!',
                      style: TextStyle(color: Colors.grey.shade600),
                    ),
                  ],
                ),
              ),
            ],
          ),
          const SizedBox(height: 24),
          const Text(
            'Click the button below to show a snackbar. The side sheet remains visible!',
            style: TextStyle(fontSize: 14),
          ),
          const SizedBox(height: 24),
          SizedBox(
            width: double.infinity,
            child: ElevatedButton.icon(
              icon: const Icon(Icons.notifications),
              label: const Text('Show Snackbar'),
              style: ElevatedButton.styleFrom(
                backgroundColor: Colors.teal,
                foregroundColor: Colors.white,
                padding: const EdgeInsets.symmetric(vertical: 14),
              ),
              onPressed: () {
                Modal.showSnackbar(
                  text: 'I am a snackbar on top of the side sheet!',
                  position: Alignment.topCenter,
                  backgroundColor: Colors.teal.shade800,
                  duration: const Duration(seconds: 3),
                );
              },
            ),
          ),
          const SizedBox(height: 12),
          SizedBox(
            width: double.infinity,
            child: TextButton(
              onPressed: () => Modal.dismissSideSheet(),
              child: const Text('Close Side Sheet'),
            ),
          ),
        ],
      ),
    );
  }

  // ============================================
  // Reactive State Examples (using states_rebuilder)
  // ============================================

  /// Builds reactive counter content using states_rebuilder
  /// The counter state is managed by ReactiveModel and rebuilds automatically
  Widget _buildReactiveCounterContent() {
    // Create a local reactive state for the counter
    final counter = 0.inj();

    return Padding(
      padding: const EdgeInsets.all(20),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          const Icon(Icons.add_circle, size: 48, color: Colors.purple),
          const SizedBox(height: 16),
          Text(
            'Reactive Counter',
            style: Theme.of(context).textTheme.headlineSmall,
          ),
          const SizedBox(height: 12),
          const Text(
            'This counter updates in real-time using states_rebuilder!',
            textAlign: TextAlign.center,
          ),
          const SizedBox(height: 24),
          // OnReactive automatically rebuilds when counter changes
          OnReactive(
            () => Text(
              '${counter.state}',
              style: const TextStyle(fontSize: 48, fontWeight: FontWeight.bold),
            ),
          ),
          const SizedBox(height: 16),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              IconButton(
                icon: const Icon(Icons.remove_circle, size: 40),
                onPressed: () => counter.state--,
              ),
              const SizedBox(width: 24),
              IconButton(
                icon: const Icon(Icons.add_circle, size: 40),
                onPressed: () => counter.state++,
              ),
            ],
          ),
          const SizedBox(height: 24),
          ElevatedButton(
            onPressed: () => Modal.dismissBottomSheet(),
            child: const Text('Close'),
          ),
        ],
      ),
    );
  }

  /// Demonstrates reactive state with Modal.show() - the key is wrapping
  /// the content with reactive state management
  void _showReactiveCounterWithModalShow() {
    // The counter state lives OUTSIDE the builder, but the builder
    // uses OnReactive to subscribe and rebuild when it changes
    final counter = 0.inj();

    Modal.show(
      builder: () => Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            const Icon(Icons.refresh, size: 48, color: Colors.teal),
            const SizedBox(height: 16),
            Builder(
              builder: (context) => Text(
                'Modal.show() Reactive',
                style: Theme.of(context).textTheme.headlineSmall,
              ),
            ),
            const SizedBox(height: 12),
            const Text(
              'Even with Modal.show(), you can have reactive state!\n'
              'Just wrap reactive widgets with OnReactive.',
              textAlign: TextAlign.center,
            ),
            const SizedBox(height: 24),
            // OnReactive subscribes to counter and rebuilds
            OnReactive(
              () => Container(
                padding: const EdgeInsets.all(16),
                decoration: BoxDecoration(
                  color: Colors.teal.shade50,
                  borderRadius: BorderRadius.circular(12),
                  border: Border.all(color: Colors.teal.shade200),
                ),
                child: Text(
                  'Count: ${counter.state}',
                  style: const TextStyle(
                    fontSize: 32,
                    fontWeight: FontWeight.bold,
                    color: Colors.teal,
                  ),
                ),
              ),
            ),
            const SizedBox(height: 16),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton.icon(
                  icon: const Icon(Icons.remove),
                  label: const Text('Decrement'),
                  onPressed: () => counter.state--,
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Colors.red.shade100,
                  ),
                ),
                const SizedBox(width: 16),
                ElevatedButton.icon(
                  icon: const Icon(Icons.add),
                  label: const Text('Increment'),
                  onPressed: () => counter.state++,
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Colors.green.shade100,
                  ),
                ),
              ],
            ),
            const SizedBox(height: 24),
            ElevatedButton(
              onPressed: () => Modal.dismissBottomSheet(),
              child: const Text('Close'),
            ),
          ],
        ),
      ),
      size: 420,
      shouldBlurBackground: _globalBlur,
      isDismissable: _globalDismissable,
      blockBackgroundInteraction: _globalBlockBackgroundInteraction,
    );
  }

  /// Builds a reactive form dialog demonstrating form state management
  Widget _buildReactiveFormContent() {
    final name = ''.inj();
    final email = ''.inj();
    final isValid = false.inj();

    void validateForm() {
      isValid.state = name.state.isNotEmpty &&
          email.state.contains('@') &&
          email.state.contains('.');
    }

    return Dialog(
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(16),
        side: BorderSide(color: Colors.grey.shade400),
      ),
      shadowColor: Colors.black,
      child: Padding(
        padding: const EdgeInsets.all(24),
        child: SizedBox(
          height: 330,
          width: 400,
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              const Icon(Icons.edit, size: 48, color: Colors.indigo),
              const SizedBox(height: 16),
              Text(
                'Reactive Form',
                style: Theme.of(context).textTheme.headlineSmall,
              ),
              const SizedBox(height: 24),
              TextField(
                decoration: const InputDecoration(
                  labelText: 'Name',
                  border: OutlineInputBorder(),
                ),
                onChanged: (value) {
                  name.state = value;
                  validateForm();
                },
              ),
              const SizedBox(height: 16),
              TextField(
                decoration: const InputDecoration(
                  labelText: 'Email',
                  border: OutlineInputBorder(),
                ),
                onChanged: (value) {
                  email.state = value;
                  validateForm();
                },
              ),
              const SizedBox(height: 24),
              OnReactive(
                () => Column(
                  children: [
                    Text(
                      isValid.state
                          ? '✓ Form is valid'
                          : '✗ Please fill all fields',
                      style: TextStyle(
                        color: isValid.state ? Colors.green : Colors.red,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 16),
                    ElevatedButton(
                      onPressed: isValid.state
                          ? () {
                              Modal.dismissDialog();
                            }
                          : null,
                      child: const Text('Submit'),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }

  // ============================================
  // Live Update Examples (Modal.updateParams)
  // ============================================

  /// Demonstrates dynamic parameter updates on a bottom sheet
  void _showDynamicBottomSheet() {
    double currentBlur = 0.0;
    double currentHeight = 250.0;
    bool isDismissable = true;
    Color currentBgColor = Colors.white;

    final colors = [
      Colors.white,
      Colors.blue.shade50,
      Colors.green.shade50,
      Colors.orange.shade50,
      Colors.purple.shade50,
    ];
    int colorIndex = 0;

    Modal.show(
      id: 'dynamic_sheet',
      builder: () => StatefulBuilder(
        builder: (context, setModalState) {
          return Padding(
            padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 16),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  'Dynamic Updates Demo',
                  style: Theme.of(context).textTheme.titleLarge?.copyWith(
                        fontWeight: FontWeight.bold,
                      ),
                ),
                const SizedBox(height: 8),
                Text(
                  'All changes happen live without modal recreation!',
                  style: TextStyle(color: Colors.grey.shade600),
                ),
                const SizedBox(height: 20),

                // Blur slider
                Row(
                  children: [
                    const Icon(Icons.blur_on, size: 20),
                    const SizedBox(width: 8),
                    const Text('Blur: '),
                    Expanded(
                      child: Slider(
                        value: currentBlur,
                        min: 0,
                        max: 15,
                        onChanged: (value) {
                          setModalState(() => currentBlur = value);
                          Modal.updateParams(
                            id: 'dynamic_sheet',
                            blurAmount: value,
                            shouldBlurBackground: value > 0,
                          );
                        },
                      ),
                    ),
                    Text(currentBlur.toStringAsFixed(1)),
                  ],
                ),

                // Height slider
                Row(
                  children: [
                    const Icon(Icons.height, size: 20),
                    const SizedBox(width: 8),
                    const Text('Height: '),
                    Expanded(
                      child: Slider(
                        value: currentHeight,
                        min: 200,
                        max: 500,
                        onChanged: (value) {
                          setModalState(() => currentHeight = value);
                          Modal.updateParams(id: 'dynamic_sheet', size: value);
                        },
                      ),
                    ),
                    Text('${currentHeight.toInt()}'),
                  ],
                ),

                // Dismissable toggle
                SwitchListTile(
                  title: const Text('Dismissable'),
                  subtitle: Text(isDismissable
                      ? 'Tap outside to close'
                      : 'Must use button to close'),
                  value: isDismissable,
                  onChanged: (value) {
                    setModalState(() => isDismissable = value);
                    Modal.updateParams(
                        id: 'dynamic_sheet', isDismissable: value);
                  },
                  contentPadding: EdgeInsets.zero,
                ),

                // Background color button
                Row(
                  children: [
                    const Text('Background: '),
                    const SizedBox(width: 8),
                    ...colors.asMap().entries.map((entry) {
                      return GestureDetector(
                        onTap: () {
                          setModalState(() {
                            colorIndex = entry.key;
                            currentBgColor = entry.value;
                          });
                          Modal.updateParams(
                              id: 'dynamic_sheet',
                              backgroundColor: entry.value);
                        },
                        child: Container(
                          width: 32,
                          height: 32,
                          margin: const EdgeInsets.only(right: 8),
                          decoration: BoxDecoration(
                            color: entry.value,
                            border: Border.all(
                              color: colorIndex == entry.key
                                  ? Colors.black
                                  : Colors.grey.shade300,
                              width: colorIndex == entry.key ? 2 : 1,
                            ),
                            borderRadius: BorderRadius.circular(8),
                          ),
                        ),
                      );
                    }),
                  ],
                ),

                const SizedBox(height: 20),
                SizedBox(
                  width: double.infinity,
                  child: ElevatedButton(
                    onPressed: () => Modal.dismissBottomSheet(),
                    child: const Text('Close'),
                  ),
                ),
              ],
            ),
          );
        },
      ),
      modalType: ModalType.sheet,
      size: currentHeight,
      backgroundColor: currentBgColor,
      shouldBlurBackground: _globalBlur,
      isDismissable: _globalDismissable,
      blockBackgroundInteraction: _globalBlockBackgroundInteraction,
    );
  }

  /// Demonstrates changing modal type dynamically (morphing)
  void _showMorphingModal() {
    ModalType currentType = ModalType.sheet;
    int morphCount = 0;

    void showMorphContent() {
      Modal.show(
        id: 'morphing_modal', // Keep same ID to maintain identity
        builder: () => StatefulBuilder(
          builder: (context, setModalState) {
            return Container(
              padding: const EdgeInsets.all(24),
              decoration: currentType == ModalType.dialog
                  ? BoxDecoration(
                      color: Colors.white,
                      borderRadius: BorderRadius.circular(16),
                      boxShadow: [
                        BoxShadow(
                          color: Colors.black.withValues(alpha: 0.2),
                          blurRadius: 10,
                        ),
                      ],
                    )
                  : null,
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(
                    currentType == ModalType.sheet
                        ? Icons.vertical_align_bottom
                        : Icons.center_focus_strong,
                    size: 48,
                    color: currentType == ModalType.sheet
                        ? Colors.blue
                        : Colors.purple,
                  ),
                  const SizedBox(height: 16),
                  Text(
                    currentType == ModalType.sheet
                        ? 'Bottom Sheet Mode'
                        : 'Dialog Mode',
                    style: Theme.of(context).textTheme.headlineSmall,
                  ),
                  const SizedBox(height: 8),
                  Text(
                    'Morphed $morphCount times',
                    style: TextStyle(color: Colors.grey.shade600),
                  ),
                  const SizedBox(height: 24),
                  ElevatedButton.icon(
                    icon: const Icon(Icons.transform),
                    label: Text(currentType == ModalType.sheet
                        ? 'Morph to Dialog'
                        : 'Morph to Bottom Sheet'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: currentType == ModalType.sheet
                          ? Colors.purple
                          : Colors.blue,
                      foregroundColor: Colors.white,
                    ),
                    onPressed: () {
                      morphCount++;
                      final newType = currentType == ModalType.sheet
                          ? ModalType.dialog
                          : ModalType.sheet;
                      currentType = newType;

                      // Update the modal type - this triggers a full rebuild
                      Modal.updateParams(
                        id: 'morphing_modal',
                        modalType: newType,
                        modalPosition: newType == ModalType.dialog
                            ? Alignment.center
                            : Alignment.bottomCenter,
                        isDraggable: newType == ModalType.dialog,
                      );
                    },
                  ),
                  const SizedBox(height: 12),
                  TextButton(
                    onPressed: () => Modal.dismissById('morphing_modal'),
                    child: const Text('Close'),
                  ),
                ],
              ),
            );
          },
        ),
        modalType: currentType,
        modalPosition: Alignment.bottomCenter,
        size: 300,

        blurAmount: 5,
        shouldBlurBackground: _globalBlur,
        isDismissable: _globalDismissable,

        blockBackgroundInteraction: _globalBlockBackgroundInteraction,
      );
    }

    showMorphContent();
  }

  /// Demonstrates updating dialog position dynamically
  void _showPositionUpdatingDialog() {
    final positions = [
      Alignment.topLeft,
      Alignment.topCenter,
      Alignment.topRight,
      Alignment.centerLeft,
      Alignment.center,
      Alignment.centerRight,
      Alignment.bottomLeft,
      Alignment.bottomCenter,
      Alignment.bottomRight,
    ];
    int positionIndex = 4; // Start at center
    bool useOffset = false; // Track if using offset mode

    Modal.show(
      id: 'position_dialog',
      builder: () => StatefulBuilder(
        builder: (context, setModalState) {
          return Container(
            constraints: const BoxConstraints(
              maxWidth: 320,
              maxHeight: 380,
            ),
            padding: const EdgeInsets.all(24),
            decoration: BoxDecoration(
              color: Colors.white,
              borderRadius: BorderRadius.circular(16),
              boxShadow: [
                BoxShadow(
                  color: Colors.black.withValues(alpha: 0.2),
                  blurRadius: 10,
                ),
              ],
            ),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                const Icon(Icons.open_with, size: 48, color: Colors.indigo),
                const SizedBox(height: 16),
                Text(
                  useOffset
                      ? 'Mode: Offset (150, 100)'
                      : 'Position: ${positions[positionIndex].toString().split('.').last}',
                  style: Theme.of(context).textTheme.titleLarge,
                  textAlign: TextAlign.center,
                ),
                const SizedBox(height: 8),
                Text(
                  useOffset
                      ? 'Using absolute positioning'
                      : 'Tap arrows to move!',
                  style: const TextStyle(color: Colors.grey, fontSize: 12),
                  textAlign: TextAlign.center,
                ),
                const SizedBox(height: 20),
                if (!useOffset)
                  Row(
                    mainAxisAlignment: MainAxisAlignment.center,
                    children: [
                      IconButton(
                        icon: const Icon(Icons.arrow_back),
                        onPressed: positionIndex > 0
                            ? () {
                                setModalState(() => positionIndex--);
                                Modal.updateParams(
                                  id: 'position_dialog',
                                  modalPosition: positions[positionIndex],
                                );
                              }
                            : null,
                      ),
                      Text('${positionIndex + 1}/${positions.length}'),
                      IconButton(
                        icon: const Icon(Icons.arrow_forward),
                        onPressed: positionIndex < positions.length - 1
                            ? () {
                                setModalState(() => positionIndex++);
                                Modal.updateParams(
                                  id: 'position_dialog',
                                  modalPosition: positions[positionIndex],
                                );
                              }
                            : null,
                      ),
                    ],
                  ),
                const SizedBox(height: 16),
                ElevatedButton.icon(
                  onPressed: () {
                    setModalState(() => useOffset = !useOffset);
                    if (useOffset) {
                      // Switch to offset mode at a specific position
                      Modal.updateParams(
                        id: 'position_dialog',
                        offset: const Offset(150, 100),
                      );
                    } else {
                      // Switch back to alignment mode
                      // Pass null offset to clear it
                      Modal.updateParams(
                        id: 'position_dialog',
                        modalPosition: positions[positionIndex],
                        offset: null,
                      );
                    }
                  },
                  icon: Icon(useOffset ? Icons.grid_view : Icons.gps_fixed),
                  label: Text(useOffset ? 'Use Alignment' : 'Use Offset'),
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Colors.indigo,
                    foregroundColor: Colors.white,
                  ),
                ),
                const SizedBox(height: 8),
                TextButton(
                  onPressed: () => Modal.dismissDialog(),
                  child: const Text('Close'),
                ),
              ],
            ),
          );
        },
      ),
      modalType: ModalType.dialog,
      modalPosition: positions[positionIndex],
      isDraggable: true,
      blurAmount: 3,
      shouldBlurBackground: _globalBlur,
      isDismissable: _globalDismissable,
      blockBackgroundInteraction: _globalBlockBackgroundInteraction,
    );
  }

  /// Demonstrates live-updating snackbar parameters
  void _showLiveUpdatingSnackbar() {
    int countdown = 5;
    final colors = [
      Colors.grey.shade800,
      Colors.blue.shade700,
      Colors.green.shade700,
      Colors.orange.shade700,
      Colors.red.shade700,
    ];
    int colorIndex = 0;

    // Show initial snackbar
    Modal.showSnackbar(
      id: 'live_snackbar',
      builder: () => Container(
        margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
        padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
        decoration: BoxDecoration(
          color: colors[colorIndex],
          borderRadius: BorderRadius.circular(12),
        ),
        child: Row(
          children: [
            const Icon(Icons.timer, color: Colors.white),
            const SizedBox(width: 12),
            Expanded(
              child: Text(
                'Countdown: $countdown',
                style: const TextStyle(color: Colors.white, fontSize: 16),
              ),
            ),
            GestureDetector(
              onTap: () => Modal.dismissById('live_snackbar'),
              child: const Icon(Icons.close, color: Colors.white70),
            ),
          ],
        ),
      ),
      position: Alignment.topCenter,
      displayMode: SnackbarDisplayMode.replace,
      isDismissible: true,
      duration: null,
    );

    // Update countdown every second
    Future.doWhile(() async {
      await Future.delayed(const Duration(seconds: 1));
      countdown--;
      colorIndex = (colorIndex + 1) % colors.length;

      if (countdown <= 0 || !Modal.isSnackbarActive) {
        Modal.dismissById('live_snackbar');
        return false;
      }

      // Update the snackbar content
      Modal.updateParams(
        id: 'live_snackbar',
        builder: () => Container(
          margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
          padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 14),
          decoration: BoxDecoration(
            color: colors[colorIndex],
            borderRadius: BorderRadius.circular(12),
          ),
          child: Row(
            children: [
              Icon(
                countdown <= 3 ? Icons.warning : Icons.timer,
                color: Colors.white,
              ),
              const SizedBox(width: 12),
              Expanded(
                child: Text(
                  countdown <= 3
                      ? 'Closing in $countdown...'
                      : 'Countdown: $countdown',
                  style: const TextStyle(color: Colors.white, fontSize: 16),
                ),
              ),
              GestureDetector(
                onTap: () => Modal.dismissById('live_snackbar'),
                child: const Icon(Icons.close, color: Colors.white70),
              ),
            ],
          ),
        ),
      );

      return true;
    });
  }

  // ============================================
  // Mixed Modal Demos
  // ============================================

  void _showMixedModalDemo(String scenario) {
    // Reset any active modals first
    Modal.dismissAll();

    switch (scenario) {
      case 'BottomSheet + Snackbar':
        // 1. Show Bottom Sheet
        Modal.show(
          id: 'bottom-sheet-with-snackbar', // ID enables auto-update
          builder: () => _buildBottomSheetWithSnackbarContent(),
          modalType: ModalType.sheet,
          shouldBlurBackground: _globalBlur,
          isDismissable: _globalDismissable,
          blockBackgroundInteraction: _globalBlockBackgroundInteraction,
        );
        break;

      case 'Dialog + Snackbar':
        // 1. Show Dialog
        Modal.show(
          id: 'dialog-with-snackbar', // ID enables auto-update
          builder: () => _buildDialogWithSnackbarContent(),
          modalType: ModalType.dialog,
          modalPosition: Alignment.center,
          shouldBlurBackground: _globalBlur,
          isDismissable: _globalDismissable,
          blockBackgroundInteraction: _globalBlockBackgroundInteraction,
        );
        break;

      case 'Snackbar + Bottom Sheet':
        // 1. Show Snackbar first
        Modal.showSnackbar(
          text: 'I am a persistent snackbar. Open the sheet!',
          position: Alignment.topCenter,
          backgroundColor: Colors.orange.shade800,
          duration: const Duration(seconds: 5),
        );

        // 2. Wait a bit then show Bottom Sheet (simulating user action or sequence)
        Future.delayed(const Duration(milliseconds: 700), () {
          Modal.show(
            id: 'bottom-sheet-after-snackbar', // ID enables auto-update
            builder: () => _buildBottomSheetAfterSnackbarContent(),
            modalType: ModalType.sheet,
            size: 300,
            shouldBlurBackground: _globalBlur,
            isDismissable: _globalDismissable,
            blockBackgroundInteraction: _globalBlockBackgroundInteraction,
          );
        });
        break;

      case 'Snackbar with Modal Buttons':
        // Show a snackbar with buttons to open other modals
        Modal.showSnackbar(
          duration: const Duration(seconds: 8),
          id: 'snackbar_with_buttons',
          builder: () => _buildInteractiveSnackbarContent(),
          position: Alignment.bottomCenter,
          isDismissible: true,
        );
        break;

      case 'SideSheet + Snackbar':
        // 1. Show Side Sheet
        Modal.show(
          modalType: ModalType.sheet,
          builder: () => _buildSideSheetWithSnackbarContent(),
          sheetPosition: SheetPosition.right,
          size: 300,
          shouldBlurBackground: _globalBlur,
          isDismissable: _globalDismissable,
          id: 'side-sheet-with-snackbar',
        );
        break;
    }
  }

  Widget _buildPositionSelector() {
    return Column(
      children: [
        Row(
          children: [
            _buildPositionChip(Alignment.topLeft, 'TL'),
            const SizedBox(width: 8),
            _buildPositionChip(Alignment.topCenter, 'TC'),
            const SizedBox(width: 8),
            _buildPositionChip(Alignment.topRight, 'TR'),
          ],
        ),
        const SizedBox(height: 8),
        Row(
          children: [
            _buildPositionChip(Alignment.bottomLeft, 'BL'),
            const SizedBox(width: 8),
            _buildPositionChip(Alignment.bottomCenter, 'BC'),
            const SizedBox(width: 8),
            _buildPositionChip(Alignment.bottomRight, 'BR'),
          ],
        ),
      ],
    );
  }

  Widget _buildPositionChip(Alignment position, String label) {
    final isSelected = _selectedPosition == position;
    return Expanded(
      child: ChoiceChip(
        label: Text(label),
        selected: isSelected,
        onSelected: (selected) {
          if (selected) {
            setState(() => _selectedPosition = position);
            // Live update if snackbar is active
            if (Modal.isSnackbarActive) {
              final activeId = Modal.snackbarController.state?.uniqueId;
              if (activeId != null) {
                Modal.updateParams(id: activeId, modalPosition: position);
              }
            }
          }
        },
      ),
    );
  }

  /// Builds a draggable position picker for offset-based snackbar positioning
  ///
  /// The widget displays a rectangular area representing the screen,
  /// with a draggable pin that the user can move to set the snackbar's position.
  Widget _buildOffsetPositionPicker() {
    // Get actual screen dimensions
    final screenSize = MediaQuery.of(context).size;
    final screenHeight = screenSize.height;

    // The picker dimensions - represents a scaled-down version of the screen
    // Maintain aspect ratio with the actual screen
    const double pickerWidth = 160.0; // Reduced from 280.0
    final double pickerHeight = pickerWidth * (screenHeight / screenSize.width);

    // The actual screen dimensions we're mapping to (full usable screen)
    final double maxScreenX = screenSize.width;
    final double maxScreenY = screenHeight;

    return Center(
      child: ValueListenableBuilder<SnackbarOffset>(
        valueListenable: _snackbarOffsetNotifier,
        builder: (context, offset, _) {
          // Calculate pin position within the picker (scaled from actual offset values)
          final double pinX =
              (offset.offsetX / maxScreenX * pickerWidth).clamp(0, pickerWidth);
          final double pinY = (offset.offsetY / maxScreenY * pickerHeight)
              .clamp(0, pickerHeight);

          return _OffsetPickerWidget(
            width: pickerWidth,
            height: pickerHeight,
            maxScreenX: maxScreenX,
            maxScreenY: maxScreenY,
            pinX: pinX,
            pinY: pinY,
            onOffsetChanged: (offsetX, offsetY) {
              _snackbarOffsetNotifier.value =
                  SnackbarOffset(offsetX: offsetX, offsetY: offsetY);
            },
          );
        },
      ),
    );
  }

  void _showConfiguredSnackbar() {
    final count = _snackbarCounter++;

    // Callback to decrement counter when staggered snackbar is dismissed
    void onDismissedCallback() {
      if (_selectedDisplayMode == SnackbarDisplayMode.staggered ||
          _selectedDisplayMode == SnackbarDisplayMode.notificationBubble) {
        setState(() {
          _snackbarCounter--;
        });
      }
    }

    // Generate ID only for custom types that need it
    final needsId =
        ['Success', 'Error', 'Custom'].contains(_selectedSnackbarType);
    final snackbarId =
        needsId ? 'snackbar_${DateTime.now().millisecondsSinceEpoch}' : null;

    // Calculate offset for 'With Offset' type using ValueNotifier state
    Offset? offset;
    if (_selectedSnackbarType == 'With Offset') {
      final offsetState = _snackbarOffsetNotifier.value;
      offset = Offset(offsetState.offsetX, offsetState.offsetY);
    }

    // Calculate effective duration (null for staggered/notification modes)
    final effectiveDuration =
        _selectedDisplayMode == SnackbarDisplayMode.staggered ||
                _selectedDisplayMode == SnackbarDisplayMode.notificationBubble
            ? null
            : const Duration(seconds: 3);

    // Determine builder for custom content types
    final builder = switch (_selectedSnackbarType) {
      'Success' => () => _buildSuccessSnackbarContent(snackbarId!),
      'Error' => () => _buildErrorSnackbarContent(snackbarId!),
      'Custom' => () => _buildActionSnackbarContent(snackbarId!),
      _ => null,
    };

    // Get offset display text from ValueNotifier state
    final offsetStateText = _selectedSnackbarType == 'With Offset'
        ? () {
            final offsetState = _snackbarOffsetNotifier.value;
            return 'Snackbar at (${offsetState.offsetX.toInt()}, ${offsetState.offsetY.toInt()})';
          }()
        : 'Snackbar #$count';

    Modal.showSnackbar(
      id: snackbarId,
      duration: effectiveDuration,
      builder: builder,
      text: builder == null ? offsetStateText : null,
      prefixIcon: builder == null
          ? (_selectedSnackbarType == 'With Offset'
              ? Icons.control_camera
              : Icons.info_outline)
          : null,
      backgroundColor:
          _selectedSnackbarType == 'With Offset' ? Colors.deepPurple : null,
      offset: offset,
      position: _selectedPosition,
      displayMode: _selectedDisplayMode,
      onDismissed: onDismissedCallback,
      showDurationTimer: _showDurationTimer,
      durationTimerColor: _durationTimerColor,
      durationTimerDirection: _durationTimerDirection,
      width: _snackbarWidth,
      isDismissible: _snackbarIsDismissible,
      barrierColor: _snackbarBarrierColor,
    );
  }

  Widget _buildColorChip(Color? color, String label) {
    final isSelected = _durationTimerColor == color;
    return GestureDetector(
      onTap: () => setState(() => _durationTimerColor = color),
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
        decoration: BoxDecoration(
          color: color ?? Colors.amber,
          borderRadius: BorderRadius.circular(12),
          border: Border.all(
            color: isSelected
                ? Theme.of(context).colorScheme.primary
                : Colors.transparent,
            width: 2,
          ),
        ),
        child: Text(
          label,
          style: TextStyle(
            fontSize: 12,
            color: color == null
                ? Colors.black87
                : (color.computeLuminance() > 0.5
                    ? Colors.black
                    : Colors.white),
            fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
          ),
        ),
      ),
    );
  }

  Widget _buildBarrierColorChip(Color color, String label, bool isTransparent) {
    final isSelected = _snackbarBarrierColor == color;
    return GestureDetector(
      onTap: () => setState(() => _snackbarBarrierColor = color),
      child: Container(
        padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
        decoration: BoxDecoration(
          color: isTransparent
              ? Colors.grey.shade200
              : color.withValues(
                  alpha: 1.0), // Show fully opaque for visibility
          borderRadius: BorderRadius.circular(12),
          border: Border.all(
            color: isSelected
                ? Theme.of(context).colorScheme.primary
                : Colors.grey.shade400,
            width: 2,
          ),
        ),
        child: Text(
          label,
          style: TextStyle(
            fontSize: 12,
            color: isTransparent
                ? Colors.black87
                : (color.computeLuminance() > 0.5
                    ? Colors.black
                    : Colors.white),
            fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
          ),
        ),
      ),
    );
  }

  // Helper to get icon for sheet position
  IconData _getSheetIcon(SheetPosition position) {
    switch (position) {
      case SheetPosition.top:
        return Icons.vertical_align_top;
      case SheetPosition.bottom:
        return Icons.vertical_align_bottom;
      case SheetPosition.left:
        return Icons.align_horizontal_left;
      case SheetPosition.right:
        return Icons.align_horizontal_right;
    }
  }

  // Helper to get display name for sheet position
  String _getSheetTypeName(SheetPosition position) {
    switch (position) {
      case SheetPosition.top:
        return 'Top Sheet';
      case SheetPosition.bottom:
        return 'Bottom Sheet';
      case SheetPosition.left:
        return 'Left Side Sheet';
      case SheetPosition.right:
        return 'Right Side Sheet';
    }
  }

  void _showConfiguredSheet() {
    ModalWidgetBuilder builder;
    double? size = _sheetSize;
    double? expandedHeightPercent;

    // Determine the modal type based on position
    ModalType modalType;
    switch (_sheetPosition) {
      case SheetPosition.top:
        modalType = ModalType.sheet;
        break;
      case SheetPosition.bottom:
        modalType = ModalType.sheet;
        break;
      case SheetPosition.left:
      case SheetPosition.right:
        modalType = ModalType.sheet;
        break;
    }

    // Build content based on selected content type
    switch (_selectedSheetContentType) {
      case 'Custom':
        builder = () => _buildSideSheetCustomContent();
        break;
      case 'Blurred':
        builder = () => _buildCustomBlurContent();
        size = 350;
        break;
      case 'List':
        builder = () => _buildExpandableSheetContent();
        size = 200;
        expandedHeightPercent = 85;
        break;
      case 'Menu':
        builder = () => _buildSideSheetMenuContent();
        break;
      case 'Settings':
        builder = () => _buildSideSheetSettingsContent();
        break;
      case 'Profile':
        builder = () => _buildSideSheetProfileContent();
        break;
      case 'Default':
      default:
        builder = () => Modal.bottomSheetTemplate;
        break;
    }

    Modal.show(
      id: 'configured-sheet',
      builder: builder,
      modalType: modalType,
      sheetPosition: _sheetPosition,
      isExpandable: _sheetExpandable,
      size: size,
      expandedPercentageSize:
          _sheetExpandable ? (expandedHeightPercent ?? 85.0) : 85.0,
      blurAmount: _globalBlurAmount,
      barrierColor: _globalBarrierColor,
      shouldBlurBackground: _globalBlur,
      isDismissable: _globalDismissable,
      blockBackgroundInteraction: _globalBlockBackgroundInteraction,
      backgroundColor: _sheetBackgroundColor,
    );
  }

  void _showConfiguredDialog() {
    ModalWidgetBuilder builder;
    double? size;

    switch (_selectedDialogType) {
      case 'Confirm':
        builder = () => _buildConfirmationDialogContent();
        size = 300;
        break;
      case 'Form':
        builder = () => _buildReactiveFormContent();
        size = 400;
        break;
      case 'Callbacks':
        builder = () => _buildCallbacksContent();
        size = 250;
        break;
      case 'Info':
      default:
        builder = () => _buildDialogContent();
        size = 300;
        break;
    }

    final onDismissCallback = _selectedDialogType == 'Callbacks'
        ? () async {
            debugPrint("DIALOG DISMISSED");
            Modal.showSnackbar(
              text: 'Modal dismissed!',
              prefixIcon: Icons.check_circle,
              backgroundColor: Colors.teal,
              position: Alignment.topCenter,
            );
          }
        : null;

    // AUTO-UPDATE FEATURE: Just call Modal.show() with an ID.
    // If a dialog with this ID is already active, it will be updated.
    // If not, a new one will be shown. No need for if/else checking!

    Modal.show(
      id: 'configured-dialog', // ID enables auto-update
      builder: builder,
      modalType: ModalType.dialog,
      modalPosition: Alignment.center,
      size: size,
      blurAmount: _globalBlurAmount,
      barrierColor: _globalBarrierColor,
      isDraggable: _dialogDraggable,
      offset: _dialogWithOffset ? const Offset(50, 30) : null,
      shouldBlurBackground: _globalBlur,
      isDismissable: _globalDismissable,
      blockBackgroundInteraction: _globalBlockBackgroundInteraction,
      onDismissed: onDismissCallback,
    );
  }

  Widget _buildSideSheetMenuContent() {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const SizedBox(height: 40),
          Text(
            'Menu',
            style: Theme.of(context).textTheme.headlineMedium?.copyWith(
                  fontWeight: FontWeight.bold,
                ),
          ),
          const SizedBox(height: 24),
          _buildMenuListItem(
              Icons.home, 'Home', () => Modal.dismissSideSheet()),
          _buildMenuListItem(
              Icons.person, 'Profile', () => Modal.dismissSideSheet()),
          _buildMenuListItem(
              Icons.settings, 'Settings', () => Modal.dismissSideSheet()),
          _buildMenuListItem(Icons.notifications, 'Notifications',
              () => Modal.dismissSideSheet()),
          _buildMenuListItem(
              Icons.help, 'Help & Support', () => Modal.dismissSideSheet()),
          const SizedBox(height: 48),
          const Divider(),
          _buildMenuListItem(
              Icons.logout, 'Logout', () => Modal.dismissSideSheet(),
              isDestructive: true),
          const SizedBox(height: 24),
        ],
      ),
    );
  }

  Widget _buildMenuListItem(IconData icon, String title, VoidCallback onTap,
      {bool isDestructive = false}) {
    return InkWell(
      onTap: onTap,
      borderRadius: BorderRadius.circular(8),
      child: Padding(
        padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 8),
        child: Row(
          children: [
            Icon(
              icon,
              color: isDestructive ? Colors.red : Colors.grey.shade700,
            ),
            const SizedBox(width: 16),
            Text(
              title,
              style: TextStyle(
                fontSize: 16,
                color: isDestructive ? Colors.red : Colors.grey.shade800,
                fontWeight: FontWeight.w500,
              ),
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildSideSheetSettingsContent() {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          const SizedBox(height: 40),
          Text(
            'Settings',
            style: Theme.of(context).textTheme.headlineMedium?.copyWith(
                  fontWeight: FontWeight.bold,
                ),
          ),
          const SizedBox(height: 24),
          SwitchListTile(
            title: const Text('Dark Mode'),
            subtitle: const Text('Enable dark theme'),
            value: false,
            onChanged: (_) {},
            contentPadding: EdgeInsets.zero,
          ),
          SwitchListTile(
            title: const Text('Notifications'),
            subtitle: const Text('Enable push notifications'),
            value: true,
            onChanged: (_) {},
            contentPadding: EdgeInsets.zero,
          ),
          SwitchListTile(
            title: const Text('Sound Effects'),
            subtitle: const Text('Play sounds for actions'),
            value: true,
            onChanged: (_) {},
            contentPadding: EdgeInsets.zero,
          ),
          const Divider(height: 32),
          ListTile(
            contentPadding: EdgeInsets.zero,
            leading: const Icon(Icons.language),
            title: const Text('Language'),
            trailing: const Text('English'),
            onTap: () {},
          ),
          ListTile(
            contentPadding: EdgeInsets.zero,
            leading: const Icon(Icons.privacy_tip),
            title: const Text('Privacy'),
            trailing: const Icon(Icons.chevron_right),
            onTap: () {},
          ),
        ],
      ),
    );
  }

  Widget _buildSideSheetProfileContent() {
    return Padding(
      padding: const EdgeInsets.all(16.0),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          const SizedBox(height: 40),
          CircleAvatar(
            radius: 50,
            backgroundColor: Theme.of(context).colorScheme.primary,
            child: const Icon(Icons.person, size: 50, color: Colors.white),
          ),
          const SizedBox(height: 16),
          Text(
            'John Doe',
            style: Theme.of(context).textTheme.headlineSmall?.copyWith(
                  fontWeight: FontWeight.bold,
                ),
          ),
          const SizedBox(height: 4),
          Text(
            'john.doe@example.com',
            style: TextStyle(color: Colors.grey.shade600),
          ),
          const SizedBox(height: 24),
          _buildProfileStat('Posts', '142'),
          _buildProfileStat('Followers', '1.2K'),
          _buildProfileStat('Following', '89'),
          const SizedBox(height: 24),
          SizedBox(
            width: double.infinity,
            child: ElevatedButton(
              onPressed: () => Modal.dismissSideSheet(),
              child: const Text('Edit Profile'),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildProfileStat(String label, String value) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 8),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Text(label, style: TextStyle(color: Colors.grey.shade600)),
          Text(value, style: const TextStyle(fontWeight: FontWeight.bold)),
        ],
      ),
    );
  }

  Widget _buildSideSheetCustomContent() {
    return Container(
      decoration: BoxDecoration(
        gradient: LinearGradient(
          begin: Alignment.topCenter,
          end: Alignment.bottomCenter,
          colors: [
            Theme.of(context).colorScheme.primary.withValues(alpha: 0.1),
            Colors.white,
          ],
        ),
      ),
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const SizedBox(height: 40),
            Container(
              padding: const EdgeInsets.all(12),
              decoration: BoxDecoration(
                color: Theme.of(context)
                    .colorScheme
                    .primary
                    .withValues(alpha: 0.1),
                borderRadius: BorderRadius.circular(12),
              ),
              child: Icon(
                Icons.auto_awesome,
                size: 32,
                color: Theme.of(context).colorScheme.primary,
              ),
            ),
            const SizedBox(height: 16),
            Text(
              'Custom Side Sheet',
              style: Theme.of(context).textTheme.headlineSmall?.copyWith(
                    fontWeight: FontWeight.bold,
                  ),
            ),
            const SizedBox(height: 8),
            Text(
              'This is a fully customizable side sheet. You can put any content here!',
              style: TextStyle(color: Colors.grey.shade600),
            ),
            const SizedBox(height: 24),
            Container(
              padding: const EdgeInsets.all(16),
              decoration: BoxDecoration(
                color: Colors.amber.shade50,
                borderRadius: BorderRadius.circular(12),
                border: Border.all(color: Colors.amber.shade200),
              ),
              child: Row(
                children: [
                  Icon(Icons.lightbulb, color: Colors.amber.shade700),
                  const SizedBox(width: 12),
                  Expanded(
                    child: Text(
                      'Tip: Swipe horizontally to dismiss!',
                      style: TextStyle(color: Colors.amber.shade900),
                    ),
                  ),
                ],
              ),
            ),
            const SizedBox(height: 48),
            SizedBox(
              width: double.infinity,
              child: ElevatedButton(
                onPressed: () => Modal.dismissSideSheet(),
                child: const Text('Close'),
              ),
            ),
            const SizedBox(height: 24),
          ],
        ),
      ),
    );
  }

  Widget _buildColorChoice({
    required Color color,
    required String label,
    required Color selectedColor,
    required Function(Color) onSelect,
  }) {
    // Compare Color objects directly instead of using the deprecated `.value`.
    final isSelected = color == selectedColor;
    final isTransparent = color == Colors.transparent;

    return GestureDetector(
      onTap: () => onSelect(color),
      child: Column(
        children: [
          Container(
            width: 40,
            height: 40,
            decoration: BoxDecoration(
              color: isTransparent ? Colors.white : color,
              shape: BoxShape.circle,
              border: Border.all(
                color: isSelected
                    ? Theme.of(context).primaryColor
                    : Colors.grey.shade300,
                width: isSelected ? 3 : 1,
              ),
            ),
            child: isSelected
                ? Icon(Icons.check,
                    color: (color.computeLuminance() > 0.5 || isTransparent)
                        ? Colors.black
                        : Colors.white,
                    size: 20)
                : (isTransparent
                    ? Icon(Icons.grid_3x3,
                        color: Colors.grey.shade300, size: 20)
                    : null),
          ),
          const SizedBox(height: 4),
          Text(
            label,
            style: TextStyle(
              fontSize: 10,
              fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
              color: isSelected ? Theme.of(context).primaryColor : Colors.grey,
            ),
          ),
        ],
      ),
    );
  }
}

/// Model for snackbar offset state management with states_rebuilder
class SnackbarOffset {
  final double offsetX;
  final double offsetY;

  SnackbarOffset({required this.offsetX, required this.offsetY});

  SnackbarOffset copyWith({double? offsetX, double? offsetY}) {
    return SnackbarOffset(
      offsetX: offsetX ?? this.offsetX,
      offsetY: offsetY ?? this.offsetY,
    );
  }

  @override
  String toString() => 'SnackbarOffset(x: $offsetX, y: $offsetY)';
}

class ExampleButton extends StatelessWidget {
  final IconData icon;
  final String text;
  final VoidCallback? onPressed;

  const ExampleButton(
      {required this.icon, required this.text, this.onPressed, super.key});

  @override
  Widget build(BuildContext context) {
    final button = Container(
      padding: const EdgeInsets.all(5),
      decoration: BoxDecoration(
        color: Colors.blueGrey.shade50,
        borderRadius: BorderRadius.circular(8),
        border: Border.all(color: Colors.black.withValues(alpha: 0.3)),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withValues(alpha: 0.1),
            blurRadius: 4,
            offset: const Offset(0, 2),
          ),
        ],
      ),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(icon),
          const SizedBox(width: 8),
          Text(text),
        ],
      ),
    );
    if (onPressed != null) {
      return SInkButton(
        onTap: (pos) => onPressed?.call(),
        child: button,
      );
    }
    return button;
  }
}

/// Stateful widget for the offset position picker with smooth dragging
class _OffsetPickerWidget extends StatefulWidget {
  final double width;
  final double height;
  final double maxScreenX;
  final double maxScreenY;
  final double pinX;
  final double pinY;
  final Function(double offsetX, double offsetY) onOffsetChanged;

  const _OffsetPickerWidget({
    required this.width,
    required this.height,
    required this.maxScreenX,
    required this.maxScreenY,
    required this.pinX,
    required this.pinY,
    required this.onOffsetChanged,
  });

  @override
  State<_OffsetPickerWidget> createState() => _OffsetPickerWidgetState();
}

class _OffsetPickerWidgetState extends State<_OffsetPickerWidget> {
  late double _currentPinX;
  late double _currentPinY;

  @override
  void initState() {
    super.initState();
    _currentPinX = widget.pinX;
    _currentPinY = widget.pinY;
  }

  @override
  void didUpdateWidget(_OffsetPickerWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.pinX != widget.pinX || oldWidget.pinY != widget.pinY) {
      _currentPinX = widget.pinX;
      _currentPinY = widget.pinY;
    }
  }

  void _updatePinPosition(Offset localPosition) {
    // Clamp position to picker bounds
    final clampedX = localPosition.dx.clamp(0.0, widget.width);
    final clampedY = localPosition.dy.clamp(0.0, widget.height);

    // Update local state for immediate visual feedback
    setState(() {
      _currentPinX = clampedX;
      _currentPinY = clampedY;
    });

    // Convert picker position to screen offset
    final newOffsetX = (clampedX / widget.width * widget.maxScreenX);
    final newOffsetY = (clampedY / widget.height * widget.maxScreenY);

    // Notify parent of change
    widget.onOffsetChanged(newOffsetX, newOffsetY);
  }

  @override
  Widget build(BuildContext context) {
    const double pinSize = 24.0;

    return Listener(
      onPointerDown: (event) => _updatePinPosition(event.localPosition),
      onPointerMove: (event) => _updatePinPosition(event.localPosition),
      behavior: HitTestBehavior.opaque,
      child: Container(
        width: widget.width,
        height: widget.height,
        decoration: BoxDecoration(
          color: Colors.grey.shade200,
          borderRadius: BorderRadius.circular(12),
          border: Border.all(color: Colors.grey.shade400, width: 1.5),
          boxShadow: [
            BoxShadow(
              color: Colors.black.withValues(alpha: 0.1),
              blurRadius: 4,
              offset: const Offset(0, 2),
            ),
          ],
        ),
        child: Stack(
          clipBehavior: Clip.none,
          children: [
            // Grid lines for visual reference
            Positioned.fill(
              child: CustomPaint(
                painter: _GridPainter(),
              ),
            ),

            // "Screen" label
            Positioned(
              top: 4,
              left: 8,
              child: Text(
                'Screen',
                style: TextStyle(
                  fontSize: 10,
                  color: Colors.grey.shade500,
                  fontWeight: FontWeight.w500,
                ),
              ),
            ),

            // Draggable Pin
            Positioned(
              left: _currentPinX - pinSize / 2,
              top: _currentPinY - pinSize / 2,
              child: IgnorePointer(
                child: Container(
                  width: pinSize,
                  height: pinSize,
                  decoration: BoxDecoration(
                    color: Theme.of(context).colorScheme.primary,
                    shape: BoxShape.circle,
                    border: Border.all(color: Colors.white, width: 3),
                    boxShadow: [
                      BoxShadow(
                        color: Colors.black.withValues(alpha: 0.3),
                        blurRadius: 4,
                        offset: const Offset(0, 2),
                      ),
                    ],
                  ),
                  child: const Icon(
                    Icons.location_on,
                    size: 14,
                    color: Colors.white,
                  ),
                ),
              ),
            ),

            // Snackbar preview indicator
            Positioned(
              left: _currentPinX,
              top: _currentPinY,
              child: IgnorePointer(
                child: Container(
                  width: 50,
                  height: 16,
                  decoration: BoxDecoration(
                    color: Colors.deepPurple.withValues(alpha: 0.6),
                    borderRadius: BorderRadius.circular(3),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

/// Custom painter for drawing grid lines in the offset position picker
class _GridPainter extends CustomPainter {
  @override
  void paint(Canvas canvas, Size size) {
    final paint = Paint()
      ..color = Colors.grey.shade400.withValues(alpha: 0.5)
      ..strokeWidth = 0.5;

    // Draw vertical lines
    const int verticalDivisions = 4;
    for (int i = 1; i < verticalDivisions; i++) {
      final x = size.width * i / verticalDivisions;
      canvas.drawLine(Offset(x, 0), Offset(x, size.height), paint);
    }

    // Draw horizontal lines
    const int horizontalDivisions = 3;
    for (int i = 1; i < horizontalDivisions; i++) {
      final y = size.height * i / horizontalDivisions;
      canvas.drawLine(Offset(0, y), Offset(size.width, y), paint);
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
0
likes
160
points
158
downloads

Publisher

unverified uploader

Weekly Downloads

A package for displaying highly customizable modal overlays including sheets, dialogs, and snackbars... with independent lifecycle management.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

assorted_layout_widgets, dart_helper_utils, flutter, flutter_animate, s_bounceable, s_ink_button, sizer, soundsliced_dart_extensions, soundsliced_tween_animation_builder, states_rebuilder_extended

More

Packages that depend on s_modal