From dd23f7aacea4e201e8e1a65096d1a02d60ae5625 Mon Sep 17 00:00:00 2001
From: Ellet <73608287+ellet0@users.noreply.github.com>
Date: Sat, 18 May 2024 13:53:01 +0300
Subject: [PATCH] Feat/support latest stable flutter (#1874)
* fix: temporarily remove flutter_colorpicker from pub.dev
* chore: clone flutter_colorpicker from Github, add a TODO with it
* fix: update color_dialog.dart to use color picker package from the lib/src/packages/flutter_colorpicker
* refactor(example): remove the old android example and recreate it to get it working with the latest stable version without any warrnings
* fix: format flutter_colorpicker to fix CI failure, update the android example project to use latest version of Kotlin, fix AndroidManifest string resources
* fix: update the linux example to fix CI failure
* ci: update build.yml as an attemp to fix building the Linux application
* ci: add a todo in build.yml, remove flutter doctor check
* ci: fix a typo
* ci: update the name of each step
---
.github/workflows/build.yml | 19 +-
example/.metadata | 25 +-
example/android/app/build.gradle | 47 +-
.../android/app/src/main/AndroidManifest.xml | 4 +-
.../app/src/main/res/values/strings.xml | 4 -
example/android/build.gradle | 60 +-
example/android/gradle.properties | 5 +-
.../gradle/wrapper/gradle-wrapper.properties | 2 +-
example/android/settings.gradle | 20 +-
example/devtools_options.yaml | 1 -
example/linux/CMakeLists.txt | 6 +
example/linux/my_application.cc | 20 +
example/pubspec.yaml | 3 -
.../flutter_colorpicker.dart | 9 +
.../flutter_colorpicker/src/block_picker.dart | 211 +++
.../flutter_colorpicker/src/colorpicker.dart | 891 ++++++++++
.../flutter_colorpicker/src/colors.dart | 172 ++
.../src/material_picker.dart | 384 +++++
.../flutter_colorpicker/src/palette.dart | 1523 +++++++++++++++++
.../flutter_colorpicker/src/utils.dart | 224 +++
.../toolbar/buttons/color/color_dialog.dart | 4 +-
pubspec.yaml | 3 +-
22 files changed, 3499 insertions(+), 138 deletions(-)
delete mode 100644 example/android/app/src/main/res/values/strings.xml
delete mode 100644 example/devtools_options.yaml
create mode 100644 lib/src/packages/flutter_colorpicker/flutter_colorpicker.dart
create mode 100644 lib/src/packages/flutter_colorpicker/src/block_picker.dart
create mode 100644 lib/src/packages/flutter_colorpicker/src/colorpicker.dart
create mode 100644 lib/src/packages/flutter_colorpicker/src/colors.dart
create mode 100644 lib/src/packages/flutter_colorpicker/src/material_picker.dart
create mode 100644 lib/src/packages/flutter_colorpicker/src/palette.dart
create mode 100644 lib/src/packages/flutter_colorpicker/src/utils.dart
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 72a76630..a0344f60 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -1,3 +1,5 @@
+# TODO: Update the workflow to build on Android, iOS, desktop (macOS and Windows) instead of just Linux and Web
+
name: Build the example
on:
@@ -16,26 +18,29 @@ jobs:
channel: 'stable'
cache: true
- - name: Check flutter version
+ - name: Check Flutter Version
run: flutter --version
- - name: Enable Local Dev
+ - name: Enable Local Development Environment (use the local packages)
run: ./scripts/enable_local_dev.sh
- - name: Install dependencies
+ - name: Install Flutter Dependencies
run: flutter pub get
- - name: Flutter build Web
+ - name: Build Flutter Web Application
run: flutter build web --release --verbose --dart-define=CI=true
working-directory: ./example
- - name: Updates APT Linux Package Lists && Upgrade
+ - name: Update and Upgrade APT Packages
run: sudo apt update -y && sudo apt upgrade -y
- - name: Install flutter Linux prerequisites
+ - name: Install Flutter Linux Prerequisites
run: sudo apt install -y curl git unzip xz-utils zip libglu1-mesa
- - name: Flutter build Linux
+ - name: Install Flutter Linux Desktop Dependencies
+ run: sudo apt install -y clang cmake git ninja-build pkg-config libgtk-3-dev liblzma-dev libstdc++-12-dev
+
+ - name: Build Flutter Linux Desktop Application
run: flutter build linux --release --verbose --dart-define=CI=true
working-directory: ./example
diff --git a/example/.metadata b/example/.metadata
index d22992ed..e724e168 100644
--- a/example/.metadata
+++ b/example/.metadata
@@ -4,7 +4,7 @@
# This file should be version controlled and should not be manually edited.
version:
- revision: "db7ef5bf9f59442b0e200a90587e8fa5e0c6336a"
+ revision: "5dcb86f68f239346676ceb1ed1ea385bd215fba1"
channel: "stable"
project_type: app
@@ -13,26 +13,11 @@ project_type: app
migration:
platforms:
- platform: root
- create_revision: db7ef5bf9f59442b0e200a90587e8fa5e0c6336a
- base_revision: db7ef5bf9f59442b0e200a90587e8fa5e0c6336a
- - platform: android
- create_revision: db7ef5bf9f59442b0e200a90587e8fa5e0c6336a
- base_revision: db7ef5bf9f59442b0e200a90587e8fa5e0c6336a
- - platform: ios
- create_revision: db7ef5bf9f59442b0e200a90587e8fa5e0c6336a
- base_revision: db7ef5bf9f59442b0e200a90587e8fa5e0c6336a
+ create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
+ base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
- platform: linux
- create_revision: db7ef5bf9f59442b0e200a90587e8fa5e0c6336a
- base_revision: db7ef5bf9f59442b0e200a90587e8fa5e0c6336a
- - platform: macos
- create_revision: db7ef5bf9f59442b0e200a90587e8fa5e0c6336a
- base_revision: db7ef5bf9f59442b0e200a90587e8fa5e0c6336a
- - platform: web
- create_revision: db7ef5bf9f59442b0e200a90587e8fa5e0c6336a
- base_revision: db7ef5bf9f59442b0e200a90587e8fa5e0c6336a
- - platform: windows
- create_revision: db7ef5bf9f59442b0e200a90587e8fa5e0c6336a
- base_revision: db7ef5bf9f59442b0e200a90587e8fa5e0c6336a
+ create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
+ base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1
# User provided section
diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle
index aa36e402..b8a6b7f8 100644
--- a/example/android/app/build.gradle
+++ b/example/android/app/build.gradle
@@ -1,62 +1,53 @@
plugins {
id "com.android.application"
id "kotlin-android"
+ // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
-def localPropertiesFile = rootProject.file('local.properties')
+def localPropertiesFile = rootProject.file("local.properties")
if (localPropertiesFile.exists()) {
- localPropertiesFile.withReader('UTF-8') { reader ->
+ localPropertiesFile.withReader("UTF-8") { reader ->
localProperties.load(reader)
}
}
-def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
if (flutterVersionCode == null) {
- flutterVersionCode = '1'
+ flutterVersionCode = "1"
}
-def flutterVersionName = localProperties.getProperty('flutter.versionName')
+def flutterVersionName = localProperties.getProperty("flutter.versionName")
if (flutterVersionName == null) {
- flutterVersionName = '1.0'
+ flutterVersionName = "1.0"
}
android {
- namespace "com.example.example"
- compileSdkVersion flutter.compileSdkVersion
- ndkVersion flutter.ndkVersion
+ namespace = "com.example.example"
+ compileSdk = flutter.compileSdkVersion
+ ndkVersion = flutter.ndkVersion
compileOptions {
- sourceCompatibility JavaVersion.VERSION_17
- targetCompatibility JavaVersion.VERSION_17
- }
-
- kotlinOptions {
- jvmTarget = '1.8'
- }
-
- sourceSets {
- main.java.srcDirs += 'src/main/kotlin'
+ sourceCompatibility = JavaVersion.VERSION_1_8
+ targetCompatibility = JavaVersion.VERSION_1_8
}
defaultConfig {
- applicationId "com.example.example"
- minSdkVersion 23
- targetSdkVersion flutter.targetSdkVersion
- versionCode flutterVersionCode.toInteger()
- versionName flutterVersionName
+ applicationId = "com.example.example"
+ minSdk = 23
+ targetSdk = flutter.targetSdkVersion
+ versionCode = flutterVersionCode.toInteger()
+ versionName = flutterVersionName
}
buildTypes {
release {
- signingConfig signingConfigs.debug
+ signingConfig = signingConfigs.debug
}
}
}
flutter {
- source '../..'
+ source = "../.."
}
-
-dependencies {}
diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml
index bf856053..e68e61df 100644
--- a/example/android/app/src/main/AndroidManifest.xml
+++ b/example/android/app/src/main/AndroidManifest.xml
@@ -23,7 +23,7 @@
+ android:label="Flutter Quill Example">
-
+
\ No newline at end of file
diff --git a/example/android/app/src/main/res/values/strings.xml b/example/android/app/src/main/res/values/strings.xml
deleted file mode 100644
index 5c509355..00000000
--- a/example/android/app/src/main/res/values/strings.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-
-
- Flutter Quill Demo
-
\ No newline at end of file
diff --git a/example/android/build.gradle b/example/android/build.gradle
index 76a4bae8..d2ffbffa 100644
--- a/example/android/build.gradle
+++ b/example/android/build.gradle
@@ -1,18 +1,3 @@
-import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
-
-buildscript {
- ext.kotlin_version = '1.9.21'
- repositories {
- google()
- mavenCentral()
- }
-
- dependencies {
- classpath 'com.android.tools.build:gradle:8.2.0'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- }
-}
-
allprojects {
repositories {
google()
@@ -20,53 +5,12 @@ allprojects {
}
}
-rootProject.buildDir = '../build'
+rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
-
- // For mode details visit https://gist.github.com/ellet0/93fefb39e48c40592bda3931e05fd35c
- afterEvaluate {
- // check if android block is available
-
- if (it.hasProperty('android')) {
-
- if (it.android.namespace == null) {
- def manifest = new XmlSlurper().parse(file(it.android.sourceSets.main.manifest.srcFile))
- def packageName = manifest.@package.text()
- println("Setting ${packageName} as android namespace in build.gradle from the AndroidManifest.xml")
- android.namespace = packageName
- }
-
- def javaVersion = JavaVersion.VERSION_17
- println("Changes will be applied for the following packages:")
- android {
- def androidApiVersion = 34
-// compileSdkVersion androidApiVersion
- compileSdk androidApiVersion
- defaultConfig {
- targetSdkVersion androidApiVersion
- }
- compileOptions {
- sourceCompatibility javaVersion
- targetCompatibility javaVersion
- }
- tasks.withType(KotlinCompile).configureEach {
- buildscript {
- ext.kotlin_version = kotlin_version
- }
- kotlinOptions {
- jvmTarget = javaVersion.toString()
- }
- }
- String message = "For package ${android.namespace} by update compileSdkVersion, targetSdkVersion \n to $androidApiVersion and java version to ${javaVersion.toString()}"
- println(message)
- }
- }
-
- }
}
subprojects {
- project.evaluationDependsOn(':app')
+ project.evaluationDependsOn(":app")
}
tasks.register("clean", Delete) {
diff --git a/example/android/gradle.properties b/example/android/gradle.properties
index b9a9a246..3b5b324f 100644
--- a/example/android/gradle.properties
+++ b/example/android/gradle.properties
@@ -1,6 +1,3 @@
-org.gradle.jvmargs=-Xmx1536M
+org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
-android.defaults.buildfeatures.buildconfig=true
-android.nonTransitiveRClass=false
-android.nonFinalResIds=false
diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/example/android/gradle/wrapper/gradle-wrapper.properties
index aa49780c..e1ca574e 100644
--- a/example/android/gradle/wrapper/gradle-wrapper.properties
+++ b/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip
diff --git a/example/android/settings.gradle b/example/android/settings.gradle
index 55c4ca8b..ee3c79c1 100644
--- a/example/android/settings.gradle
+++ b/example/android/settings.gradle
@@ -5,16 +5,22 @@ pluginManagement {
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
- }
- settings.ext.flutterSdkPath = flutterSdkPath()
+ }()
- includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
- plugins {
- id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
}
}
-include ":app"
+plugins {
+ id "dev.flutter.flutter-plugin-loader" version "1.0.0"
+ id "com.android.application" version "7.3.0" apply false
+ // TODO: We should update the project to not require higher version of Kotlin
+ id "org.jetbrains.kotlin.android" version "1.9.24" apply false
+}
-apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle"
+include ":app"
diff --git a/example/devtools_options.yaml b/example/devtools_options.yaml
deleted file mode 100644
index 7e7e7f67..00000000
--- a/example/devtools_options.yaml
+++ /dev/null
@@ -1 +0,0 @@
-extensions:
diff --git a/example/linux/CMakeLists.txt b/example/linux/CMakeLists.txt
index d67bd4e0..9cb0d1dd 100644
--- a/example/linux/CMakeLists.txt
+++ b/example/linux/CMakeLists.txt
@@ -123,6 +123,12 @@ foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})
COMPONENT Runtime)
endforeach(bundled_library)
+# Copy the native assets provided by the build.dart from all packages.
+set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/")
+install(DIRECTORY "${NATIVE_ASSETS_DIR}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
# Fully re-copy the assets directory on each build to avoid having stale files
# from a previous install.
set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
diff --git a/example/linux/my_application.cc b/example/linux/my_application.cc
index 0ba8f430..c0530d42 100644
--- a/example/linux/my_application.cc
+++ b/example/linux/my_application.cc
@@ -81,6 +81,24 @@ static gboolean my_application_local_command_line(GApplication* application, gch
return TRUE;
}
+// Implements GApplication::startup.
+static void my_application_startup(GApplication* application) {
+ //MyApplication* self = MY_APPLICATION(object);
+
+ // Perform any actions required at application startup.
+
+ G_APPLICATION_CLASS(my_application_parent_class)->startup(application);
+}
+
+// Implements GApplication::shutdown.
+static void my_application_shutdown(GApplication* application) {
+ //MyApplication* self = MY_APPLICATION(object);
+
+ // Perform any actions required at application shutdown.
+
+ G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);
+}
+
// Implements GObject::dispose.
static void my_application_dispose(GObject* object) {
MyApplication* self = MY_APPLICATION(object);
@@ -91,6 +109,8 @@ static void my_application_dispose(GObject* object) {
static void my_application_class_init(MyApplicationClass* klass) {
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;
+ G_APPLICATION_CLASS(klass)->startup = my_application_startup;
+ G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;
G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
}
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
index 360a03ab..85d52bf0 100644
--- a/example/pubspec.yaml
+++ b/example/pubspec.yaml
@@ -63,9 +63,6 @@ dependency_overrides:
quill_pdf_converter:
path: ../quill_pdf_converter
- # TODO: Temporarily add this so the example can work
- flutter_colorpicker:
- git: https://github.com/mchome/flutter_colorpicker.git
pdf_widget_wrapper: ^1.0.4
diff --git a/lib/src/packages/flutter_colorpicker/flutter_colorpicker.dart b/lib/src/packages/flutter_colorpicker/flutter_colorpicker.dart
new file mode 100644
index 00000000..fee43157
--- /dev/null
+++ b/lib/src/packages/flutter_colorpicker/flutter_colorpicker.dart
@@ -0,0 +1,9 @@
+library flutter_colorpicker;
+
+// TODO: temporarily clone https://pub.dev/packages/flutter_colorpicker as it's hasn't been published on pub.dev for a while
+
+export 'src/block_picker.dart';
+export 'src/colorpicker.dart';
+export 'src/material_picker.dart';
+export 'src/palette.dart';
+export 'src/utils.dart';
diff --git a/lib/src/packages/flutter_colorpicker/src/block_picker.dart b/lib/src/packages/flutter_colorpicker/src/block_picker.dart
new file mode 100644
index 00000000..fd6c488b
--- /dev/null
+++ b/lib/src/packages/flutter_colorpicker/src/block_picker.dart
@@ -0,0 +1,211 @@
+// ignore_for_file: type=lint
+
+/// Blocky Color Picker
+
+library block_colorpicker;
+
+import 'package:flutter/material.dart';
+import 'utils.dart';
+
+/// Child widget for layout builder.
+typedef PickerItem = Widget Function(Color color);
+
+/// Customize the layout.
+typedef PickerLayoutBuilder = Widget Function(
+ BuildContext context, List colors, PickerItem child);
+
+/// Customize the item shape.
+typedef PickerItemBuilder = Widget Function(
+ Color color, bool isCurrentColor, void Function() changeColor);
+
+// Provide a list of colors for block color picker.
+const List _defaultColors = [
+ Colors.red,
+ Colors.pink,
+ Colors.purple,
+ Colors.deepPurple,
+ Colors.indigo,
+ Colors.blue,
+ Colors.lightBlue,
+ Colors.cyan,
+ Colors.teal,
+ Colors.green,
+ Colors.lightGreen,
+ Colors.lime,
+ Colors.yellow,
+ Colors.amber,
+ Colors.orange,
+ Colors.deepOrange,
+ Colors.brown,
+ Colors.grey,
+ Colors.blueGrey,
+ Colors.black,
+];
+
+// Provide a layout for [BlockPicker].
+Widget _defaultLayoutBuilder(
+ BuildContext context, List colors, PickerItem child) {
+ Orientation orientation = MediaQuery.of(context).orientation;
+
+ return SizedBox(
+ width: 300,
+ height: orientation == Orientation.portrait ? 360 : 200,
+ child: GridView.count(
+ crossAxisCount: orientation == Orientation.portrait ? 4 : 6,
+ crossAxisSpacing: 5,
+ mainAxisSpacing: 5,
+ children: [for (Color color in colors) child(color)],
+ ),
+ );
+}
+
+// Provide a shape for [BlockPicker].
+Widget _defaultItemBuilder(
+ Color color, bool isCurrentColor, void Function() changeColor) {
+ return Container(
+ margin: const EdgeInsets.all(7),
+ decoration: BoxDecoration(
+ shape: BoxShape.circle,
+ color: color,
+ boxShadow: [
+ BoxShadow(
+ color: color.withOpacity(0.8),
+ offset: const Offset(1, 2),
+ blurRadius: 5)
+ ],
+ ),
+ child: Material(
+ color: Colors.transparent,
+ child: InkWell(
+ onTap: changeColor,
+ borderRadius: BorderRadius.circular(50),
+ child: AnimatedOpacity(
+ duration: const Duration(milliseconds: 210),
+ opacity: isCurrentColor ? 1 : 0,
+ child: Icon(Icons.done,
+ color: useWhiteForeground(color) ? Colors.white : Colors.black),
+ ),
+ ),
+ ),
+ );
+}
+
+// The blocky color picker you can alter the layout and shape.
+class BlockPicker extends StatefulWidget {
+ const BlockPicker({
+ Key? key,
+ required this.pickerColor,
+ required this.onColorChanged,
+ this.availableColors = _defaultColors,
+ this.useInShowDialog = true,
+ this.layoutBuilder = _defaultLayoutBuilder,
+ this.itemBuilder = _defaultItemBuilder,
+ }) : super(key: key);
+
+ final Color? pickerColor;
+ final ValueChanged onColorChanged;
+ final List availableColors;
+ final bool useInShowDialog;
+ final PickerLayoutBuilder layoutBuilder;
+ final PickerItemBuilder itemBuilder;
+
+ @override
+ State createState() => _BlockPickerState();
+}
+
+class _BlockPickerState extends State {
+ Color? _currentColor;
+
+ @override
+ void initState() {
+ _currentColor = widget.pickerColor;
+ super.initState();
+ }
+
+ void changeColor(Color color) {
+ setState(() => _currentColor = color);
+ widget.onColorChanged(color);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return widget.layoutBuilder(
+ context,
+ widget.availableColors,
+ (Color color) => widget.itemBuilder(
+ color,
+ (_currentColor != null &&
+ (widget.useInShowDialog ? true : widget.pickerColor != null))
+ ? (_currentColor?.value == color.value) &&
+ (widget.useInShowDialog
+ ? true
+ : widget.pickerColor?.value == color.value)
+ : false,
+ () => changeColor(color),
+ ),
+ );
+ }
+}
+
+// The blocky color picker you can alter the layout and shape with multiple choice.
+class MultipleChoiceBlockPicker extends StatefulWidget {
+ const MultipleChoiceBlockPicker({
+ Key? key,
+ required this.pickerColors,
+ required this.onColorsChanged,
+ this.availableColors = _defaultColors,
+ this.useInShowDialog = true,
+ this.layoutBuilder = _defaultLayoutBuilder,
+ this.itemBuilder = _defaultItemBuilder,
+ }) : super(key: key);
+
+ final List? pickerColors;
+ final ValueChanged> onColorsChanged;
+ final List availableColors;
+ final bool useInShowDialog;
+ final PickerLayoutBuilder layoutBuilder;
+ final PickerItemBuilder itemBuilder;
+
+ @override
+ State createState() => _MultipleChoiceBlockPickerState();
+}
+
+class _MultipleChoiceBlockPickerState extends State {
+ List? _currentColors;
+
+ @override
+ void initState() {
+ _currentColors = widget.pickerColors;
+ super.initState();
+ }
+
+ void toggleColor(Color color) {
+ setState(() {
+ if (_currentColors != null) {
+ _currentColors!.contains(color)
+ ? _currentColors!.remove(color)
+ : _currentColors!.add(color);
+ }
+ });
+ widget.onColorsChanged(_currentColors ?? []);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return widget.layoutBuilder(
+ context,
+ widget.availableColors,
+ (Color color) => widget.itemBuilder(
+ color,
+ (_currentColors != null &&
+ (widget.useInShowDialog ? true : widget.pickerColors != null))
+ ? _currentColors!.contains(color) &&
+ (widget.useInShowDialog
+ ? true
+ : widget.pickerColors!.contains(color))
+ : false,
+ () => toggleColor(color),
+ ),
+ );
+ }
+}
diff --git a/lib/src/packages/flutter_colorpicker/src/colorpicker.dart b/lib/src/packages/flutter_colorpicker/src/colorpicker.dart
new file mode 100644
index 00000000..68c8f649
--- /dev/null
+++ b/lib/src/packages/flutter_colorpicker/src/colorpicker.dart
@@ -0,0 +1,891 @@
+// ignore_for_file: type=lint
+
+/// HSV(HSB)/HSL Color Picker example
+///
+/// You can create your own layout by importing `picker.dart`.
+
+library hsv_picker;
+
+import 'package:flutter/material.dart';
+import 'palette.dart';
+import 'utils.dart';
+
+/// The default layout of Color Picker.
+class ColorPicker extends StatefulWidget {
+ const ColorPicker({
+ Key? key,
+ required this.pickerColor,
+ required this.onColorChanged,
+ this.pickerHsvColor,
+ this.onHsvColorChanged,
+ this.paletteType = PaletteType.hsvWithHue,
+ this.enableAlpha = true,
+ @Deprecated('Use empty list in [labelTypes] to disable label.')
+ this.showLabel = true,
+ this.labelTypes = const [
+ ColorLabelType.rgb,
+ ColorLabelType.hsv,
+ ColorLabelType.hsl
+ ],
+ @Deprecated(
+ 'Use Theme.of(context).textTheme.bodyText1 & 2 to alter text style.')
+ this.labelTextStyle,
+ this.displayThumbColor = false,
+ this.portraitOnly = false,
+ this.colorPickerWidth = 300.0,
+ this.pickerAreaHeightPercent = 1.0,
+ this.pickerAreaBorderRadius = const BorderRadius.all(Radius.zero),
+ this.hexInputBar = false,
+ this.hexInputController,
+ this.colorHistory,
+ this.onHistoryChanged,
+ }) : super(key: key);
+
+ final Color pickerColor;
+ final ValueChanged onColorChanged;
+ final HSVColor? pickerHsvColor;
+ final ValueChanged? onHsvColorChanged;
+ final PaletteType paletteType;
+ final bool enableAlpha;
+ final bool showLabel;
+ final List labelTypes;
+ final TextStyle? labelTextStyle;
+ final bool displayThumbColor;
+ final bool portraitOnly;
+ final double colorPickerWidth;
+ final double pickerAreaHeightPercent;
+ final BorderRadius pickerAreaBorderRadius;
+ final bool hexInputBar;
+
+ /// Allows setting the color using text input, via [TextEditingController].
+ ///
+ /// Listens to [String] input and trying to convert it to the valid [Color].
+ /// Contains basic validator, that requires final input to be provided
+ /// in one of those formats:
+ ///
+ /// * RGB
+ /// * #RGB
+ /// * RRGGBB
+ /// * #RRGGBB
+ /// * AARRGGBB
+ /// * #AARRGGBB
+ ///
+ /// Where: A stands for Alpha, R for Red, G for Green, and B for blue color.
+ /// It will only accept 3/6/8 long HEXs with an optional hash (`#`) at the beginning.
+ /// Allowed characters are Latin A-F case insensitive and numbers 0-9.
+ /// It does respect the [enableAlpha] flag, so if alpha is disabled, all inputs
+ /// with transparency are also converted to non-transparent color values.
+ /// ```dart
+ /// MaterialButton(
+ /// elevation: 3.0,
+ /// onPressed: () {
+ /// // The initial value can be provided directly to the controller.
+ /// final textController =
+ /// TextEditingController(text: '#2F19DB');
+ /// showDialog(
+ /// context: context,
+ /// builder: (BuildContext context) {
+ /// return AlertDialog(
+ /// scrollable: true,
+ /// titlePadding: const EdgeInsets.all(0.0),
+ /// contentPadding: const EdgeInsets.all(0.0),
+ /// content: Column(
+ /// children: [
+ /// ColorPicker(
+ /// pickerColor: currentColor,
+ /// onColorChanged: changeColor,
+ /// colorPickerWidth: 300.0,
+ /// pickerAreaHeightPercent: 0.7,
+ /// enableAlpha:
+ /// true, // hexInputController will respect it too.
+ /// displayThumbColor: true,
+ /// showLabel: true,
+ /// paletteType: PaletteType.hsv,
+ /// pickerAreaBorderRadius: const BorderRadius.only(
+ /// topLeft: const Radius.circular(2.0),
+ /// topRight: const Radius.circular(2.0),
+ /// ),
+ /// hexInputController: textController, // <- here
+ /// portraitOnly: true,
+ /// ),
+ /// Padding(
+ /// padding: const EdgeInsets.all(16),
+ /// /* It can be any text field, for example:
+ /// * TextField
+ /// * TextFormField
+ /// * CupertinoTextField
+ /// * EditableText
+ /// * any text field from 3-rd party package
+ /// * your own text field
+ /// so basically anything that supports/uses
+ /// a TextEditingController for an editable text.
+ /// */
+ /// child: CupertinoTextField(
+ /// controller: textController,
+ /// // Everything below is purely optional.
+ /// prefix: Padding(
+ /// padding: const EdgeInsets.only(left: 8),
+ /// child: const Icon(Icons.tag),
+ /// ),
+ /// suffix: IconButton(
+ /// icon:
+ /// const Icon(Icons.content_paste_rounded),
+ /// onPressed: () async =>
+ /// copyToClipboard(textController.text),
+ /// ),
+ /// autofocus: true,
+ /// maxLength: 9,
+ /// inputFormatters: [
+ /// // Any custom input formatter can be passed
+ /// // here or use any Form validator you want.
+ /// UpperCaseTextFormatter(),
+ /// FilteringTextInputFormatter.allow(
+ /// RegExp(kValidHexPattern)),
+ /// ],
+ /// ),
+ /// )
+ /// ],
+ /// ),
+ /// );
+ /// },
+ /// );
+ /// },
+ /// child: const Text('Change me via text input'),
+ /// color: currentColor,
+ /// textColor: useWhiteForeground(currentColor)
+ /// ? const Color(0xffffffff)
+ /// : const Color(0xff000000),
+ /// ),
+ /// ```
+ ///
+ /// Do not forget to `dispose()` your [TextEditingController] if you creating
+ /// it inside any kind of [StatefulWidget]'s [State].
+ /// Reference: https://en.wikipedia.org/wiki/Web_colors#Hex_triplet
+ final TextEditingController? hexInputController;
+ final List? colorHistory;
+ final ValueChanged>? onHistoryChanged;
+
+ @override
+ _ColorPickerState createState() => _ColorPickerState();
+}
+
+class _ColorPickerState extends State {
+ HSVColor currentHsvColor = const HSVColor.fromAHSV(0.0, 0.0, 0.0, 0.0);
+ List colorHistory = [];
+
+ @override
+ void initState() {
+ currentHsvColor = (widget.pickerHsvColor != null)
+ ? widget.pickerHsvColor as HSVColor
+ : HSVColor.fromColor(widget.pickerColor);
+ // If there's no initial text in `hexInputController`,
+ if (widget.hexInputController?.text.isEmpty == true) {
+ // set it to the current's color HEX value.
+ widget.hexInputController?.text = colorToHex(
+ currentHsvColor.toColor(),
+ enableAlpha: widget.enableAlpha,
+ );
+ }
+ // Listen to the text input, If there is an `hexInputController` provided.
+ widget.hexInputController?.addListener(colorPickerTextInputListener);
+ if (widget.colorHistory != null && widget.onHistoryChanged != null) {
+ colorHistory = widget.colorHistory ?? [];
+ }
+ super.initState();
+ }
+
+ @override
+ void didUpdateWidget(ColorPicker oldWidget) {
+ super.didUpdateWidget(oldWidget);
+ currentHsvColor = (widget.pickerHsvColor != null)
+ ? widget.pickerHsvColor as HSVColor
+ : HSVColor.fromColor(widget.pickerColor);
+ }
+
+ void colorPickerTextInputListener() {
+ // It can't be null really, since it's only listening if the controller
+ // is provided, but it may help to calm the Dart analyzer in the future.
+ if (widget.hexInputController == null) return;
+ // If a user is inserting/typing any text — try to get the color value from it,
+ // and interpret its transparency, dependent on the widget's settings.
+ final Color? color = colorFromHex(widget.hexInputController!.text,
+ enableAlpha: widget.enableAlpha);
+ // If it's the valid color:
+ if (color != null) {
+ // set it as the current color and
+ setState(() => currentHsvColor = HSVColor.fromColor(color));
+ // notify with a callback.
+ widget.onColorChanged(color);
+ if (widget.onHsvColorChanged != null)
+ widget.onHsvColorChanged!(currentHsvColor);
+ }
+ }
+
+ @override
+ void dispose() {
+ widget.hexInputController?.removeListener(colorPickerTextInputListener);
+ super.dispose();
+ }
+
+ Widget colorPickerSlider(TrackType trackType) {
+ return ColorPickerSlider(
+ trackType,
+ currentHsvColor,
+ (HSVColor color) {
+ // Update text in `hexInputController` if provided.
+ widget.hexInputController?.text =
+ colorToHex(color.toColor(), enableAlpha: widget.enableAlpha);
+ setState(() => currentHsvColor = color);
+ widget.onColorChanged(currentHsvColor.toColor());
+ if (widget.onHsvColorChanged != null)
+ widget.onHsvColorChanged!(currentHsvColor);
+ },
+ displayThumbColor: widget.displayThumbColor,
+ );
+ }
+
+ void onColorChanging(HSVColor color) {
+ // Update text in `hexInputController` if provided.
+ widget.hexInputController?.text =
+ colorToHex(color.toColor(), enableAlpha: widget.enableAlpha);
+ setState(() => currentHsvColor = color);
+ widget.onColorChanged(currentHsvColor.toColor());
+ if (widget.onHsvColorChanged != null)
+ widget.onHsvColorChanged!(currentHsvColor);
+ }
+
+ Widget colorPicker() {
+ return ClipRRect(
+ borderRadius: widget.pickerAreaBorderRadius,
+ child: Padding(
+ padding:
+ EdgeInsets.all(widget.paletteType == PaletteType.hueWheel ? 10 : 0),
+ child: ColorPickerArea(
+ currentHsvColor, onColorChanging, widget.paletteType),
+ ),
+ );
+ }
+
+ Widget sliderByPaletteType() {
+ switch (widget.paletteType) {
+ case PaletteType.hsv:
+ case PaletteType.hsvWithHue:
+ case PaletteType.hsl:
+ case PaletteType.hslWithHue:
+ return colorPickerSlider(TrackType.hue);
+ case PaletteType.hsvWithValue:
+ case PaletteType.hueWheel:
+ return colorPickerSlider(TrackType.value);
+ case PaletteType.hsvWithSaturation:
+ return colorPickerSlider(TrackType.saturation);
+ case PaletteType.hslWithLightness:
+ return colorPickerSlider(TrackType.lightness);
+ case PaletteType.hslWithSaturation:
+ return colorPickerSlider(TrackType.saturationForHSL);
+ case PaletteType.rgbWithBlue:
+ return colorPickerSlider(TrackType.blue);
+ case PaletteType.rgbWithGreen:
+ return colorPickerSlider(TrackType.green);
+ case PaletteType.rgbWithRed:
+ return colorPickerSlider(TrackType.red);
+ default:
+ return const SizedBox();
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ if (MediaQuery.of(context).orientation == Orientation.portrait ||
+ widget.portraitOnly) {
+ return Column(
+ children: [
+ SizedBox(
+ width: widget.colorPickerWidth,
+ height: widget.colorPickerWidth * widget.pickerAreaHeightPercent,
+ child: colorPicker(),
+ ),
+ Padding(
+ padding: const EdgeInsets.fromLTRB(15.0, 5.0, 10.0, 5.0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ GestureDetector(
+ onTap: () => setState(() {
+ if (widget.onHistoryChanged != null &&
+ !colorHistory.contains(currentHsvColor.toColor())) {
+ colorHistory.add(currentHsvColor.toColor());
+ widget.onHistoryChanged!(colorHistory);
+ }
+ }),
+ child: ColorIndicator(currentHsvColor),
+ ),
+ Expanded(
+ child: Column(
+ children: [
+ SizedBox(
+ height: 40.0,
+ width: widget.colorPickerWidth - 75.0,
+ child: sliderByPaletteType()),
+ if (widget.enableAlpha)
+ SizedBox(
+ height: 40.0,
+ width: widget.colorPickerWidth - 75.0,
+ child: colorPickerSlider(TrackType.alpha),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ if (colorHistory.isNotEmpty)
+ SizedBox(
+ width: widget.colorPickerWidth,
+ height: 50,
+ child:
+ ListView(scrollDirection: Axis.horizontal, children: [
+ for (Color color in colorHistory)
+ Padding(
+ key: Key(color.hashCode.toString()),
+ padding: const EdgeInsets.fromLTRB(15, 0, 0, 10),
+ child: Center(
+ child: GestureDetector(
+ onTap: () => onColorChanging(HSVColor.fromColor(color)),
+ child: ColorIndicator(HSVColor.fromColor(color),
+ width: 30, height: 30),
+ ),
+ ),
+ ),
+ const SizedBox(width: 15),
+ ]),
+ ),
+ if (widget.showLabel && widget.labelTypes.isNotEmpty)
+ FittedBox(
+ child: ColorPickerLabel(
+ currentHsvColor,
+ enableAlpha: widget.enableAlpha,
+ textStyle: widget.labelTextStyle,
+ colorLabelTypes: widget.labelTypes,
+ ),
+ ),
+ if (widget.hexInputBar)
+ ColorPickerInput(
+ currentHsvColor.toColor(),
+ (Color color) {
+ setState(() => currentHsvColor = HSVColor.fromColor(color));
+ widget.onColorChanged(currentHsvColor.toColor());
+ if (widget.onHsvColorChanged != null)
+ widget.onHsvColorChanged!(currentHsvColor);
+ },
+ enableAlpha: widget.enableAlpha,
+ embeddedText: false,
+ ),
+ const SizedBox(height: 20.0),
+ ],
+ );
+ } else {
+ return Row(
+ children: [
+ SizedBox(
+ width: widget.colorPickerWidth,
+ height: widget.colorPickerWidth * widget.pickerAreaHeightPercent,
+ child: colorPicker()),
+ Column(
+ children: [
+ Row(
+ children: [
+ const SizedBox(width: 20.0),
+ GestureDetector(
+ onTap: () => setState(() {
+ if (widget.onHistoryChanged != null &&
+ !colorHistory.contains(currentHsvColor.toColor())) {
+ colorHistory.add(currentHsvColor.toColor());
+ widget.onHistoryChanged!(colorHistory);
+ }
+ }),
+ child: ColorIndicator(currentHsvColor),
+ ),
+ Column(
+ children: [
+ SizedBox(
+ height: 40.0,
+ width: 260.0,
+ child: sliderByPaletteType()),
+ if (widget.enableAlpha)
+ SizedBox(
+ height: 40.0,
+ width: 260.0,
+ child: colorPickerSlider(TrackType.alpha)),
+ ],
+ ),
+ const SizedBox(width: 10.0),
+ ],
+ ),
+ if (colorHistory.isNotEmpty)
+ SizedBox(
+ width: widget.colorPickerWidth,
+ height: 50,
+ child: ListView(
+ scrollDirection: Axis.horizontal,
+ children: [
+ for (Color color in colorHistory)
+ Padding(
+ key: Key(color.hashCode.toString()),
+ padding: const EdgeInsets.fromLTRB(15, 18, 0, 0),
+ child: Center(
+ child: GestureDetector(
+ onTap: () =>
+ onColorChanging(HSVColor.fromColor(color)),
+ onLongPress: () {
+ if (colorHistory.remove(color)) {
+ widget.onHistoryChanged!(colorHistory);
+ setState(() {});
+ }
+ },
+ child: ColorIndicator(HSVColor.fromColor(color),
+ width: 30, height: 30),
+ ),
+ ),
+ ),
+ const SizedBox(width: 15),
+ ]),
+ ),
+ const SizedBox(height: 20.0),
+ if (widget.showLabel && widget.labelTypes.isNotEmpty)
+ FittedBox(
+ child: ColorPickerLabel(
+ currentHsvColor,
+ enableAlpha: widget.enableAlpha,
+ textStyle: widget.labelTextStyle,
+ colorLabelTypes: widget.labelTypes,
+ ),
+ ),
+ if (widget.hexInputBar)
+ ColorPickerInput(
+ currentHsvColor.toColor(),
+ (Color color) {
+ setState(() => currentHsvColor = HSVColor.fromColor(color));
+ widget.onColorChanged(currentHsvColor.toColor());
+ if (widget.onHsvColorChanged != null)
+ widget.onHsvColorChanged!(currentHsvColor);
+ },
+ enableAlpha: widget.enableAlpha,
+ embeddedText: false,
+ ),
+ const SizedBox(height: 5),
+ ],
+ ),
+ ],
+ );
+ }
+ }
+}
+
+/// The Color Picker with sliders only. Support HSV, HSL and RGB color model.
+class SlidePicker extends StatefulWidget {
+ const SlidePicker({
+ Key? key,
+ required this.pickerColor,
+ required this.onColorChanged,
+ this.colorModel = ColorModel.rgb,
+ this.enableAlpha = true,
+ this.sliderSize = const Size(260, 40),
+ this.showSliderText = true,
+ @Deprecated(
+ 'Use Theme.of(context).textTheme.bodyText1 & 2 to alter text style.')
+ this.sliderTextStyle,
+ this.showParams = true,
+ @Deprecated('Use empty list in [labelTypes] to disable label.')
+ this.showLabel = true,
+ this.labelTypes = const [],
+ @Deprecated(
+ 'Use Theme.of(context).textTheme.bodyText1 & 2 to alter text style.')
+ this.labelTextStyle,
+ this.showIndicator = true,
+ this.indicatorSize = const Size(280, 50),
+ this.indicatorAlignmentBegin = const Alignment(-1.0, -3.0),
+ this.indicatorAlignmentEnd = const Alignment(1.0, 3.0),
+ this.displayThumbColor = true,
+ this.indicatorBorderRadius = const BorderRadius.all(Radius.zero),
+ }) : super(key: key);
+
+ final Color pickerColor;
+ final ValueChanged onColorChanged;
+ final ColorModel colorModel;
+ final bool enableAlpha;
+ final Size sliderSize;
+ final bool showSliderText;
+ final TextStyle? sliderTextStyle;
+ final bool showLabel;
+ final bool showParams;
+ final List labelTypes;
+ final TextStyle? labelTextStyle;
+ final bool showIndicator;
+ final Size indicatorSize;
+ final AlignmentGeometry indicatorAlignmentBegin;
+ final AlignmentGeometry indicatorAlignmentEnd;
+ final bool displayThumbColor;
+ final BorderRadius indicatorBorderRadius;
+
+ @override
+ State createState() => _SlidePickerState();
+}
+
+class _SlidePickerState extends State {
+ HSVColor currentHsvColor = const HSVColor.fromAHSV(0.0, 0.0, 0.0, 0.0);
+
+ @override
+ void initState() {
+ super.initState();
+ currentHsvColor = HSVColor.fromColor(widget.pickerColor);
+ }
+
+ @override
+ void didUpdateWidget(SlidePicker oldWidget) {
+ super.didUpdateWidget(oldWidget);
+ currentHsvColor = HSVColor.fromColor(widget.pickerColor);
+ }
+
+ Widget colorPickerSlider(TrackType trackType) {
+ return ColorPickerSlider(
+ trackType,
+ currentHsvColor,
+ (HSVColor color) {
+ setState(() => currentHsvColor = color);
+ widget.onColorChanged(currentHsvColor.toColor());
+ },
+ displayThumbColor: widget.displayThumbColor,
+ fullThumbColor: true,
+ );
+ }
+
+ Widget indicator() {
+ return ClipRRect(
+ borderRadius: widget.indicatorBorderRadius,
+ clipBehavior: Clip.antiAliasWithSaveLayer,
+ child: GestureDetector(
+ onTap: () {
+ setState(
+ () => currentHsvColor = HSVColor.fromColor(widget.pickerColor));
+ widget.onColorChanged(currentHsvColor.toColor());
+ },
+ child: Container(
+ width: widget.indicatorSize.width,
+ height: widget.indicatorSize.height,
+ margin: const EdgeInsets.only(bottom: 15.0),
+ foregroundDecoration: BoxDecoration(
+ gradient: LinearGradient(
+ colors: [
+ widget.pickerColor,
+ widget.pickerColor,
+ currentHsvColor.toColor(),
+ currentHsvColor.toColor(),
+ ],
+ begin: widget.indicatorAlignmentBegin,
+ end: widget.indicatorAlignmentEnd,
+ stops: const [0.0, 0.5, 0.5, 1.0],
+ ),
+ ),
+ child: const CustomPaint(painter: CheckerPainter()),
+ ),
+ ),
+ );
+ }
+
+ String getColorParams(int pos) {
+ assert(pos >= 0 && pos < 4);
+ if (widget.colorModel == ColorModel.rgb) {
+ final Color color = currentHsvColor.toColor();
+ return [
+ color.red.toString(),
+ color.green.toString(),
+ color.blue.toString(),
+ '${(color.opacity * 100).round()}',
+ ][pos];
+ } else if (widget.colorModel == ColorModel.hsv) {
+ return [
+ currentHsvColor.hue.round().toString(),
+ (currentHsvColor.saturation * 100).round().toString(),
+ (currentHsvColor.value * 100).round().toString(),
+ (currentHsvColor.alpha * 100).round().toString(),
+ ][pos];
+ } else if (widget.colorModel == ColorModel.hsl) {
+ HSLColor hslColor = hsvToHsl(currentHsvColor);
+ return [
+ hslColor.hue.round().toString(),
+ (hslColor.saturation * 100).round().toString(),
+ (hslColor.lightness * 100).round().toString(),
+ (currentHsvColor.alpha * 100).round().toString(),
+ ][pos];
+ } else {
+ return '??';
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ double fontSize = 14;
+ if (widget.labelTextStyle != null &&
+ widget.labelTextStyle?.fontSize != null) {
+ fontSize = widget.labelTextStyle?.fontSize ?? 14;
+ }
+ final List trackTypes = [
+ if (widget.colorModel == ColorModel.hsv) ...[
+ TrackType.hue,
+ TrackType.saturation,
+ TrackType.value
+ ],
+ if (widget.colorModel == ColorModel.hsl) ...[
+ TrackType.hue,
+ TrackType.saturationForHSL,
+ TrackType.lightness
+ ],
+ if (widget.colorModel == ColorModel.rgb) ...[
+ TrackType.red,
+ TrackType.green,
+ TrackType.blue
+ ],
+ if (widget.enableAlpha) ...[TrackType.alpha],
+ ];
+ List sliders = [
+ for (TrackType trackType in trackTypes)
+ SizedBox(
+ width: widget.sliderSize.width,
+ height: widget.sliderSize.height,
+ child: Row(
+ children: [
+ if (widget.showSliderText)
+ Padding(
+ padding: const EdgeInsets.symmetric(horizontal: 10.0),
+ child: Text(
+ trackType.toString().split('.').last[0].toUpperCase(),
+ style: widget.sliderTextStyle ??
+ Theme.of(context).textTheme.bodyLarge,
+ ),
+ ),
+ Expanded(child: colorPickerSlider(trackType)),
+ if (widget.showParams)
+ ConstrainedBox(
+ constraints: BoxConstraints(minWidth: fontSize * 2 + 5),
+ child: Text(
+ getColorParams(trackTypes.indexOf(trackType)),
+ style: widget.sliderTextStyle ??
+ Theme.of(context).textTheme.bodyMedium,
+ textAlign: TextAlign.right,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ];
+
+ return Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.center,
+ children: [
+ if (widget.showIndicator) indicator(),
+ if (!widget.showIndicator) const SizedBox(height: 20),
+ ...sliders,
+ const SizedBox(height: 20.0),
+ if (widget.showLabel && widget.labelTypes.isNotEmpty)
+ Padding(
+ padding: const EdgeInsets.only(bottom: 20.0),
+ child: ColorPickerLabel(
+ currentHsvColor,
+ enableAlpha: widget.enableAlpha,
+ textStyle: widget.labelTextStyle,
+ colorLabelTypes: widget.labelTypes,
+ ),
+ ),
+ ],
+ );
+ }
+}
+
+/// The Color Picker with HUE Ring & HSV model.
+class HueRingPicker extends StatefulWidget {
+ const HueRingPicker({
+ Key? key,
+ required this.pickerColor,
+ required this.onColorChanged,
+ this.portraitOnly = false,
+ this.colorPickerHeight = 250.0,
+ this.hueRingStrokeWidth = 20.0,
+ this.enableAlpha = false,
+ this.displayThumbColor = true,
+ this.pickerAreaBorderRadius = const BorderRadius.all(Radius.zero),
+ }) : super(key: key);
+
+ final Color pickerColor;
+ final ValueChanged onColorChanged;
+ final bool portraitOnly;
+ final double colorPickerHeight;
+ final double hueRingStrokeWidth;
+ final bool enableAlpha;
+ final bool displayThumbColor;
+ final BorderRadius pickerAreaBorderRadius;
+
+ @override
+ _HueRingPickerState createState() => _HueRingPickerState();
+}
+
+class _HueRingPickerState extends State {
+ HSVColor currentHsvColor = const HSVColor.fromAHSV(0.0, 0.0, 0.0, 0.0);
+
+ @override
+ void initState() {
+ currentHsvColor = HSVColor.fromColor(widget.pickerColor);
+ super.initState();
+ }
+
+ @override
+ void didUpdateWidget(HueRingPicker oldWidget) {
+ super.didUpdateWidget(oldWidget);
+ currentHsvColor = HSVColor.fromColor(widget.pickerColor);
+ }
+
+ void onColorChanging(HSVColor color) {
+ setState(() => currentHsvColor = color);
+ widget.onColorChanged(currentHsvColor.toColor());
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ if (MediaQuery.of(context).orientation == Orientation.portrait ||
+ widget.portraitOnly) {
+ return Column(
+ children: [
+ ClipRRect(
+ borderRadius: widget.pickerAreaBorderRadius,
+ child: Padding(
+ padding: const EdgeInsets.all(15),
+ child: Stack(
+ alignment: AlignmentDirectional.center,
+ children: [
+ SizedBox(
+ width: widget.colorPickerHeight,
+ height: widget.colorPickerHeight,
+ child: ColorPickerHueRing(
+ currentHsvColor,
+ onColorChanging,
+ displayThumbColor: widget.displayThumbColor,
+ strokeWidth: widget.hueRingStrokeWidth,
+ ),
+ ),
+ SizedBox(
+ width: widget.colorPickerHeight / 1.6,
+ height: widget.colorPickerHeight / 1.6,
+ child: ColorPickerArea(
+ currentHsvColor, onColorChanging, PaletteType.hsv),
+ )
+ ]),
+ ),
+ ),
+ if (widget.enableAlpha)
+ SizedBox(
+ height: 40.0,
+ width: widget.colorPickerHeight,
+ child: ColorPickerSlider(
+ TrackType.alpha,
+ currentHsvColor,
+ onColorChanging,
+ displayThumbColor: widget.displayThumbColor,
+ ),
+ ),
+ Padding(
+ padding: const EdgeInsets.fromLTRB(15.0, 5.0, 10.0, 5.0),
+ child: Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const SizedBox(width: 10),
+ ColorIndicator(currentHsvColor),
+ Expanded(
+ child: Padding(
+ padding: const EdgeInsets.fromLTRB(0, 5, 0, 20),
+ child: ColorPickerInput(
+ currentHsvColor.toColor(),
+ (Color color) {
+ setState(
+ () => currentHsvColor = HSVColor.fromColor(color));
+ widget.onColorChanged(currentHsvColor.toColor());
+ },
+ enableAlpha: widget.enableAlpha,
+ embeddedText: true,
+ ),
+ ),
+ ),
+ ],
+ ),
+ ),
+ ],
+ );
+ } else {
+ return Row(
+ children: [
+ Expanded(
+ child: SizedBox(
+ width: 300.0,
+ height: widget.colorPickerHeight,
+ child: ClipRRect(
+ borderRadius: widget.pickerAreaBorderRadius,
+ child: ColorPickerArea(
+ currentHsvColor, onColorChanging, PaletteType.hsv),
+ ),
+ ),
+ ),
+ ClipRRect(
+ borderRadius: widget.pickerAreaBorderRadius,
+ child: Padding(
+ padding: const EdgeInsets.all(15),
+ child: Stack(
+ alignment: AlignmentDirectional.topCenter,
+ children: [
+ SizedBox(
+ width: widget.colorPickerHeight -
+ widget.hueRingStrokeWidth * 2,
+ height: widget.colorPickerHeight -
+ widget.hueRingStrokeWidth * 2,
+ child: ColorPickerHueRing(
+ currentHsvColor, onColorChanging,
+ strokeWidth: widget.hueRingStrokeWidth),
+ ),
+ Column(
+ children: [
+ SizedBox(height: widget.colorPickerHeight / 8.5),
+ ColorIndicator(currentHsvColor),
+ const SizedBox(height: 10),
+ ColorPickerInput(
+ currentHsvColor.toColor(),
+ (Color color) {
+ setState(() =>
+ currentHsvColor = HSVColor.fromColor(color));
+ widget.onColorChanged(currentHsvColor.toColor());
+ },
+ enableAlpha: widget.enableAlpha,
+ embeddedText: true,
+ disable: true,
+ ),
+ if (widget.enableAlpha) const SizedBox(height: 5),
+ if (widget.enableAlpha)
+ SizedBox(
+ height: 40.0,
+ width: (widget.colorPickerHeight -
+ widget.hueRingStrokeWidth * 2) /
+ 2,
+ child: ColorPickerSlider(
+ TrackType.alpha,
+ currentHsvColor,
+ onColorChanging,
+ displayThumbColor: true,
+ ),
+ ),
+ ],
+ ),
+ ]),
+ ),
+ ),
+ ],
+ );
+ }
+ }
+}
diff --git a/lib/src/packages/flutter_colorpicker/src/colors.dart b/lib/src/packages/flutter_colorpicker/src/colors.dart
new file mode 100644
index 00000000..041a1f20
--- /dev/null
+++ b/lib/src/packages/flutter_colorpicker/src/colors.dart
@@ -0,0 +1,172 @@
+// ignore_for_file: type=lint
+
+import 'dart:ui';
+
+/// X11 Colors
+///
+/// https://en.wikipedia.org/wiki/X11_color_names
+
+const Map x11Colors = {
+ 'aliceblue': Color(0xfff0f8ff),
+ 'antiquewhite': Color(0xfffaebd7),
+ 'aqua': Color(0xff00ffff),
+ 'aquamarine': Color(0xff7fffd4),
+ 'azure': Color(0xfff0ffff),
+ 'beige': Color(0xfff5f5dc),
+ 'bisque': Color(0xffffe4c4),
+ 'black': Color(0xff000000),
+ 'blanchedalmond': Color(0xffffebcd),
+ 'blue': Color(0xff0000ff),
+ 'blueviolet': Color(0xff8a2be2),
+ 'brown': Color(0xffa52a2a),
+ 'burlywood': Color(0xffdeb887),
+ 'cadetblue': Color(0xff5f9ea0),
+ 'chartreuse': Color(0xff7fff00),
+ 'chocolate': Color(0xffd2691e),
+ 'coral': Color(0xffff7f50),
+ 'cornflower': Color(0xff6495ed),
+ 'cornflowerblue': Color(0xff6495ed),
+ 'cornsilk': Color(0xfffff8dc),
+ 'crimson': Color(0xffdc143c),
+ 'cyan': Color(0xff00ffff),
+ 'darkblue': Color(0xff00008b),
+ 'darkcyan': Color(0xff008b8b),
+ 'darkgoldenrod': Color(0xffb8860b),
+ 'darkgray': Color(0xffa9a9a9),
+ 'darkgreen': Color(0xff006400),
+ 'darkgrey': Color(0xffa9a9a9),
+ 'darkkhaki': Color(0xffbdb76b),
+ 'darkmagenta': Color(0xff8b008b),
+ 'darkolivegreen': Color(0xff556b2f),
+ 'darkorange': Color(0xffff8c00),
+ 'darkorchid': Color(0xff9932cc),
+ 'darkred': Color(0xff8b0000),
+ 'darksalmon': Color(0xffe9967a),
+ 'darkseagreen': Color(0xff8fbc8f),
+ 'darkslateblue': Color(0xff483d8b),
+ 'darkslategray': Color(0xff2f4f4f),
+ 'darkslategrey': Color(0xff2f4f4f),
+ 'darkturquoise': Color(0xff00ced1),
+ 'darkviolet': Color(0xff9400d3),
+ 'deeppink': Color(0xffff1493),
+ 'deepskyblue': Color(0xff00bfff),
+ 'dimgray': Color(0xff696969),
+ 'dimgrey': Color(0xff696969),
+ 'dodgerblue': Color(0xff1e90ff),
+ 'firebrick': Color(0xffb22222),
+ 'floralwhite': Color(0xfffffaf0),
+ 'forestgreen': Color(0xff228b22),
+ 'fuchsia': Color(0xffff00ff),
+ 'gainsboro': Color(0xffdcdcdc),
+ 'ghostwhite': Color(0xfff8f8ff),
+ 'gold': Color(0xffffd700),
+ 'goldenrod': Color(0xffdaa520),
+ 'gray': Color(0xff808080),
+ 'green': Color(0xff008000),
+ 'greenyellow': Color(0xffadff2f),
+ 'grey': Color(0xff808080),
+ 'honeydew': Color(0xfff0fff0),
+ 'hotpink': Color(0xffff69b4),
+ 'indianred': Color(0xffcd5c5c),
+ 'indigo': Color(0xff4b0082),
+ 'ivory': Color(0xfffffff0),
+ 'khaki': Color(0xfff0e68c),
+ 'laserlemon': Color(0xffffff54),
+ 'lavender': Color(0xffe6e6fa),
+ 'lavenderblush': Color(0xfffff0f5),
+ 'lawngreen': Color(0xff7cfc00),
+ 'lemonchiffon': Color(0xfffffacd),
+ 'lightblue': Color(0xffadd8e6),
+ 'lightcoral': Color(0xfff08080),
+ 'lightcyan': Color(0xffe0ffff),
+ 'lightgoldenrod': Color(0xfffafad2),
+ 'lightgoldenrodyellow': Color(0xfffafad2),
+ 'lightgray': Color(0xffd3d3d3),
+ 'lightgreen': Color(0xff90ee90),
+ 'lightgrey': Color(0xffd3d3d3),
+ 'lightpink': Color(0xffffb6c1),
+ 'lightsalmon': Color(0xffffa07a),
+ 'lightseagreen': Color(0xff20b2aa),
+ 'lightskyblue': Color(0xff87cefa),
+ 'lightslategray': Color(0xff778899),
+ 'lightslategrey': Color(0xff778899),
+ 'lightsteelblue': Color(0xffb0c4de),
+ 'lightyellow': Color(0xffffffe0),
+ 'lime': Color(0xff00ff00),
+ 'limegreen': Color(0xff32cd32),
+ 'linen': Color(0xfffaf0e6),
+ 'magenta': Color(0xffff00ff),
+ 'maroon': Color(0xff800000),
+ 'maroon2': Color(0xff7f0000),
+ 'maroon3': Color(0xffb03060),
+ 'mediumaquamarine': Color(0xff66cdaa),
+ 'mediumblue': Color(0xff0000cd),
+ 'mediumorchid': Color(0xffba55d3),
+ 'mediumpurple': Color(0xff9370db),
+ 'mediumseagreen': Color(0xff3cb371),
+ 'mediumslateblue': Color(0xff7b68ee),
+ 'mediumspringgreen': Color(0xff00fa9a),
+ 'mediumturquoise': Color(0xff48d1cc),
+ 'mediumvioletred': Color(0xffc71585),
+ 'midnightblue': Color(0xff191970),
+ 'mintcream': Color(0xfff5fffa),
+ 'mistyrose': Color(0xffffe4e1),
+ 'moccasin': Color(0xffffe4b5),
+ 'navajowhite': Color(0xffffdead),
+ 'navy': Color(0xff000080),
+ 'oldlace': Color(0xfffdf5e6),
+ 'olive': Color(0xff808000),
+ 'olivedrab': Color(0xff6b8e23),
+ 'orange': Color(0xffffa500),
+ 'orangered': Color(0xffff4500),
+ 'orchid': Color(0xffda70d6),
+ 'palegoldenrod': Color(0xffeee8aa),
+ 'palegreen': Color(0xff98fb98),
+ 'paleturquoise': Color(0xffafeeee),
+ 'palevioletred': Color(0xffdb7093),
+ 'papayawhip': Color(0xffffefd5),
+ 'peachpuff': Color(0xffffdab9),
+ 'peru': Color(0xffcd853f),
+ 'pink': Color(0xffffc0cb),
+ 'plum': Color(0xffdda0dd),
+ 'powderblue': Color(0xffb0e0e6),
+ 'purple': Color(0xff800080),
+ 'purple2': Color(0xff7f007f),
+ 'purple3': Color(0xffa020f0),
+ 'rebeccapurple': Color(0xff663399),
+ 'red': Color(0xffff0000),
+ 'rosybrown': Color(0xffbc8f8f),
+ 'royalblue': Color(0xff4169e1),
+ 'saddlebrown': Color(0xff8b4513),
+ 'salmon': Color(0xfffa8072),
+ 'sandybrown': Color(0xfff4a460),
+ 'seagreen': Color(0xff2e8b57),
+ 'seashell': Color(0xfffff5ee),
+ 'sienna': Color(0xffa0522d),
+ 'silver': Color(0xffc0c0c0),
+ 'skyblue': Color(0xff87ceeb),
+ 'slateblue': Color(0xff6a5acd),
+ 'slategray': Color(0xff708090),
+ 'slategrey': Color(0xff708090),
+ 'snow': Color(0xfffffafa),
+ 'springgreen': Color(0xff00ff7f),
+ 'steelblue': Color(0xff4682b4),
+ 'tan': Color(0xffd2b48c),
+ 'teal': Color(0xff008080),
+ 'thistle': Color(0xffd8bfd8),
+ 'tomato': Color(0xffff6347),
+ 'turquoise': Color(0xff40e0d0),
+ 'violet': Color(0xffee82ee),
+ 'wheat': Color(0xfff5deb3),
+ 'white': Color(0xffffffff),
+ 'whitesmoke': Color(0xfff5f5f5),
+ 'yellow': Color(0xffffff00),
+ 'yellowgreen': Color(0xff9acd32),
+};
+
+Color? colorFromName(String val) =>
+ x11Colors[val.trim().replaceAll(' ', '').toLowerCase()];
+
+extension ColorExtension on String {
+ Color? toColor() => colorFromName(this);
+}
diff --git a/lib/src/packages/flutter_colorpicker/src/material_picker.dart b/lib/src/packages/flutter_colorpicker/src/material_picker.dart
new file mode 100644
index 00000000..b9d5d8c2
--- /dev/null
+++ b/lib/src/packages/flutter_colorpicker/src/material_picker.dart
@@ -0,0 +1,384 @@
+// ignore_for_file: type=lint
+
+/// Material Color Picker
+
+library material_colorpicker;
+
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'utils.dart';
+
+// The Color Picker which contains Material Design Color Palette.
+class MaterialPicker extends StatefulWidget {
+ const MaterialPicker({
+ Key? key,
+ required this.pickerColor,
+ required this.onColorChanged,
+ this.onPrimaryChanged,
+ this.enableLabel = false,
+ this.portraitOnly = false,
+ }) : super(key: key);
+
+ final Color pickerColor;
+ final ValueChanged onColorChanged;
+ final ValueChanged? onPrimaryChanged;
+ final bool enableLabel;
+ final bool portraitOnly;
+
+ @override
+ State createState() => _MaterialPickerState();
+}
+
+class _MaterialPickerState extends State {
+ final List> _colorTypes = [
+ [Colors.red, Colors.redAccent],
+ [Colors.pink, Colors.pinkAccent],
+ [Colors.purple, Colors.purpleAccent],
+ [Colors.deepPurple, Colors.deepPurpleAccent],
+ [Colors.indigo, Colors.indigoAccent],
+ [Colors.blue, Colors.blueAccent],
+ [Colors.lightBlue, Colors.lightBlueAccent],
+ [Colors.cyan, Colors.cyanAccent],
+ [Colors.teal, Colors.tealAccent],
+ [Colors.green, Colors.greenAccent],
+ [Colors.lightGreen, Colors.lightGreenAccent],
+ [Colors.lime, Colors.limeAccent],
+ [Colors.yellow, Colors.yellowAccent],
+ [Colors.amber, Colors.amberAccent],
+ [Colors.orange, Colors.orangeAccent],
+ [Colors.deepOrange, Colors.deepOrangeAccent],
+ [Colors.brown],
+ [Colors.grey],
+ [Colors.blueGrey],
+ [Colors.black],
+ ];
+
+ List _currentColorType = [Colors.red, Colors.redAccent];
+ Color _currentShading = Colors.transparent;
+
+ List