moved non-premium features from premium folder

This commit is contained in:
Kima
2023-10-21 21:11:39 +02:00
parent 774f63aa89
commit 316039463f
25 changed files with 90 additions and 146 deletions

View File

@@ -14,7 +14,7 @@ import 'package:filcnaplo/theme/theme.dart';
import 'package:filcnaplo_kreta_api/client/client.dart';
import 'package:filcnaplo_kreta_api/providers/grade_provider.dart';
import 'package:filcnaplo_premium/providers/goal_provider.dart';
import 'package:filcnaplo_premium/providers/share_provider.dart';
import 'package:filcnaplo_kreta_api/providers/share_provider.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

View File

@@ -0,0 +1,137 @@
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
/// Blocky Color Picker
library block_colorpicker;
import 'package:flutter/material.dart';
import 'package:filcnaplo/theme/colors/accent.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<Color> 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<Color> _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<Color> 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 {
BlockPicker({
Key? key,
required this.pickerColor,
required this.onColorChanged,
this.useInShowDialog = true,
this.layoutBuilder = _defaultLayoutBuilder,
this.itemBuilder = _defaultItemBuilder,
}) : super(key: key);
final Color? pickerColor;
final ValueChanged<Color> onColorChanged;
final List<Color> availableColors = accentColorMap.values.toList();
final bool useInShowDialog;
final PickerLayoutBuilder layoutBuilder;
final PickerItemBuilder itemBuilder;
@override
State<StatefulWidget> createState() => _BlockPickerState();
}
class _BlockPickerState extends State<BlockPicker> {
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),
),
);
}
}

View File

@@ -0,0 +1,468 @@
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
/// HSV(HSB)/HSL Color Picker example
///
/// You can create your own layout by importing `picker.dart`.
library hsv_picker;
import 'package:filcnaplo/models/shared_theme.dart';
import 'package:filcnaplo_mobile_ui/common/custom_snack_bar.dart';
import 'package:filcnaplo_kreta_api/providers/share_provider.dart';
import 'package:filcnaplo/ui/flutter_colorpicker/block_picker.dart';
import 'package:filcnaplo/ui/flutter_colorpicker/palette.dart';
import 'package:filcnaplo/ui/flutter_colorpicker/utils.dart';
import 'package:filcnaplo_mobile_ui/screens/settings/theme_screen.dart';
import 'package:filcnaplo_mobile_ui/screens/settings/theme_screen.i18n.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:provider/provider.dart';
class FilcColorPicker extends StatefulWidget {
const FilcColorPicker({
Key? key,
required this.colorMode,
required this.pickerColor,
required this.onColorChanged,
required this.onColorChangeEnd,
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,
required this.onThemeIdProvided,
}) : super(key: key);
final CustomColorMode colorMode;
final Color pickerColor;
final ValueChanged<Color> onColorChanged;
final void Function(Color color, {bool? adaptive}) onColorChangeEnd;
final HSVColor? pickerHsvColor;
final ValueChanged<HSVColor>? onHsvColorChanged;
final PaletteType paletteType;
final bool enableAlpha;
final bool showLabel;
final List<ColorLabelType> labelTypes;
final TextStyle? labelTextStyle;
final bool displayThumbColor;
final bool portraitOnly;
final double colorPickerWidth;
final double pickerAreaHeightPercent;
final BorderRadius pickerAreaBorderRadius;
final bool hexInputBar;
final TextEditingController? hexInputController;
final List<Color>? colorHistory;
final ValueChanged<List<Color>>? onHistoryChanged;
final void Function(SharedTheme theme) onThemeIdProvided;
@override
_FilcColorPickerState createState() => _FilcColorPickerState();
}
class _FilcColorPickerState extends State<FilcColorPicker> {
final idController = TextEditingController();
late final ShareProvider shareProvider;
HSVColor currentHsvColor = const HSVColor.fromAHSV(0.0, 0.0, 0.0, 0.0);
List<Color> colorHistory = [];
bool isAdvancedView = false;
@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 ?? [];
}
shareProvider = Provider.of<ShareProvider>(context, listen: false);
super.initState();
}
@override
void didUpdateWidget(FilcColorPicker 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);
}
},
() => widget.onColorChangeEnd(currentHsvColor.toColor()),
(p) {
ScaffoldMessenger.of(context).clearSnackBars();
ScaffoldMessenger.of(context).showSnackBar(SnackBar(
content: Text(
"Move the ${p == 0 ? 'Saturation (second)' : 'Value (third)'} slider first.",
textAlign: TextAlign.center,
style: TextStyle(
color: AppColors.of(context).text,
fontWeight: FontWeight.w600)),
backgroundColor: AppColors.of(context).background));
},
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);
}
}
@override
Widget build(BuildContext context) {
if (MediaQuery.of(context).orientation == Orientation.portrait ||
widget.portraitOnly) {
return Column(
children: [
if (widget.colorMode != CustomColorMode.theme &&
widget.colorMode != CustomColorMode.enterId)
Padding(
padding: const EdgeInsets.only(top: 8.0),
child: Column(
children: [
Padding(
padding: const EdgeInsets.only(left: 12.0, right: 12.0),
child: SizedBox(
height: 45.0,
width: double.infinity,
child: colorPickerSlider(TrackType.hue),
),
),
Padding(
padding: const EdgeInsets.only(left: 12.0, right: 12.0),
child: SizedBox(
height: 45.0,
width: double.infinity,
child: colorPickerSlider(TrackType.saturation),
),
),
if (isAdvancedView)
Padding(
padding: const EdgeInsets.only(left: 12.0, right: 12.0),
child: SizedBox(
height: 45.0,
width: double.infinity,
child: colorPickerSlider(TrackType.value),
),
),
],
),
),
if (isAdvancedView &&
widget.colorMode != CustomColorMode.theme &&
widget.colorMode != CustomColorMode.enterId)
Padding(
padding: const EdgeInsets.only(bottom: 6.0),
child: ColorPickerInput(
currentHsvColor.toColor(),
(Color color) {
setState(() => currentHsvColor = HSVColor.fromColor(color));
widget.onColorChanged(currentHsvColor.toColor());
if (widget.onHsvColorChanged != null) {
widget.onHsvColorChanged!(currentHsvColor);
}
},
enableAlpha: false,
embeddedText: false,
),
),
if (widget.colorMode == CustomColorMode.enterId)
Padding(
padding:
const EdgeInsets.symmetric(vertical: 10.0, horizontal: 10.0),
child: Column(
children: [
TextField(
autocorrect: false,
autofocus: true,
onEditingComplete: () async {
SharedTheme? theme = await shareProvider.getThemeById(
context,
id: idController.text.replaceAll(' ', ''),
);
if (theme != null) {
widget.onThemeIdProvided(theme);
idController.clear();
} else {
ScaffoldMessenger.of(context).showSnackBar(
CustomSnackBar(
content: Text("theme_not_found".i18n,
style: const TextStyle(color: Colors.white)),
backgroundColor: AppColors.of(context).red,
context: context,
),
);
}
},
controller: idController,
decoration: InputDecoration(
hintText: 'theme_id'.i18n,
),
),
// MaterialActionButton(
// child: Row(
// mainAxisSize: MainAxisSize.min,
// children: [
// Text('check_id'.i18n),
// ],
// ),
// backgroundColor: AppColors.of(context).filc,
// onPressed: () {},
// ),
],
),
),
if (widget.colorMode != CustomColorMode.enterId)
SizedBox(
height: 70 * (widget.colorMode == CustomColorMode.theme ? 2 : 1),
child: BlockPicker(
pickerColor: Colors.red,
layoutBuilder: (context, colors, child) {
return GridView.count(
shrinkWrap: true,
crossAxisCount:
widget.colorMode == CustomColorMode.theme ? 2 : 1,
scrollDirection: Axis.horizontal,
crossAxisSpacing: 15,
physics: const BouncingScrollPhysics(),
mainAxisSpacing: 15,
padding: const EdgeInsets.symmetric(
horizontal: 12.0, vertical: 8.0),
children: List.generate(
colors.toSet().length +
(widget.colorMode == CustomColorMode.theme ? 1 : 0),
(index) {
if (widget.colorMode == CustomColorMode.theme) {
if (index == 0) {
return GestureDetector(
onTap: () => widget.onColorChangeEnd(
Colors.transparent,
adaptive: true),
child: ColorIndicator(
HSVColor.fromColor(
const Color.fromARGB(255, 255, 238, 177)),
icon: CupertinoIcons.wand_stars,
currentHsvColor: currentHsvColor,
width: 30,
height: 30,
adaptive: true),
);
}
index--;
}
return GestureDetector(
onTap: () => widget.onColorChangeEnd(colors[index]),
child: ColorIndicator(HSVColor.fromColor(colors[index]),
currentHsvColor: currentHsvColor,
width: 30,
height: 30),
);
}),
);
},
onColorChanged: (c) => {},
),
),
if (widget.colorMode != CustomColorMode.theme &&
widget.colorMode != CustomColorMode.enterId)
Material(
color: Colors.transparent,
child: InkWell(
customBorder: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(20),
),
onTap: () => setState(() {
isAdvancedView = !isAdvancedView;
}),
child: Padding(
padding:
const EdgeInsets.only(left: 8.0, right: 8.0, top: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.min,
children: [
Switch(
onChanged: (v) => setState(() => isAdvancedView = v),
value: isAdvancedView,
),
const SizedBox(width: 12.0),
Text(
"advanced".i18n,
style: TextStyle(
fontWeight: FontWeight.w600,
fontSize: 16.0,
color: AppColors.of(context)
.text
.withOpacity(isAdvancedView ? 1.0 : .5),
),
),
],
),
),
),
),
],
);
} else {
return Row(
children: [
//SizedBox(width: widget.colorPickerWidth, height: widget.colorPickerWidth * widget.pickerAreaHeightPercent, child: colorPicker()),
Column(
children: [
Row(
children: <Widget>[
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: <Widget>[
//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: <Widget>[
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.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),
],
),
],
);
}
}
}

View File

@@ -0,0 +1,174 @@
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
import 'dart:ui';
/// X11 Colors
///
/// https://en.wikipedia.org/wiki/X11_color_names
const Map<String, Color> 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);
}

View File

@@ -0,0 +1,785 @@
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
/// The components of HSV Color Picker
///
/// Try to create a Color Picker with other layout on your own :)
import 'package:filcnaplo/models/settings.dart';
import 'package:filcnaplo/theme/colors/accent.dart';
import 'package:filcnaplo/theme/colors/colors.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:provider/provider.dart';
import 'utils.dart';
/// Palette types for color picker area widget.
enum PaletteType {
hsv,
hsvWithHue,
hsvWithValue,
hsvWithSaturation,
hsl,
hslWithHue,
hslWithLightness,
hslWithSaturation,
rgbWithBlue,
rgbWithGreen,
rgbWithRed,
hueWheel,
}
/// Track types for slider picker.
enum TrackType {
hue,
saturation,
saturationForHSL,
value,
lightness,
red,
green,
blue,
alpha,
}
enum FilcTrackType {
hue,
saturation,
value,
}
/// Color information label type.
enum ColorLabelType { hex, rgb, hsv, hsl }
/// Types for slider picker widget.
enum ColorModel { rgb, hsv, hsl }
// enum ColorSpace { rgb, hsv, hsl, hsp, okhsv, okhsl, xyz, yuv, lab, lch, cmyk }
/// Painter for SV mixture.
class HSVWithHueColorPainter extends CustomPainter {
const HSVWithHueColorPainter(this.hsvColor, {this.pointerColor});
final HSVColor hsvColor;
final Color? pointerColor;
@override
void paint(Canvas canvas, Size size) {
final Rect rect = Offset.zero & size;
const Gradient gradientV = LinearGradient(
begin: Alignment.topCenter,
end: Alignment.bottomCenter,
colors: [Colors.white, Colors.black],
);
final Gradient gradientH = LinearGradient(
colors: [
Colors.white,
HSVColor.fromAHSV(1.0, hsvColor.hue, 1.0, 1.0).toColor(),
],
);
canvas.drawRect(rect, Paint()..shader = gradientV.createShader(rect));
canvas.drawRect(
rect,
Paint()
..blendMode = BlendMode.multiply
..shader = gradientH.createShader(rect),
);
canvas.drawCircle(
Offset(size.width * hsvColor.saturation, size.height * (1 - hsvColor.value)),
size.height * 0.04,
Paint()
..color = pointerColor ?? (useWhiteForeground(hsvColor.toColor()) ? Colors.white : Colors.black)
..strokeWidth = 1.5
..style = PaintingStyle.stroke,
);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
class _SliderLayout extends MultiChildLayoutDelegate {
static const String track = 'track';
static const String thumb = 'thumb';
static const String gestureContainer = 'gesturecontainer';
@override
void performLayout(Size size) {
layoutChild(
track,
BoxConstraints.tightFor(
width: size.width + 3,
height: size.height / 1.5,
),
);
positionChild(track, const Offset(-2.0, 0));
layoutChild(
thumb,
const BoxConstraints.tightFor(width: 5.5, height: 10.5),
);
positionChild(thumb, Offset(0.0, (size.height / 1.5) / 2 - 4.5));
layoutChild(
gestureContainer,
BoxConstraints.tightFor(width: size.width, height: size.height),
);
positionChild(gestureContainer, Offset.zero);
}
@override
bool shouldRelayout(_SliderLayout oldDelegate) => false;
}
/// Painter for all kinds of track types.
class TrackPainter extends CustomPainter {
const TrackPainter(this.trackType, this.hsvColor);
final TrackType trackType;
final HSVColor hsvColor;
@override
void paint(Canvas canvas, Size size) {
final Rect rect = Offset.zero & size;
if (trackType == TrackType.alpha) {
final Size chessSize = Size(size.height / 2, size.height / 2);
Paint chessPaintB = Paint()..color = const Color(0xffcccccc);
Paint chessPaintW = Paint()..color = Colors.white;
List.generate((size.height / chessSize.height).round(), (int y) {
List.generate((size.width / chessSize.width).round(), (int x) {
canvas.drawRect(
Offset(chessSize.width * x, chessSize.width * y) & chessSize,
(x + y) % 2 != 0 ? chessPaintW : chessPaintB,
);
});
});
}
switch (trackType) {
case TrackType.hue:
final List<Color> colors = [
const HSVColor.fromAHSV(1.0, 0.0, 1.0, 1.0).toColor(),
const HSVColor.fromAHSV(1.0, 60.0, 1.0, 1.0).toColor(),
const HSVColor.fromAHSV(1.0, 120.0, 1.0, 1.0).toColor(),
const HSVColor.fromAHSV(1.0, 180.0, 1.0, 1.0).toColor(),
const HSVColor.fromAHSV(1.0, 240.0, 1.0, 1.0).toColor(),
const HSVColor.fromAHSV(1.0, 300.0, 1.0, 1.0).toColor(),
const HSVColor.fromAHSV(1.0, 360.0, 1.0, 1.0).toColor(),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
case TrackType.saturation:
final List<Color> colors = [
HSVColor.fromAHSV(1.0, hsvColor.hue, 0.0, 1.0).toColor(),
HSVColor.fromAHSV(1.0, hsvColor.hue, 1.0, 1.0).toColor(),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
case TrackType.saturationForHSL:
final List<Color> colors = [
HSLColor.fromAHSL(1.0, hsvColor.hue, 0.0, 0.5).toColor(),
HSLColor.fromAHSL(1.0, hsvColor.hue, 1.0, 0.5).toColor(),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
case TrackType.value:
final List<Color> colors = [
HSVColor.fromAHSV(1.0, hsvColor.hue, 1.0, 0.0).toColor(),
HSVColor.fromAHSV(1.0, hsvColor.hue, 1.0, 1.0).toColor(),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
case TrackType.lightness:
final List<Color> colors = [
HSLColor.fromAHSL(1.0, hsvColor.hue, 1.0, 0.0).toColor(),
HSLColor.fromAHSL(1.0, hsvColor.hue, 1.0, 0.5).toColor(),
HSLColor.fromAHSL(1.0, hsvColor.hue, 1.0, 1.0).toColor(),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
case TrackType.red:
final List<Color> colors = [
hsvColor.toColor().withRed(0).withOpacity(1.0),
hsvColor.toColor().withRed(255).withOpacity(1.0),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
case TrackType.green:
final List<Color> colors = [
hsvColor.toColor().withGreen(0).withOpacity(1.0),
hsvColor.toColor().withGreen(255).withOpacity(1.0),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
case TrackType.blue:
final List<Color> colors = [
hsvColor.toColor().withBlue(0).withOpacity(1.0),
hsvColor.toColor().withBlue(255).withOpacity(1.0),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
case TrackType.alpha:
final List<Color> colors = [
hsvColor.toColor().withOpacity(0.0),
hsvColor.toColor().withOpacity(1.0),
];
Gradient gradient = LinearGradient(colors: colors);
canvas.drawRect(rect, Paint()..shader = gradient.createShader(rect));
break;
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
/// Painter for thumb of slider.
class ThumbPainter extends CustomPainter {
const ThumbPainter({this.thumbColor, this.fullThumbColor = false});
final Color? thumbColor;
final bool fullThumbColor;
@override
void paint(Canvas canvas, Size size) {
canvas.drawShadow(
Path()
..addOval(
Rect.fromCircle(center: const Offset(0.5, 2.0), radius: size.width * 1.8),
),
Colors.black,
3.0,
true,
);
canvas.drawCircle(
Offset(0.0, size.height * 0.4),
size.height,
Paint()
..color = Colors.white
..style = PaintingStyle.fill);
if (thumbColor != null) {
canvas.drawCircle(
Offset(0.0, size.height * 0.4),
size.height * (fullThumbColor ? 1.0 : 0.65),
Paint()
..color = thumbColor!
..style = PaintingStyle.fill);
}
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
/// Painter for chess type alpha background in color indicator widget.
class IndicatorPainter extends CustomPainter {
const IndicatorPainter(this.color);
final Color color;
@override
void paint(Canvas canvas, Size size) {
final Size chessSize = Size(size.width / 10, size.height / 10);
final Paint chessPaintB = Paint()..color = const Color(0xFFCCCCCC);
final Paint chessPaintW = Paint()..color = Colors.white;
List.generate((size.height / chessSize.height).round(), (int y) {
List.generate((size.width / chessSize.width).round(), (int x) {
canvas.drawRect(
Offset(chessSize.width * x, chessSize.height * y) & chessSize,
(x + y) % 2 != 0 ? chessPaintW : chessPaintB,
);
});
});
canvas.drawCircle(
Offset(size.width / 2, size.height / 2),
size.height / 2,
Paint()
..color = color
..style = PaintingStyle.fill);
}
@override
bool shouldRepaint(CustomPainter oldDelegate) => false;
}
/// Provide hex input wiget for 3/6/8 digits.
class ColorPickerInput extends StatefulWidget {
const ColorPickerInput(
this.color,
this.onColorChanged, {
Key? key,
this.enableAlpha = true,
this.embeddedText = false,
this.disable = false,
}) : super(key: key);
final Color color;
final ValueChanged<Color> onColorChanged;
final bool enableAlpha;
final bool embeddedText;
final bool disable;
@override
_ColorPickerInputState createState() => _ColorPickerInputState();
}
class _ColorPickerInputState extends State<ColorPickerInput> {
TextEditingController textEditingController = TextEditingController();
int inputColor = 0;
@override
void dispose() {
textEditingController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (inputColor != widget.color.value) {
textEditingController.text = '#' +
widget.color.red.toRadixString(16).toUpperCase().padLeft(2, '0') +
widget.color.green.toRadixString(16).toUpperCase().padLeft(2, '0') +
widget.color.blue.toRadixString(16).toUpperCase().padLeft(2, '0') +
(widget.enableAlpha ? widget.color.alpha.toRadixString(16).toUpperCase().padLeft(2, '0') : '');
}
return Padding(
padding: const EdgeInsets.only(top: 6.0, left: 12.0, right: 12.0),
child: SizedBox(
width: double.infinity,
child: TextField(
enabled: !widget.disable,
controller: textEditingController,
style: TextStyle(
fontSize: 18,
color: Theme.of(context).colorScheme.onBackground,
),
inputFormatters: [
UpperCaseTextFormatter(),
FilteringTextInputFormatter.allow(RegExp(kValidHexPattern)),
],
decoration: InputDecoration(
isDense: true,
filled: true,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.0),
borderSide: const BorderSide(color: Colors.transparent, width: 0.0),
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.0),
borderSide: const BorderSide(color: Colors.transparent, width: 0.0),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(12.0),
borderSide: const BorderSide(color: Colors.transparent, width: 0.0),
),
contentPadding: const EdgeInsets.symmetric(vertical: 8.0, horizontal: 8.0),
fillColor: AppColors.of(context).text.withOpacity(.1),
),
onChanged: (String value) {
String input = value;
if (value.length == 9) {
input = value.split('').getRange(7, 9).join() + value.split('').getRange(1, 7).join();
}
final Color? color = colorFromHex(input);
if (color != null) {
widget.onColorChanged(color);
inputColor = color.value;
}
},
),
),
);
}
}
/*class ValueColorPickerSlider extends StatefulWidget {
ValueColorPickerSlider(this.trackType, this.initialHsvColor, this.onProgressChanged, this.onColorChangeEnd, {Key? key}) : super(key: key);
final TrackType trackType;
final HSVColor initialHsvColor;
final void Function(double progress) onProgressChanged;
final void Function() onColorChangeEnd;
@override
State<ValueColorPickerSlider> createState() => _ValueColorPickerSliderState();
}
class _ValueColorPickerSliderState extends State<ValueColorPickerSlider> {
HSVColor hsvColor = HSVColor.fromColor(Colors.red);
@override
void initState() {
super.initState();
hsvColor = widget.initialHsvColor;
}
void slideEvent(RenderBox getBox, BoxConstraints box, Offset globalPosition) {
double localDx = getBox.globalToLocal(globalPosition).dx - 15.0;
double progress = localDx.clamp(0.0, box.maxWidth - 30.0) / (box.maxWidth - 30.0);
setState(() {
switch (widget.trackType) {
case TrackType.hue:
hsvColor = hsvColor.withHue(progress * 359);
break;
case TrackType.saturation:
hsvColor = hsvColor.withSaturation(progress);
break;
case TrackType.value:
hsvColor = hsvColor.withValue(progress);
break;
default:
break;
}
});
widget.onProgressChanged(progress);
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (BuildContext context, BoxConstraints box) {
double thumbOffset = 15.0;
Color thumbColor = Colors.white;
switch (widget.trackType) {
case TrackType.hue:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.hue / 360;
break;
case TrackType.saturation:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.saturation;
break;
case TrackType.value:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.value;
break;
default:
break;
}
return CustomMultiChildLayout(
delegate: _SliderLayout(),
children: <Widget>[
LayoutId(
id: _SliderLayout.track,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(50.0)),
child: CustomPaint(
painter: TrackPainter(
TrackType.values.firstWhere((element) => element == widget.trackType),
hsvColor,
)),
),
),
LayoutId(
id: _SliderLayout.thumb,
child: Transform.translate(
offset: Offset(thumbOffset, 0.0),
child: CustomPaint(
painter: ThumbPainter(
thumbColor: thumbColor,
fullThumbColor: false,
),
),
),
),
LayoutId(
id: _SliderLayout.gestureContainer,
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints box) {
RenderBox? getBox = context.findRenderObject() as RenderBox?;
return GestureDetector(
onPanDown: (DragDownDetails details) => getBox != null ? slideEvent(getBox, box, details.globalPosition) : null,
onPanEnd: (details) => widget.onColorChangeEnd(),
onPanUpdate: (DragUpdateDetails details) => getBox != null ? slideEvent(getBox, box, details.globalPosition) : null,
);
},
),
),
],
);
});
}
}*/
/// 9 track types for slider picker widget.
class ColorPickerSlider extends StatelessWidget {
const ColorPickerSlider(
this.trackType,
this.hsvColor,
this.onColorChanged,
this.onColorChangeEnd,
this.onProblem, {
Key? key,
this.displayThumbColor = false,
this.fullThumbColor = false,
}) : super(key: key);
final TrackType trackType;
final HSVColor hsvColor;
final ValueChanged<HSVColor> onColorChanged;
final void Function() onColorChangeEnd;
final void Function(int v) onProblem;
final bool displayThumbColor;
final bool fullThumbColor;
void slideEvent(RenderBox getBox, BoxConstraints box, Offset globalPosition) {
double localDx = getBox.globalToLocal(globalPosition).dx - 15.0;
double progress = localDx.clamp(0.0, box.maxWidth - 30.0) / (box.maxWidth - 30.0);
switch (trackType) {
case TrackType.hue:
// 360 is the same as zero
// if set to 360, sliding to end goes to zero
final newColor = hsvColor.withHue(progress * 359);
if (newColor.saturation == 0) {
onProblem(0);
return;
}
onColorChanged(newColor);
break;
case TrackType.saturation:
final newColor = hsvColor.withSaturation(progress);
if (newColor.value == 0) {
onProblem(1);
return;
}
onColorChanged(newColor);
break;
case TrackType.value:
onColorChanged(hsvColor.withValue(progress));
break;
default:
break;
}
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(builder: (BuildContext context, BoxConstraints box) {
double thumbOffset = 15.0;
Color thumbColor;
switch (trackType) {
case TrackType.hue:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.hue / 360;
thumbColor = HSVColor.fromAHSV(1.0, hsvColor.hue, 1.0, 1.0).toColor();
break;
case TrackType.saturation:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.saturation;
thumbColor = HSVColor.fromAHSV(1.0, hsvColor.hue, hsvColor.saturation, 1.0).toColor();
break;
case TrackType.saturationForHSL:
thumbOffset += (box.maxWidth - 30.0) * hsvToHsl(hsvColor).saturation;
thumbColor = HSLColor.fromAHSL(1.0, hsvColor.hue, hsvToHsl(hsvColor).saturation, 0.5).toColor();
break;
case TrackType.value:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.value;
thumbColor = HSVColor.fromAHSV(1.0, hsvColor.hue, 1.0, hsvColor.value).toColor();
break;
case TrackType.lightness:
thumbOffset += (box.maxWidth - 30.0) * hsvToHsl(hsvColor).lightness;
thumbColor = HSLColor.fromAHSL(1.0, hsvColor.hue, 1.0, hsvToHsl(hsvColor).lightness).toColor();
break;
case TrackType.red:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.toColor().red / 0xff;
thumbColor = hsvColor.toColor().withOpacity(1.0);
break;
case TrackType.green:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.toColor().green / 0xff;
thumbColor = hsvColor.toColor().withOpacity(1.0);
break;
case TrackType.blue:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.toColor().blue / 0xff;
thumbColor = hsvColor.toColor().withOpacity(1.0);
break;
case TrackType.alpha:
thumbOffset += (box.maxWidth - 30.0) * hsvColor.toColor().opacity;
thumbColor = hsvColor.toColor().withOpacity(hsvColor.alpha);
break;
}
return CustomMultiChildLayout(
delegate: _SliderLayout(),
children: <Widget>[
LayoutId(
id: _SliderLayout.track,
child: ClipRRect(
borderRadius: const BorderRadius.all(Radius.circular(50.0)),
child: CustomPaint(
painter: TrackPainter(
trackType,
hsvColor,
)),
),
),
LayoutId(
id: _SliderLayout.thumb,
child: Transform.translate(
offset: Offset(thumbOffset, 0.0),
child: CustomPaint(
painter: ThumbPainter(
thumbColor: displayThumbColor ? thumbColor : null,
fullThumbColor: fullThumbColor,
),
),
),
),
LayoutId(
id: _SliderLayout.gestureContainer,
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints box) {
RenderBox? getBox = context.findRenderObject() as RenderBox?;
return GestureDetector(
onPanDown: (DragDownDetails details) => getBox != null ? slideEvent(getBox, box, details.globalPosition) : null,
onPanEnd: (details) {
if ((trackType == TrackType.hue && hsvColor.saturation == 0) || (trackType == TrackType.saturation && hsvColor.value == 0)) {
return;
}
onColorChangeEnd();
},
onPanUpdate: (DragUpdateDetails details) => getBox != null ? slideEvent(getBox, box, details.globalPosition) : null,
);
},
),
),
],
);
});
}
}
/// Simple round color indicator.
class ColorIndicator extends StatelessWidget {
const ColorIndicator(
this.hsvColor, {
Key? key,
this.currentHsvColor,
this.icon,
this.width = 50.0,
this.height = 50.0,
this.adaptive = false,
}) : super(key: key);
final HSVColor hsvColor;
final HSVColor? currentHsvColor;
final double width;
final double height;
final IconData? icon;
final bool adaptive;
@override
Widget build(BuildContext context) {
Color color = hsvColor.toColor();
return Container(
width: width,
height: height,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: color,
boxShadow: [
BoxShadow(
color: useWhiteForeground(color) ? Colors.white.withOpacity(.5) : Colors.black.withOpacity(.5),
offset: const Offset(0, 0),
blurRadius: 5)
],
),
child: Material(
color: Colors.transparent,
child: AnimatedOpacity(
duration: const Duration(milliseconds: 210),
opacity: (icon != null || currentHsvColor == hsvColor) &&
(adaptive || Provider.of<SettingsProvider>(context, listen: false).accentColor != AccentColor.adaptive)
? 1
: 0,
child: Icon(icon ?? Icons.done, color: useWhiteForeground(color) ? Colors.white : Colors.black),
),
),
);
}
}
/// Provide Rectangle & Circle 2 categories, 10 variations of palette widget.
class ColorPickerArea extends StatelessWidget {
const ColorPickerArea(
this.hsvColor,
this.onColorChanged,
this.onChangeEnd,
this.paletteType, {
Key? key,
}) : super(key: key);
final HSVColor hsvColor;
final ValueChanged<HSVColor> onColorChanged;
final void Function() onChangeEnd;
final PaletteType paletteType;
/*void _handleColorRectChange(double horizontal, double vertical) {
onColorChanged(hsvColor.withSaturation(horizontal).withValue(vertical));
}*/
void _handleGesture(Offset position, BuildContext context, double height, double width) {
RenderBox? getBox = context.findRenderObject() as RenderBox?;
if (getBox == null) return;
Offset localOffset = getBox.globalToLocal(position);
double horizontal = localOffset.dx.clamp(0.0, width);
double vertical = localOffset.dy.clamp(0.0, height);
//_handleColorRectChange(horizontal / width, 1 - vertical / height);
onColorChanged(hsvColor.withSaturation(horizontal).withValue(vertical));
}
@override
Widget build(BuildContext context) {
return LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
double width = constraints.maxWidth;
double height = constraints.maxHeight;
return RawGestureDetector(
gestures: {
_AlwaysWinPanGestureRecognizer: GestureRecognizerFactoryWithHandlers<_AlwaysWinPanGestureRecognizer>(
() => _AlwaysWinPanGestureRecognizer(),
(_AlwaysWinPanGestureRecognizer instance) {
instance
..onDown = ((details) => _handleGesture(details.globalPosition, context, height, width))
..onEnd = ((d) => onChangeEnd())
..onUpdate = ((details) => _handleGesture(details.globalPosition, context, height, width));
},
),
},
child: Builder(
builder: (BuildContext _) {
return CustomPaint(painter: HSVWithHueColorPainter(hsvColor));
},
),
);
},
);
}
}
class _AlwaysWinPanGestureRecognizer extends PanGestureRecognizer {
@override
void addAllowedPointer(event) {
super.addAllowedPointer(event);
resolve(GestureDisposition.accepted);
}
@override
String get debugDescription => 'alwaysWin';
}
/// Uppercase text formater
class UpperCaseTextFormatter extends TextInputFormatter {
@override
TextEditingValue formatEditUpdate(oldValue, TextEditingValue newValue) =>
TextEditingValue(text: newValue.text.toUpperCase(), selection: newValue.selection);
}

View File

@@ -0,0 +1,220 @@
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
// FROM: https://pub.dev/packages/flutter_colorpicker
/// Common function lib
import 'dart:math';
import 'package:flutter/painting.dart';
import 'colors.dart';
/// Check if is good condition to use white foreground color by passing
/// the background color, and optional bias.
///
/// Reference:
///
/// Old: https://www.w3.org/TR/WCAG20-TECHS/G18.html
///
/// New: https://github.com/mchome/flutter_statusbarcolor/issues/40
bool useWhiteForeground(Color backgroundColor, {double bias = 0.0}) {
// Old:
// return 1.05 / (color.computeLuminance() + 0.05) > 4.5;
// New:
int v = sqrt(pow(backgroundColor.red, 2) * 0.299 +
pow(backgroundColor.green, 2) * 0.587 +
pow(backgroundColor.blue, 2) * 0.114)
.round();
return v < 130 + bias ? true : false;
}
/// Convert HSV to HSL
///
/// Reference: https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_HSL
HSLColor hsvToHsl(HSVColor color) {
double s = 0.0;
double l = 0.0;
l = (2 - color.saturation) * color.value / 2;
if (l != 0) {
if (l == 1) {
s = 0.0;
} else if (l < 0.5) {
s = color.saturation * color.value / (l * 2);
} else {
s = color.saturation * color.value / (2 - l * 2);
}
}
return HSLColor.fromAHSL(
color.alpha,
color.hue,
s.clamp(0.0, 1.0),
l.clamp(0.0, 1.0),
);
}
/// Convert HSL to HSV
///
/// Reference: https://en.wikipedia.org/wiki/HSL_and_HSV#HSL_to_HSV
HSVColor hslToHsv(HSLColor color) {
double s = 0.0;
double v = 0.0;
v = color.lightness + color.saturation * (color.lightness < 0.5 ? color.lightness : 1 - color.lightness);
if (v != 0) s = 2 - 2 * color.lightness / v;
return HSVColor.fromAHSV(
color.alpha,
color.hue,
s.clamp(0.0, 1.0),
v.clamp(0.0, 1.0),
);
}
/// [RegExp] pattern for validation HEX color [String] inputs, allows only:
///
/// * exactly 1 to 8 digits in HEX format,
/// * only Latin A-F characters, case insensitive,
/// * and integer numbers 0,1,2,3,4,5,6,7,8,9,
/// * with optional hash (`#`) symbol at the beginning (not calculated in length).
///
/// ```dart
/// final RegExp hexInputValidator = RegExp(kValidHexPattern);
/// if (hexInputValidator.hasMatch(hex)) print('$hex might be a valid HEX color');
/// ```
/// Reference: https://en.wikipedia.org/wiki/Web_colors#Hex_triplet
const String kValidHexPattern = r'^#?[0-9a-fA-F]{1,8}';
/// [RegExp] pattern for validation complete HEX color [String], allows only:
///
/// * exactly 6 or 8 digits in HEX format,
/// * only Latin A-F characters, case insensitive,
/// * and integer numbers 0,1,2,3,4,5,6,7,8,9,
/// * with optional hash (`#`) symbol at the beginning (not calculated in length).
///
/// ```dart
/// final RegExp hexCompleteValidator = RegExp(kCompleteValidHexPattern);
/// if (hexCompleteValidator.hasMatch(hex)) print('$hex is valid HEX color');
/// ```
/// Reference: https://en.wikipedia.org/wiki/Web_colors#Hex_triplet
const String kCompleteValidHexPattern = r'^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$';
/// Try to convert text input or any [String] to valid [Color].
/// The [String] must 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.
/// Optional [enableAlpha] can be provided (it's `true` by default). If it's set
/// to `false` transparency information (alpha channel) will be removed.
/// ```dart
/// /// // Valid 3 digit HEXs:
/// colorFromHex('abc') == Color(0xffaabbcc)
/// colorFromHex('ABc') == Color(0xffaabbcc)
/// colorFromHex('ABC') == Color(0xffaabbcc)
/// colorFromHex('#Abc') == Color(0xffaabbcc)
/// colorFromHex('#abc') == Color(0xffaabbcc)
/// colorFromHex('#ABC') == Color(0xffaabbcc)
/// // Valid 6 digit HEXs:
/// colorFromHex('aabbcc') == Color(0xffaabbcc)
/// colorFromHex('AABbcc') == Color(0xffaabbcc)
/// colorFromHex('AABBCC') == Color(0xffaabbcc)
/// colorFromHex('#AABbcc') == Color(0xffaabbcc)
/// colorFromHex('#aabbcc') == Color(0xffaabbcc)
/// colorFromHex('#AABBCC') == Color(0xffaabbcc)
/// // Valid 8 digit HEXs:
/// colorFromHex('ffaabbcc') == Color(0xffaabbcc)
/// colorFromHex('ffAABbcc') == Color(0xffaabbcc)
/// colorFromHex('ffAABBCC') == Color(0xffaabbcc)
/// colorFromHex('ffaabbcc', enableAlpha: true) == Color(0xffaabbcc)
/// colorFromHex('FFAAbbcc', enableAlpha: true) == Color(0xffaabbcc)
/// colorFromHex('ffAABBCC', enableAlpha: true) == Color(0xffaabbcc)
/// colorFromHex('FFaabbcc', enableAlpha: true) == Color(0xffaabbcc)
/// colorFromHex('#ffaabbcc') == Color(0xffaabbcc)
/// colorFromHex('#ffAABbcc') == Color(0xffaabbcc)
/// colorFromHex('#FFAABBCC') == Color(0xffaabbcc)
/// colorFromHex('#ffaabbcc', enableAlpha: true) == Color(0xffaabbcc)
/// colorFromHex('#FFAAbbcc', enableAlpha: true) == Color(0xffaabbcc)
/// colorFromHex('#ffAABBCC', enableAlpha: true) == Color(0xffaabbcc)
/// colorFromHex('#FFaabbcc', enableAlpha: true) == Color(0xffaabbcc)
/// // Invalid HEXs:
/// colorFromHex('bc') == null // length 2
/// colorFromHex('aabbc') == null // length 5
/// colorFromHex('#ffaabbccd') == null // length 9 (+#)
/// colorFromHex('aabbcx') == null // x character
/// colorFromHex('#aabbвв') == null // в non-latin character
/// colorFromHex('') == null // empty
/// ```
/// Reference: https://en.wikipedia.org/wiki/Web_colors#Hex_triplet
Color? colorFromHex(String inputString, {bool enableAlpha = true}) {
// Registers validator for exactly 6 or 8 digits long HEX (with optional #).
final RegExp hexValidator = RegExp(kCompleteValidHexPattern);
// Validating input, if it does not match — it's not proper HEX.
if (!hexValidator.hasMatch(inputString)) return null;
// Remove optional hash if exists and convert HEX to UPPER CASE.
String hexToParse = inputString.replaceFirst('#', '').toUpperCase();
// It may allow HEXs with transparency information even if alpha is disabled,
if (!enableAlpha && hexToParse.length == 8) {
// but it will replace this info with 100% non-transparent value (FF).
hexToParse = 'FF${hexToParse.substring(2)}';
}
// HEX may be provided in 3-digits format, let's just duplicate each letter.
if (hexToParse.length == 3) {
hexToParse = hexToParse.split('').expand((i) => [i * 2]).join();
}
// We will need 8 digits to parse the color, let's add missing digits.
if (hexToParse.length == 6) hexToParse = 'FF$hexToParse';
// HEX must be valid now, but as a precaution, it will just "try" to parse it.
final intColorValue = int.tryParse(hexToParse, radix: 16);
// If for some reason HEX is not valid — abort the operation, return nothing.
if (intColorValue == null) return null;
// Register output color for the last step.
final color = Color(intColorValue);
// Decide to return color with transparency information or not.
return enableAlpha ? color : color.withAlpha(255);
}
/// Converts `dart:ui` [Color] to the 6/8 digits HEX [String].
///
/// Prefixes a hash (`#`) sign if [includeHashSign] is set to `true`.
/// The result will be provided as UPPER CASE, it can be changed via [toUpperCase]
/// flag set to `false` (default is `true`). Hex can be returned without alpha
/// channel information (transparency), with the [enableAlpha] flag set to `false`.
String colorToHex(
Color color, {
bool includeHashSign = false,
bool enableAlpha = true,
bool toUpperCase = true,
}) {
final String hex = (includeHashSign ? '#' : '') +
(enableAlpha ? _padRadix(color.alpha) : '') +
_padRadix(color.red) +
_padRadix(color.green) +
_padRadix(color.blue);
return toUpperCase ? hex.toUpperCase() : hex;
}
// Shorthand for padLeft of RadixString, DRY.
String _padRadix(int value) => value.toRadixString(16).padLeft(2, '0');
// Extension for String
extension ColorExtension1 on String {
Color? toColor() {
Color? color = colorFromName(this);
if (color != null) return color;
return colorFromHex(this);
}
}
// Extension from Color
extension ColorExtension2 on Color {
String toHexString({bool includeHashSign = false, bool enableAlpha = true, bool toUpperCase = true}) =>
colorToHex(this, includeHashSign: false, enableAlpha: true, toUpperCase: true);
}