Skip to content

Commit

Permalink
Merge pull request #18 from robiness/add-samples-to-color-picker
Browse files Browse the repository at this point in the history
Add samples to color picker
  • Loading branch information
robiness authored Jul 12, 2023
2 parents 91cb016 + 3927f13 commit ff979e3
Show file tree
Hide file tree
Showing 7 changed files with 227 additions and 40 deletions.
27 changes: 27 additions & 0 deletions example/lib/stage_data/example_data.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:flutter/material.dart';
import 'package:stage_craft/stage_craft.dart';

class ExampleData {
static const colorSamples = [
ColorSample(
color: Colors.teal,
name: 'StgColors.teal',
),
ColorSample(
color: Colors.amber,
name: 'StgColors.amber',
),
ColorSample(
color: Colors.indigo,
name: 'StgColors.indigo',
),
ColorSample(
color: Colors.blueAccent,
name: 'StgColors.blueAccent',
),
ColorSample(
color: Colors.lime,
name: 'StgColors.lime',
),
];
}
7 changes: 6 additions & 1 deletion example/lib/stage_data/my_list_tile_widget_stage.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import 'package:example/stage_data/example_data.dart';
import 'package:flutter/material.dart';
import 'package:stage_craft/stage_craft.dart';

Expand All @@ -7,7 +8,11 @@ class MyListTileWidgetStage extends WidgetStageData {
_listPadding = PaddingFieldConfigurator(value: EdgeInsets.zero, name: 'listPadding'),
_title = StringFieldConfigurator(value: 'My List Tile', name: 'title'),
_circleColor = ColorFieldConfigurator(value: Colors.purple, name: 'circleColor'),
_tileColor = ColorFieldConfiguratorNullable(value: Colors.cyan, name: 'tileColor'),
_tileColor = ColorFieldConfiguratorNullable(
value: Colors.cyan,
name: 'tileColor',
colorSamples: ExampleData.colorSamples,
),
_textColor = ColorFieldConfiguratorNullable(value: Colors.white, name: 'textColor'),
_borderRadius = DoubleFieldConfiguratorNullable(value: 10, name: 'borderRadius'),
_tileGap = DoubleFieldConfigurator(value: 0, name: 'tileSpace');
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ dependencies:


dev_dependencies:
lint: ^2.1.2
flutter_test:
sdk: flutter
lint: ^2.1.2


flutter:
Expand Down
82 changes: 45 additions & 37 deletions lib/src/field_configurators/color_field_configurator.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:stage_craft/stage_craft.dart';

class ColorFieldConfigurator extends FieldConfigurator<Color> {
ColorFieldConfigurator({
required super.value,
required super.name,
this.colorSamples,
});

final List<ColorSample>? colorSamples;

@override
Widget build(BuildContext context) {
return ColorConfigurationWidget(
configurator: this,
value: value,
updateValue: (Color? color) {
updateValue(color ?? Colors.transparent);
},
colorSamples: colorSamples,
);
}
}
Expand All @@ -24,30 +27,30 @@ class ColorFieldConfiguratorNullable extends FieldConfigurator<Color?> {
ColorFieldConfiguratorNullable({
required super.value,
required super.name,
this.colorSamples,
});

final List<ColorSample>? colorSamples;

@override
Widget build(BuildContext context) {
return ColorConfigurationWidget(
configurator: this,
value: value,
updateValue: updateValue,
colorSamples: colorSamples,
);
}
}

class ColorConfigurationWidget extends StatefulConfigurationWidget<Color?> {
class ColorConfigurationWidget extends ConfigurationWidget<Color?> {
const ColorConfigurationWidget({
super.key,
required super.configurator,
required super.value,
required super.updateValue,
this.colorSamples,
});

@override
State<ColorConfigurationWidget> createState() => _ColorConfigurationFieldState();
}

class _ColorConfigurationFieldState extends State<ColorConfigurationWidget> {
late Color? color = widget.configurator.value;
final List<ColorSample>? colorSamples;

@override
Widget build(BuildContext context) {
Expand All @@ -56,31 +59,36 @@ class _ColorConfigurationFieldState extends State<ColorConfigurationWidget> {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
title: const Text('Pick a color!'),
content: SingleChildScrollView(
child: ColorPicker(
pickerColor: color ?? Colors.white,
onColorChanged: (newColor) {
color = newColor;
},
),
),
actions: <Widget>[
ElevatedButton(
child: const Text('Abort'),
onPressed: () {
Navigator.of(context).pop();
},
),
ElevatedButton(
child: const Text('Accept'),
onPressed: () {
widget.updateValue(color);
Navigator.of(context).pop();
},
),
],
Color? color = value;
return StatefulBuilder(
builder: (context, setState) {
return AlertDialog(
scrollable: true,
title: const Text('Pick a color!'),
content: ColorPicker(
color: color ?? Colors.white,
colorSamples: colorSamples,
onColorChanged: (newColor) {
setState(() {
color = newColor;
});
},
),
actions: <Widget>[
ElevatedButton(
onPressed: Navigator.of(context).pop,
child: const Text('Abort'),
),
ElevatedButton(
onPressed: () {
updateValue(color);
Navigator.of(context).pop();
},
child: const Text('Accept'),
),
],
);
},
);
},
);
Expand All @@ -96,7 +104,7 @@ class _ColorConfigurationFieldState extends State<ColorConfigurationWidget> {
width: 38,
foregroundDecoration: BoxDecoration(
// The actual color drawn over the chessboard pattern
color: widget.configurator.value,
color: value,
),
// The chessboard pattern
child: const CustomPaint(
Expand Down
1 change: 0 additions & 1 deletion lib/src/field_configurators/field_configurator.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';

export 'bool_field_configurator.dart';
export 'color_field_configurator.dart';
Expand Down
147 changes: 147 additions & 0 deletions lib/src/widgets/color_picker.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart' as picker;

/// A color picking interface with two modes of color selection.
///
/// The widget provides a complete color picker that allows free selection of any color. In addition,
/// it also presents a list of predefined color samples to quickly select from if [colorSamples] are provided.
///
/// ```
/// Creating a ColorPicker widget
/// ColorPicker(
/// colorSamples: [ColorSample(color: Colors.blue, name: 'Blue')],
/// color: Colors.red,
/// onColorChanged: (color) {
/// print('Color changed to $color');
/// },
/// );
/// ```
class ColorPicker extends StatelessWidget {
/// Creates a `ColorPicker`.
///
/// The [colorSamples] parameter is a list of `ColorSample` objects, each representing a predefined color.
/// This parameter is optional, if it's null or empty, no color samples will be displayed.
///
/// The [color] parameter represents the currently selected color in the [ColorPicker]. If not provided,
/// the color picker will default to white.
///
/// The required [onColorChanged] parameter is a callback that will be called when a new color is selected.
const ColorPicker({
this.colorSamples,
this.color,
required this.onColorChanged,
});

/// List of color samples. Can be null.
final List<ColorSample>? colorSamples;

/// Currently selected color. Can be null, in which case the color picker defaults to white.
final Color? color;

/// Callback that is called when a new color is selected.
final void Function(Color color) onColorChanged;

@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
picker.ColorPicker(
pickerColor: color ?? Colors.white,
onColorChanged: onColorChanged,
),
if (colorSamples?.isNotEmpty == true) ...[
const SizedBox(height: 32.0),
const Text('Color Samples'),
const SizedBox(height: 16.0),
Wrap(
spacing: 24,
runSpacing: 10,
children: colorSamples!.map(
(sample) {
final isSelected = sample.color == color;
return GestureDetector(
onTap: () => onColorChanged.call(sample.color),
child: MouseRegion(
cursor: SystemMouseCursors.click,
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Container(
width: 38,
height: 38,
decoration: BoxDecoration(
color: isSelected ? Colors.blue : Colors.transparent,
shape: BoxShape.circle,
),
child: Padding(
padding: const EdgeInsets.all(2),
child: Container(
width: 34,
height: 34,
decoration: BoxDecoration(
color: isSelected ? Colors.white : Colors.transparent,
shape: BoxShape.circle,
),
child: Padding(
padding: const EdgeInsets.all(2),
child: Container(
width: 30,
height: 30,
decoration: BoxDecoration(
color: sample.color,
shape: BoxShape.circle,
),
),
),
),
),
),
if (sample.name != null) ...[
const SizedBox(height: 6.0),
Text(
sample.name!,
style: const TextStyle(
fontSize: 10,
),
),
]
],
),
),
);
},
).toList(),
),
]
],
);
}
}

/// A ColorSample class represents a color sample with an optional name.
///
/// It contains a `Color` and an optional `String` to represent its according name.
///
/// Used as optional parameters in [ColorFieldConfigurator] and [ColorFieldConfiguratorNullable].
/// ```
/// Creating a ColorSample
/// const ColorSample sample = ColorSample(color: Colors.blue, name: "MyColors.blue");
/// ```
class ColorSample {
/// Creates a `ColorSample`.
///
/// The [color] parameter is required, and it represents the actual color of the sample.
///
/// The [name] parameter is optional. It can be used to assign a specific name to the sample.
const ColorSample({
required this.color,
this.name,
});

/// The name of the color sample, can be null.
final String? name;

/// The color of the color sample. This property is required and cannot be null.
final Color color;
}
1 change: 1 addition & 0 deletions lib/stage_craft.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export 'src/field_configurators/field_configurator.dart';
export 'src/stage_controller.dart';
export 'src/widget_stage_data.dart';
export 'src/widgets/color_picker.dart';
export 'src/widgets/stage_craft.dart';

0 comments on commit ff979e3

Please sign in to comment.