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

A Dart package for working with colors in the Oklab color space, providing utilities for color manipulation, conversion, and analysis.

example/lib/main.dart

import 'package:example/color_wheel.dart';
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:okcolor/models/extensions.dart';
import 'package:okcolor/models/okcolor.dart';

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

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

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  int index = 0;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'OkColor Demo',
      theme: ThemeData(
        textTheme: GoogleFonts.interTextTheme().copyWith(),
        sliderTheme: const SliderThemeData(
          activeTrackColor: Colors.black,
          inactiveTrackColor: Colors.black12,
          thumbColor: Colors.black,
          trackHeight: 5,
        ),
        switchTheme: SwitchThemeData(
          thumbColor: WidgetStateProperty.all(Colors.black),
          trackColor: WidgetStateProperty.all(Colors.black12),
        ),
      ),
      home: Scaffold(
        bottomNavigationBar: BottomNavigationBar(
          items: const [
            BottomNavigationBarItem(icon: Icon(Icons.gradient), label: 'Gradient'),
            BottomNavigationBarItem(icon: Icon(Icons.color_lens), label: 'Harmonies'),
          ],
          onTap: (value) {
            setState(() {
              index = value;
            });
          },
        ),
        body: [
          const Gradient(),
          const Harmonies(),
        ][index],
      ),
    );
  }
}

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

  @override
  State<Harmonies> createState() => _HarmoniesState();
}

class _HarmoniesState extends State<Harmonies> {
  Color color = const Color(0xff0000ff);

  double darker = 0;
  double lighter = 0;
  double saturated = 0;
  double desaturated = 0;
  double rotation = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      appBar: AppBar(
        title: const Text('Color harmonies'),
      ),
      body: SafeArea(
        child: SingleChildScrollView(
          padding: const EdgeInsets.all(16),
          child: Column(
            children: [
              AnimatedContainer(
                duration: const Duration(milliseconds: 300),
                decoration: BoxDecoration(
                  borderRadius: BorderRadius.circular(24),
                  border: Border.all(color: color.darker(0.2), width: 1),
                  color: color,
                ),
                child: ClipRRect(
                  borderRadius: BorderRadius.circular(24),
                  child: Material(
                    color: Colors.transparent,
                    child: InkWell(
                      onTap: () {
                        showModalBottomSheet(
                          context: context,
                          builder: (context) {
                            return ColorPicker(
                              pickerColor: color,
                              onColorChanged: (color) {
                                setState(() {
                                  this.color = color;
                                });
                              },
                            );
                          },
                        );
                      },
                      child: const SizedBox(
                        height: 120,
                        width: double.infinity,
                      ),
                    ),
                  ),
                ),
              ),
              const SizedBox(height: 16),
              Text("Lightness", style: GoogleFonts.rubik(fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: -0.25)),
              const SizedBox(height: 16),
              Row(
                children: [
                  Expanded(
                    child: Column(
                      children: [
                        AspectRatio(
                          aspectRatio: 1,
                          child: Container(
                            decoration: BoxDecoration(
                              borderRadius: BorderRadius.circular(12),
                              color: color.darker(darker),
                            ),
                            width: double.infinity,
                          ),
                        ),
                        const SizedBox(height: 8),
                        Text('Darker', style: GoogleFonts.rubik(fontSize: 16, fontWeight: FontWeight.w500, letterSpacing: -0.25)),
                        const SizedBox(height: 8),
                        Slider(
                          value: darker,
                          onChanged: (value) {
                            setState(() {
                              darker = value;
                            });
                          },
                        ),
                      ],
                    ),
                  ),
                  const SizedBox(width: 16),
                  Expanded(
                    child: Column(
                      children: [
                        AspectRatio(
                          aspectRatio: 1,
                          child: Container(
                            decoration: BoxDecoration(
                              borderRadius: BorderRadius.circular(12),
                              color: color.lighter(lighter),
                            ),
                            width: double.infinity,
                          ),
                        ),
                        const SizedBox(height: 8),
                        Text('Lighter', style: GoogleFonts.rubik(fontSize: 16, fontWeight: FontWeight.w500, letterSpacing: -0.25)),
                        const SizedBox(height: 8),
                        Slider(
                          value: lighter,
                          onChanged: (value) {
                            setState(() {
                              lighter = value;
                            });
                          },
                        ),
                      ],
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 16),
              Text("Saturation", style: GoogleFonts.rubik(fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: -0.25)),
              const SizedBox(height: 16),
              Row(
                children: [
                  Expanded(
                    child: Column(
                      children: [
                        AspectRatio(
                          aspectRatio: 1,
                          child: Container(
                            decoration: BoxDecoration(
                              borderRadius: BorderRadius.circular(12),
                              color: color.saturate(saturated),
                            ),
                            width: double.infinity,
                          ),
                        ),
                        const SizedBox(height: 8),
                        Text('Saturated', style: GoogleFonts.rubik(fontSize: 16, fontWeight: FontWeight.w500, letterSpacing: -0.25)),
                        const SizedBox(height: 8),
                        Slider(
                          value: saturated,
                          onChanged: (value) {
                            setState(() {
                              saturated = value;
                            });
                          },
                        ),
                      ],
                    ),
                  ),
                  const SizedBox(width: 16),
                  Expanded(
                    child: Column(
                      children: [
                        AspectRatio(
                          aspectRatio: 1,
                          child: Container(
                            decoration: BoxDecoration(
                              borderRadius: BorderRadius.circular(12),
                              color: color.desaturate(desaturated),
                            ),
                            width: double.infinity,
                          ),
                        ),
                        const SizedBox(height: 8),
                        Text('Desaturated', style: GoogleFonts.rubik(fontSize: 16, fontWeight: FontWeight.w500, letterSpacing: -0.25)),
                        const SizedBox(height: 8),
                        Slider(
                          value: desaturated,
                          onChanged: (value) {
                            setState(() {
                              desaturated = value;
                            });
                          },
                        ),
                      ],
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 16),
              Text("Hues", style: GoogleFonts.rubik(fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: -0.25)),
              const SizedBox(height: 16),
              Row(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Expanded(
                    child: Column(
                      children: [
                        AspectRatio(
                          aspectRatio: 1,
                          child: Container(
                            decoration: BoxDecoration(
                              borderRadius: BorderRadius.circular(12),
                              color: color.rotated(rotation * 360),
                            ),
                            width: double.infinity,
                          ),
                        ),
                        const SizedBox(height: 8),
                        Text('Rotated', style: GoogleFonts.rubik(fontSize: 16, fontWeight: FontWeight.w500, letterSpacing: -0.25)),
                        const SizedBox(height: 8),
                        Slider(
                          value: rotation,
                          onChanged: (value) {
                            setState(() {
                              rotation = value;
                            });
                          },
                        ),
                      ],
                    ),
                  ),
                  const SizedBox(width: 16),
                  Expanded(
                    child: Column(
                      children: [
                        AspectRatio(
                          aspectRatio: 1,
                          child: Container(
                            decoration: BoxDecoration(
                              borderRadius: BorderRadius.circular(12),
                              color: color.rotated(rotation * 360).complementary(),
                            ),
                            width: double.infinity,
                          ),
                        ),
                        const SizedBox(height: 8),
                        Text('Complementary', style: GoogleFonts.rubik(fontSize: 16, fontWeight: FontWeight.w500, letterSpacing: -0.25)),
                      ],
                    ),
                  ),
                ],
              ),
              const SizedBox(height: 16),
              Text("Split complementary", style: GoogleFonts.rubik(fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: -0.25)),
              const SizedBox(height: 16),
              Row(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: color.rotated(rotation * 360).splitComplementary().map((c) {
                  return Expanded(
                    child: Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      child: AspectRatio(
                        aspectRatio: 1,
                        child: Container(
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(12),
                            color: c,
                          ),
                          width: double.infinity,
                        ),
                      ),
                    ),
                  );
                }).toList(),
              ),
              const SizedBox(height: 16),
              Text("Triadic", style: GoogleFonts.rubik(fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: -0.25)),
              const SizedBox(height: 16),
              Row(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: color.rotated(rotation * 360).triadic().map((c) {
                  return Expanded(
                    child: Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      child: AspectRatio(
                        aspectRatio: 1,
                        child: Container(
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(12),
                            color: c,
                          ),
                          width: double.infinity,
                        ),
                      ),
                    ),
                  );
                }).toList(),
              ),
              const SizedBox(height: 16),
              Text("Tetradic", style: GoogleFonts.rubik(fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: -0.25)),
              const SizedBox(height: 16),
              Row(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: color.rotated(rotation * 360).tetradic().map((c) {
                  return Expanded(
                    child: Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      child: AspectRatio(
                        aspectRatio: 1,
                        child: Container(
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(12),
                            color: c,
                          ),
                          width: double.infinity,
                        ),
                      ),
                    ),
                  );
                }).toList(),
              ),
              const SizedBox(height: 16),
              Text("Analogous", style: GoogleFonts.rubik(fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: -0.25)),
              const SizedBox(height: 16),
              Row(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: color.rotated(rotation * 360).analogous().map((c) {
                  return Expanded(
                    child: Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      child: AspectRatio(
                        aspectRatio: 1,
                        child: Container(
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(12),
                            color: c,
                          ),
                          width: double.infinity,
                        ),
                      ),
                    ),
                  );
                }).toList(),
              ),
              const SizedBox(height: 16),
              Text("Shades", style: GoogleFonts.rubik(fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: -0.25)),
              const SizedBox(height: 16),
              Row(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: color.rotated(rotation * 360).shades().map((c) {
                  return Expanded(
                    child: Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      child: AspectRatio(
                        aspectRatio: 1,
                        child: Container(
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(12),
                            color: c,
                          ),
                          width: double.infinity,
                        ),
                      ),
                    ),
                  );
                }).toList(),
              ),
              const SizedBox(height: 16),
              Text("Tints", style: GoogleFonts.rubik(fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: -0.25)),
              const SizedBox(height: 16),
              Row(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: color.rotated(rotation * 360).tints().map((c) {
                  return Expanded(
                    child: Padding(
                      padding: const EdgeInsets.symmetric(horizontal: 8),
                      child: AspectRatio(
                        aspectRatio: 1,
                        child: Container(
                          decoration: BoxDecoration(
                            borderRadius: BorderRadius.circular(12),
                            color: c,
                          ),
                          width: double.infinity,
                        ),
                      ),
                    ),
                  );
                }).toList(),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

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

  @override
  State<Gradient> createState() => _GradientState();
}

class _GradientState extends State<Gradient> {
  Color startColor = const Color(0xffffffff);
  Color endColor = const Color(0xff0000ff);
  int numberOfColors = 20;
  bool shortest = true;

  Widget _buildControls() {
    return Column(
      children: [
        Row(
          children: [
            Text(
              'Number of colors',
              style: GoogleFonts.rubik(fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: -0.25),
            ),
            Expanded(
              child: Slider(
                value: numberOfColors.toDouble(),
                min: 5,
                max: 30,
                divisions: 25,
                label: numberOfColors.toString(),
                onChanged: (value) {
                  setState(() {
                    numberOfColors = value.toInt();
                  });
                },
                activeColor: Colors.black,
                inactiveColor: Colors.black12,
              ),
            ),
          ],
        ),
      ],
    );
  }

  Widget _buildGradient(String title, List<Color> colors) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(
          title,
          style: GoogleFonts.rubik(fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: -0.25),
        ),
        const SizedBox(height: 8),
        AnimatedContainer(
          duration: Durations.short4,
          width: double.infinity,
          height: 64,
          decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(14),
            border: Border.all(color: Colors.black12, width: 1),
            gradient: LinearGradient(
              colors: colors,
            ),
          ),
        ),
      ],
    );
  }

  Widget _buildColorWheel() {
    return Column(
      children: [
        Text(
          'Color Wheel',
          style: GoogleFonts.rubik(fontSize: 18, fontWeight: FontWeight.w500, letterSpacing: -0.25),
        ),
        AspectRatio(
          aspectRatio: 1,
          child: Transform.flip(
            flipY: true,
            child: HSVColorWheel(
              gradientColors: OkColor.gradient(startColor, endColor, numberOfColors: numberOfColors, method: InterpolationMethod.oklch),
            ),
          ),
        ),
      ],
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.white,
      body: SafeArea(
        child: SingleChildScrollView(
          padding: const EdgeInsets.all(16),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: <Widget>[
              Text(
                'Gradient',
                style: GoogleFonts.rubik(fontSize: 24, fontWeight: FontWeight.w500, letterSpacing: -0.25),
              ),
              const SizedBox(height: 20),
              Row(
                children: [
                  Expanded(
                    child: _buildColorPicker(context, startColor, 'Start Color', (color) {
                      setState(() {
                        startColor = color;
                      });
                    }),
                  ),
                  const SizedBox(width: 16),
                  Expanded(
                    child: _buildColorPicker(context, endColor, 'End Color', (color) {
                      setState(() {
                        endColor = color;
                      });
                    }),
                  ),
                ],
              ),
              const SizedBox(height: 8),
              _buildColorWheel(),
              const SizedBox(height: 8),
              _buildControls(),
              const SizedBox(height: 8),
              _buildGradient('RGB', OkColor.gradient(startColor, endColor, numberOfColors: numberOfColors, method: InterpolationMethod.rgb)),
              const SizedBox(height: 8),
              _buildGradient('HSV', OkColor.gradient(startColor, endColor, numberOfColors: numberOfColors, method: InterpolationMethod.hsv)),
              const SizedBox(height: 8),
              _buildGradient('OkLab', OkColor.gradient(startColor, endColor, numberOfColors: numberOfColors, method: InterpolationMethod.oklab)),
              const SizedBox(height: 8),
              _buildGradient('OkHsv', OkColor.gradient(startColor, endColor, numberOfColors: numberOfColors, method: InterpolationMethod.okhsv)),
              const SizedBox(height: 8),
              _buildGradient('OkLch', OkColor.gradient(startColor, endColor, numberOfColors: numberOfColors, method: InterpolationMethod.oklch)),
            ],
          ),
        ),
      ),
    );
  }
}

Widget _buildColorPicker(BuildContext context, Color color, String label, Function(Color) onColorChanged) {
  return Column(
    crossAxisAlignment: CrossAxisAlignment.center,
    children: [
      AnimatedContainer(
        duration: const Duration(milliseconds: 300),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(24),
          border: Border.all(color: Colors.black12, width: 1),
          color: color,
        ),
        child: ClipRRect(
          borderRadius: BorderRadius.circular(24),
          child: Material(
            color: Colors.transparent,
            child: InkWell(
              onTap: () {
                showModalBottomSheet(
                  context: context,
                  builder: (context) {
                    return ColorPicker(
                      pickerColor: color,
                      onColorChanged: onColorChanged,
                    );
                  },
                );
              },
              child: const AspectRatio(
                aspectRatio: 1,
                child: SizedBox(
                  width: double.infinity,
                ),
              ),
            ),
          ),
        ),
      ),
      const SizedBox(height: 8),
      Text(
        label,
        style: GoogleFonts.rubik(fontSize: 16, fontWeight: FontWeight.w500, letterSpacing: -0.25),
      ),
    ],
  );
}
4
likes
140
points
554
downloads

Publisher

verified publishereliseyozerov.com

Weekly Downloads

A Dart package for working with colors in the Oklab color space, providing utilities for color manipulation, conversion, and analysis.

Repository (GitHub)
View/report issues

Documentation

API reference

License

MIT (license)

Dependencies

collection, flutter, vector_math

More

Packages that depend on okcolor