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

Full-featured Flutter color picker with hex, HSV, alpha, gradients, presets, recent colors, and design-tool style controls.

example/lib/main.dart

import 'package:color_picker_plus/color_picker_plus.dart';
import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'local_soft_saas.dart';

import 'widgets/color_picker_trigger_demo.dart';
import 'widgets/color_picker_row_demo.dart';
import 'widgets/dialog_mode_demo.dart';
import 'widgets/layers_list_demo.dart';
import 'widgets/layers_control_panel_demo.dart';
import 'widgets/gradient_demo.dart';
import 'widgets/component_demos.dart';

void main() => runApp(const ColorPickerDemoApp());

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

  @override
  State<ColorPickerDemoApp> createState() => _ColorPickerDemoAppState();
}

class _ColorPickerDemoAppState extends State<ColorPickerDemoApp> {
  bool _darkMode = false;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Color Picker',
      debugShowCheckedModeBanner: false,
      theme: SoftSaaSTheme.light(),
      darkTheme: SoftSaaSTheme.dark(),
      themeMode: _darkMode ? ThemeMode.dark : ThemeMode.light,
      home: ColorPickerHomePage(
        darkMode: _darkMode,
        onToggleTheme: () => setState(() => _darkMode = !_darkMode),
      ),
    );
  }
}

class ColorPickerHomePage extends StatefulWidget {
  const ColorPickerHomePage({
    super.key,
    required this.darkMode,
    required this.onToggleTheme,
  });

  final bool darkMode;
  final VoidCallback onToggleTheme;

  @override
  State<ColorPickerHomePage> createState() => _ColorPickerHomePageState();
}

class _ColorPickerHomePageState extends State<ColorPickerHomePage> {
  final ScrollController _paneScrollController = ScrollController();
  final PageController _mobilePageController = PageController();
  int _selectedPane = 0;

  @override
  void dispose() {
    _paneScrollController.dispose();
    _mobilePageController.dispose();
    super.dispose();
  }

  void _selectPane(int index) {
    if (_selectedPane != index) {
      setState(() => _selectedPane = index);
    }

    if (_mobilePageController.hasClients) {
      _mobilePageController.animateToPage(
        index,
        duration: const Duration(milliseconds: 320),
        curve: Curves.easeOutCubic,
      );
    }

    if (!_paneScrollController.hasClients) return;
    final maxExtent = _paneScrollController.position.maxScrollExtent;
    final target = _paneScrollOffsets[index].clamp(0.0, maxExtent);
    _paneScrollController.animateTo(
      target,
      duration: const Duration(milliseconds: 320),
      curve: Curves.easeOutCubic,
    );
  }

  void _handleMobilePageChanged(int index) {
    if (_selectedPane == index) return;
    setState(() => _selectedPane = index);
  }

  @override
  Widget build(BuildContext context) {
    final brightness = Theme.of(context).brightness;

    return Scaffold(
      backgroundColor: SoftSaaSTokens.secondaryBackground(brightness),
      body: SafeArea(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            _AppHeader(
              darkMode: widget.darkMode,
              onToggleTheme: widget.onToggleTheme,
            ),
            _PaneTabs(selectedIndex: _selectedPane, onChanged: _selectPane),
            Expanded(
              child: _PaneCanvas(
                controller: _paneScrollController,
                mobilePageController: _mobilePageController,
                onMobilePageChanged: _handleMobilePageChanged,
              ),
            ),
          ],
        ),
      ),
    );
  }
}

// ── App Header ────────────────────────────────────────────────────────────────

class _AppHeader extends StatelessWidget {
  const _AppHeader({required this.darkMode, required this.onToggleTheme});

  final bool darkMode;
  final VoidCallback onToggleTheme;

  @override
  Widget build(BuildContext context) {
    final brightness = Theme.of(context).brightness;
    final primary = SoftSaaSTokens.primaryColor(brightness);

    return Container(
      height: 46,
      decoration: BoxDecoration(
        color: SoftSaaSTokens.primaryBackground(brightness),
        border: Border(
          bottom: BorderSide(color: SoftSaaSTokens.primaryBorder(brightness)),
        ),
      ),
      padding: const EdgeInsets.symmetric(horizontal: 14),
      child: Row(
        children: [
          Container(
            width: 24,
            height: 24,
            decoration: BoxDecoration(
              color: primary.withValues(alpha: 0.12),
              borderRadius: BorderRadius.circular(6),
            ),
            alignment: Alignment.center,
            child: Icon(LucideIcons.palette, size: 14, color: primary),
          ),
          const SizedBox(width: 10),
          Text(
            'Color Picker',
            style: TextStyle(
              fontSize: 13,
              fontWeight: FontWeight.w600,
              letterSpacing: -0.2,
              height: 1.0,
              color: SoftSaaSTokens.primaryText(brightness),
            ),
          ),
          const Spacer(),
          SoftSaaSIconButton(
            icon: darkMode ? LucideIcons.sun : LucideIcons.moon,
            size: SoftSaaSButtonSize.small,
            variant: SoftSaaSIconButtonVariant.ghost,
            tooltip: darkMode ? 'Switch to light' : 'Switch to dark',
            iconColor: SoftSaaSTokens.primaryText(brightness),
            onPressed: onToggleTheme,
          ),
        ],
      ),
    );
  }
}

// ── Workspace panes ──────────────────────────────────────────────────────────

const double _mobilePaneBreakpoint = 600;
const double _normalPaneWidth = 420;
const double _widePaneWidth = _normalPaneWidth * 1.5;
const double _paneGap = 16;
const double _panelGap = 16;
const EdgeInsets _paneContentPadding = EdgeInsets.all(16);
const EdgeInsets _panelBodyPadding = EdgeInsets.all(12);

const List<double> _paneScrollOffsets = [
  0,
  _normalPaneWidth + _paneGap,
  (_normalPaneWidth + _paneGap) * 2,
  (_normalPaneWidth + _paneGap) * 2 + _widePaneWidth + _paneGap,
  (_normalPaneWidth + _paneGap) * 3 + _widePaneWidth + _paneGap,
];

class _PaneTabs extends StatelessWidget {
  const _PaneTabs({required this.selectedIndex, required this.onChanged});

  final int selectedIndex;
  final ValueChanged<int> onChanged;

  @override
  Widget build(BuildContext context) {
    final brightness = Theme.of(context).brightness;

    return DecoratedBox(
      decoration: BoxDecoration(
        color: SoftSaaSTokens.primaryBackground(brightness),
        border: Border(
          bottom: BorderSide(color: SoftSaaSTokens.primaryBorder(brightness)),
        ),
      ),
      child: SoftSaaSTabs(
        tabs: const [
          SoftSaaSTab(label: 'Popup', subtitle: 'Default embedded picker'),
          SoftSaaSTab(label: 'Picker', subtitle: 'Trigger & row modes'),
          SoftSaaSTab(label: 'Layers', subtitle: 'Paint stack controls'),
          SoftSaaSTab(label: 'Gradient', subtitle: 'Stop editor'),
          SoftSaaSTab(label: 'Primitives', subtitle: 'Inputs & swatches'),
        ],
        selectedIndex: selectedIndex,
        onChanged: onChanged,
      ),
    );
  }
}

class _PaneCanvas extends StatelessWidget {
  const _PaneCanvas({
    required this.controller,
    required this.mobilePageController,
    required this.onMobilePageChanged,
  });

  final ScrollController controller;
  final PageController mobilePageController;
  final ValueChanged<int> onMobilePageChanged;

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        if (constraints.maxWidth < _mobilePaneBreakpoint) {
          return PageView(
            controller: mobilePageController,
            onPageChanged: onMobilePageChanged,
            children: [
              _MobilePanePage(child: _fullPickerPane),
              _MobilePanePage(child: _pickerPane),
              _MobilePanePage(child: _layersPane),
              _MobilePanePage(child: _gradientPane),
              _MobilePanePage(child: _primitivesPane),
            ],
          );
        }

        return Scrollbar(
          controller: controller,
          thumbVisibility: true,
          child: SingleChildScrollView(
            controller: controller,
            scrollDirection: Axis.horizontal,
            primary: false,
            padding: _paneContentPadding,
            child: Row(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: const [
                _DemoPane(
                  title: 'Popup Picker',
                  subtitle: 'Default embedded picker with dropdown header',
                  icon: LucideIcons.palette,
                  width: _normalPaneWidth,
                  child: _FullPickerTab(),
                ),
                SizedBox(width: _paneGap),
                _DemoPane(
                  title: 'Picker',
                  subtitle: 'Trigger, inline row, and dialog entry points',
                  icon: LucideIcons.pipette,
                  width: _normalPaneWidth,
                  child: _PickerTab(),
                ),
                SizedBox(width: _paneGap),
                _DemoPane(
                  title: 'Layers',
                  subtitle:
                      'Paint stack editing, reordering, and blend controls',
                  icon: LucideIcons.layers,
                  width: _widePaneWidth,
                  child: _LayersTab(),
                ),
                SizedBox(width: _paneGap),
                _DemoPane(
                  title: 'Gradient',
                  subtitle: 'Stop editing and gradient preview workflows',
                  icon: LucideIcons.blend,
                  width: _normalPaneWidth,
                  child: _GradientTab(),
                ),
                SizedBox(width: _paneGap),
                _DemoPane(
                  title: 'Primitives',
                  subtitle: 'Low-level sliders, swatches, inputs, and palettes',
                  icon: LucideIcons.sliders_horizontal,
                  width: _normalPaneWidth,
                  child: _PrimitivesTab(),
                ),
              ],
            ),
          ),
        );
      },
    );
  }
}

class _MobilePanePage extends StatelessWidget {
  const _MobilePanePage({required this.child});

  final Widget child;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: _paneContentPadding,
      child: SizedBox.expand(child: child),
    );
  }
}

const Widget _fullPickerPane = _DemoPane(
  title: 'Popup Picker',
  subtitle: 'Default embedded picker with dropdown header',
  icon: LucideIcons.palette,
  width: double.infinity,
  child: _FullPickerTab(),
);

const Widget _pickerPane = _DemoPane(
  title: 'Picker',
  subtitle: 'Trigger, inline row, and dialog entry points',
  icon: LucideIcons.pipette,
  width: double.infinity,
  child: _PickerTab(),
);

const Widget _layersPane = _DemoPane(
  title: 'Layers',
  subtitle: 'Paint stack editing, reordering, and blend controls',
  icon: LucideIcons.layers,
  width: double.infinity,
  child: _LayersTab(),
);

const Widget _gradientPane = _DemoPane(
  title: 'Gradient',
  subtitle: 'Stop editing and gradient preview workflows',
  icon: LucideIcons.blend,
  width: double.infinity,
  child: _GradientTab(),
);

const Widget _primitivesPane = _DemoPane(
  title: 'Primitives',
  subtitle: 'Low-level sliders, swatches, inputs, and palettes',
  icon: LucideIcons.sliders_horizontal,
  width: double.infinity,
  child: _PrimitivesTab(),
);

class _DemoPane extends StatelessWidget {
  const _DemoPane({
    required this.title,
    required this.subtitle,
    required this.icon,
    required this.width,
    required this.child,
  });

  final String title;
  final String subtitle;
  final IconData icon;
  final double width;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    final brightness = Theme.of(context).brightness;

    return SizedBox(
      width: width,
      height: double.infinity,
      child: DecoratedBox(
        decoration: BoxDecoration(
          color: SoftSaaSTokens.primaryBackground(brightness),
          borderRadius: BorderRadius.circular(10),
          boxShadow: [
            BoxShadow(
              color: Colors.black.withValues(alpha: 0.08),
              blurRadius: 3,
              offset: const Offset(0, 1),
            ),
          ],
        ),
        child: ClipRRect(
          borderRadius: BorderRadius.circular(10),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              Container(
                height: 37,
                padding: const EdgeInsets.symmetric(
                  horizontal: 12,
                  vertical: 6,
                ),
                decoration: BoxDecoration(
                  color: SoftSaaSTokens.primaryBackground(brightness),
                  border: Border(
                    bottom: BorderSide(
                      color: SoftSaaSTokens.primaryBorder(brightness),
                    ),
                  ),
                ),
                child: Row(
                  children: [
                    Tooltip(
                      message: subtitle,
                      child: Icon(
                        icon,
                        size: 14,
                        color: SoftSaaSTokens.tertiaryText(brightness),
                      ),
                    ),
                    const SizedBox(width: 8),
                    Expanded(
                      child: Text(
                        title,
                        maxLines: 1,
                        overflow: TextOverflow.ellipsis,
                        style: TextStyle(
                          fontSize: 14,
                          height: 1.0,
                          fontWeight: FontWeight.w600,
                          letterSpacing: -0.1,
                          color: SoftSaaSTokens.primaryText(brightness),
                        ),
                      ),
                    ),
                  ],
                ),
              ),
              Expanded(child: child),
            ],
          ),
        ),
      ),
    );
  }
}

// ── Pane: Full Picker ────────────────────────────────────────────────────────

class _FullPickerTab extends StatefulWidget {
  const _FullPickerTab();

  @override
  State<_FullPickerTab> createState() => _FullPickerTabState();
}

class _FullPickerTabState extends State<_FullPickerTab> {
  Color _color = const Color(0xFF2196F3);
  late final RecentColorsManager _recentColorsManager;

  @override
  void initState() {
    super.initState();
    _recentColorsManager = RecentColorsManager.shared;
    _recentColorsManager.addListener(_handleRecentColorsChanged);
    _recentColorsManager.loadRecentColors();
  }

  @override
  void dispose() {
    _recentColorsManager.removeListener(_handleRecentColorsChanged);
    super.dispose();
  }

  void _handleRecentColorsChanged() {
    if (mounted) setState(() {});
  }

  double _panelHeight(double width) {
    final presetCount =
        DefaultPresetLibrary.getByName('Codelessly')?.swatches.length ?? 0;

    return ColorPickerPopupHeight.estimate(
      popupWidth: width,
      paint: PaintData.solid(color: _color),
      showRecentColors: true,
      recentSwatches: _recentColorsManager.swatches,
      showPresets: true,
      presetCount: presetCount,
      usesPresetLibraryDropdown: DefaultPresetLibrary.all.isNotEmpty,
      readOnly: false,
      minHeight: 0,
      maxHeight: double.infinity,
    );
  }

  @override
  Widget build(BuildContext context) {
    final brightness = Theme.of(context).brightness;

    return SingleChildScrollView(
      padding: _paneContentPadding,
      child: LayoutBuilder(
        builder: (context, constraints) {
          final width = constraints.hasBoundedWidth
              ? constraints.maxWidth
              : 300.0;

          return Container(
            decoration: BoxDecoration(
              color: SoftSaaSTokens.primaryBackground(brightness),
              borderRadius: BorderRadius.circular(8),
              border: Border.all(
                color: SoftSaaSTokens.primaryBorder(brightness),
              ),
            ),
            child: SizedBox(
              height: _panelHeight(width),
              child: ColorPickerPanel(
                color: _color,
                onColorChanged: (color) => setState(() => _color = color),
                onColorChangeEnd: () {
                  _recentColorsManager.addColor(_color);
                },
                recentSwatches: _recentColorsManager.swatches,
                onRecentSwatchAdd: _recentColorsManager.addSwatch,
                onRecentSwatchSelected: (swatch) =>
                    setState(() => _color = swatch.color),
                showPresetLibrary: true,
                showPageSwitcher: true,
                onPresetLibrarySelected: (color) =>
                    setState(() => _color = color),
                maxWidth: null,
              ),
            ),
          );
        },
      ),
    );
  }
}

// ── Pane: Picker ─────────────────────────────────────────────────────────────

class _PickerTab extends StatelessWidget {
  const _PickerTab();

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: _paneContentPadding,
      child: Center(
        child: ConstrainedBox(
          constraints: const BoxConstraints(maxWidth: 600),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              SoftSaaSPanel(
                title: 'Popup Trigger',
                subtitle: 'Click the color swatch to open a picker popup',
                icon: LucideIcons.pipette,
                child: Padding(
                  padding: _panelBodyPadding,
                  child: SizedBox(
                    width: double.infinity,
                    child: ColorPickerTriggerDemo(),
                  ),
                ),
              ),
              const SizedBox(height: _panelGap),
              SoftSaaSPanel(
                title: 'Inline Row',
                subtitle:
                    'Edit hex and opacity inline, click swatch for full picker',
                icon: LucideIcons.list,
                child: const Padding(
                  padding: _panelBodyPadding,
                  child: ColorPickerRowDemo(),
                ),
              ),
              const SizedBox(height: _panelGap),
              SoftSaaSPanel(
                title: 'Dialog Mode',
                subtitle: 'Open a color picker inside a modal dialog',
                icon: LucideIcons.panel_right,
                child: Padding(
                  padding: _panelBodyPadding,
                  child: SizedBox(
                    width: double.infinity,
                    child: DialogModeDemo(),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

// ── Tab: Layers ───────────────────────────────────────────────────────────────

class _LayersTab extends StatelessWidget {
  const _LayersTab();

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: _paneContentPadding,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          SoftSaaSPanel(
            title: 'Layer Management',
            subtitle:
                'Drag to reorder, tap swatch to pick colors, toggle visibility',
            icon: LucideIcons.layers,
            child: const Padding(
              padding: _panelBodyPadding,
              child: LayersListDemo(),
            ),
          ),
          const SizedBox(height: _panelGap),
          SoftSaaSPanel(
            title: 'Control Panel',
            subtitle: 'Paint type, blend mode, and page switching controls',
            icon: LucideIcons.sliders_horizontal,
            child: const Padding(
              padding: _panelBodyPadding,
              child: LayersControlPanelDemo(),
            ),
          ),
        ],
      ),
    );
  }
}

// ── Tab: Gradient ─────────────────────────────────────────────────────────────

class _GradientTab extends StatelessWidget {
  const _GradientTab();

  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
      padding: _paneContentPadding,
      child: Center(
        child: ConstrainedBox(
          constraints: const BoxConstraints(maxWidth: 600),
          child: const SoftSaaSPanel(
            title: 'Gradient Editor',
            subtitle:
                'Tap to add stops · drag to move · drag down to delete · Alt+drag to copy',
            icon: LucideIcons.blend,
            child: Padding(padding: _panelBodyPadding, child: GradientDemo()),
          ),
        ),
      ),
    );
  }
}

// ── Tab: Primitives ───────────────────────────────────────────────────────────

class _PrimitivesTab extends StatefulWidget {
  const _PrimitivesTab();

  @override
  State<_PrimitivesTab> createState() => _PrimitivesTabState();
}

class _PrimitivesTabState extends State<_PrimitivesTab> {
  Color _selectedColor = Colors.blue;
  Color _paletteColor = Colors.orange;
  double _rainbowPosition = 2.0;

  late final RecentColorsManager _recentColorsManager;

  final List<ColorPreset> _presets = [
    ColorPreset.solid(color: const Color(0xFF000000), label: 'Black'),
    ColorPreset.solid(color: const Color(0xFFFFFFFF), label: 'White'),
    ColorPreset.solid(color: const Color(0xFFFF0000), label: 'Red'),
    ColorPreset.solid(color: const Color(0xFF00FF00), label: 'Green'),
    ColorPreset.solid(color: const Color(0xFF0000FF), label: 'Blue'),
    ColorPreset.solid(color: const Color(0xFFFFFF00), label: 'Yellow'),
    ColorPreset.solid(color: const Color(0xFFFF00FF), label: 'Magenta'),
    ColorPreset.solid(color: const Color(0xFF00FFFF), label: 'Cyan'),
  ];

  @override
  void initState() {
    super.initState();
    _recentColorsManager = RecentColorsManager();
    _recentColorsManager.loadRecentColors().then((_) {
      if (mounted) setState(() {});
    });
  }

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

  @override
  Widget build(BuildContext context) {
    return ListenableBuilder(
      listenable: _recentColorsManager,
      builder: (context, _) {
        return SingleChildScrollView(
          padding: _paneContentPadding,
          child: Center(
            child: ConstrainedBox(
              constraints: const BoxConstraints(maxWidth: 600),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  const SoftSaaSPanel(
                    title: 'Color Field',
                    subtitle: 'Color tile swatches with transparency support',
                    icon: LucideIcons.square,
                    child: Padding(
                      padding: _panelBodyPadding,
                      child: ColorFieldSection(),
                    ),
                  ),
                  const SizedBox(height: _panelGap),
                  SoftSaaSPanel(
                    title: 'Hex Input',
                    subtitle: 'Hex color input with optional alpha channel',
                    icon: LucideIcons.hash,
                    child: Padding(
                      padding: _panelBodyPadding,
                      child: HexInputSection(
                        color: _selectedColor,
                        onChanged: (c) => setState(() => _selectedColor = c),
                      ),
                    ),
                  ),
                  const SizedBox(height: _panelGap),
                  SoftSaaSPanel(
                    title: 'Alpha Input',
                    subtitle: 'Gradient-based opacity drag input',
                    icon: LucideIcons.droplets,
                    child: Padding(
                      padding: _panelBodyPadding,
                      child: AlphaInputSection(
                        color: _selectedColor,
                        onChanged: (c) => setState(() => _selectedColor = c),
                      ),
                    ),
                  ),
                  const SizedBox(height: _panelGap),
                  SoftSaaSPanel(
                    title: 'Palette',
                    subtitle: 'Saturation/brightness picker canvas',
                    icon: LucideIcons.circle_dot,
                    child: PaletteSection(
                      paletteColor: _paletteColor,
                      onChanged: (c) => setState(() => _paletteColor = c),
                    ),
                  ),
                  const SizedBox(height: _panelGap),
                  SoftSaaSPanel(
                    title: 'Rainbow Slider',
                    subtitle: 'Hue selection slider',
                    icon: LucideIcons.sun_dim,
                    child: Padding(
                      padding: _panelBodyPadding,
                      child: RainbowSliderSection(
                        rainbowPosition: _rainbowPosition,
                        onPositionChanged: (pos, color) => setState(() {
                          _rainbowPosition = pos;
                          _paletteColor = color;
                        }),
                      ),
                    ),
                  ),
                  const SizedBox(height: _panelGap),
                  SoftSaaSPanel(
                    title: 'Alpha Slider',
                    subtitle: 'Opacity slider for the selected color',
                    icon: LucideIcons.blend,
                    child: Padding(
                      padding: _panelBodyPadding,
                      child: AlphaSliderSection(
                        color: _selectedColor,
                        onChanged: (c) => setState(() => _selectedColor = c),
                      ),
                    ),
                  ),
                  const SizedBox(height: _panelGap),
                  SoftSaaSPanel(
                    title: 'Recent Colors',
                    subtitle: 'Persisted recent color swatches',
                    icon: LucideIcons.history,
                    child: Padding(
                      padding: _panelBodyPadding,
                      child: RecentColorsSection(
                        swatches: _recentColorsManager.swatches,
                        onSelected: (swatch) => setState(() {
                          _selectedColor = swatch.color;
                          _paletteColor = swatch.color;
                        }),
                      ),
                    ),
                  ),
                  const SizedBox(height: _panelGap),
                  SoftSaaSPanel(
                    title: 'Color Presets',
                    subtitle:
                        'Named preset swatches with current color indicator',
                    icon: LucideIcons.bookmark,
                    child: Padding(
                      padding: _panelBodyPadding,
                      child: ColorPresetsSection(
                        presets: _presets,
                        currentColor: _paletteColor,
                        onSelected: (swatch) => setState(() {
                          _selectedColor = swatch.color;
                          _paletteColor = swatch.color;
                        }),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        );
      },
    );
  }
}
1
likes
160
points
58
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Full-featured Flutter color picker with hex, HSV, alpha, gradients, presets, recent colors, and design-tool style controls.

Homepage
Repository (GitHub)
View/report issues

Topics

#color-picker #flutter #gradient #design-tools #color

License

MIT (license)

Dependencies

flutter, shared_preferences

More

Packages that depend on color_picker_plus