Skip to content

Commit 9e1d34d

Browse files
authored
Merge pull request #169 from GOSSII/master
Colorpicker with colorPickerBuilder and DialogBuilder
2 parents 0fae843 + 6312f88 commit 9e1d34d

File tree

2 files changed

+232
-92
lines changed

2 files changed

+232
-92
lines changed

packages/reactive_color_picker/example/lib/main.dart

Lines changed: 161 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@ class MyApp extends StatelessWidget {
1515
'inputDisabled': FormControl<Color>(disabled: true),
1616
'inputList': FormControl<List<Color>>(),
1717
'material': FormControl<Color>(value: Colors.amber),
18-
'colorPicker': FormControl<Color>(value: Colors.red),
18+
'colorPicker': FormControl<Color>(
19+
value: Colors.red, validators: [Validators.required]),
1920
'sliderColorPicker': FormControl<Color>(value: Colors.lime),
2021
});
2122

2223
@override
2324
Widget build(BuildContext context) {
25+
final textController = TextEditingController();
26+
2427
return MaterialApp(
2528
theme: ThemeData(primarySwatch: Colors.blue),
2629
home: Scaffold(
@@ -37,32 +40,144 @@ class MyApp extends StatelessWidget {
3740
builder: (context, form, child) {
3841
return Column(
3942
children: [
40-
const SizedBox(height: 16),
41-
ReactiveBlockColorPicker<Color>(
42-
formControlName: 'input',
43+
_Section(
44+
title: const Text('colorPicker'),
45+
child: ReactiveColorPicker<Color>(
46+
formControlName: 'colorPicker',
47+
),
48+
),
49+
_Section(
50+
title: const Text('colorPicker with custom picker'),
51+
child: ReactiveColorPicker<Color>(
52+
formControlName: 'colorPicker',
53+
pickerAreaHeightPercent: 0.7,
54+
displayThumbColor: true,
55+
labelTypes: const [],
56+
hexInputController: textController,
57+
portraitOnly: true,
58+
colorPickerBuilder: (pickColor, color) {
59+
return ListTile(
60+
title: const Text('Color Picker'),
61+
trailing: Container(
62+
width: 30,
63+
height: 30,
64+
decoration: BoxDecoration(
65+
color: color,
66+
borderRadius: BorderRadius.circular(8),
67+
border: Border.all(
68+
color: Colors.grey.shade400,
69+
),
70+
),
71+
),
72+
onTap: pickColor,
73+
);
74+
},
75+
colorPickerDialogBuilder: (colorPicker) {
76+
final border = OutlineInputBorder(
77+
borderSide:
78+
const BorderSide(color: Color(0xFFD77243)),
79+
borderRadius: BorderRadius.circular(12),
80+
);
81+
return AlertDialog(
82+
scrollable: true,
83+
titlePadding: EdgeInsets.zero,
84+
contentPadding: EdgeInsets.zero,
85+
clipBehavior: Clip.antiAlias,
86+
shape: RoundedRectangleBorder(
87+
borderRadius: BorderRadius.circular(16),
88+
),
89+
content: Column(
90+
children: [
91+
colorPicker,
92+
Padding(
93+
padding:
94+
const EdgeInsets.fromLTRB(16, 0, 16, 16),
95+
child: Row(
96+
children: [
97+
Container(
98+
height: 40,
99+
padding: const EdgeInsets.symmetric(
100+
horizontal: 12,
101+
),
102+
decoration: BoxDecoration(
103+
border: Border.all(
104+
color: const Color(0xFFD77243),
105+
),
106+
borderRadius:
107+
BorderRadius.circular(12),
108+
),
109+
child: const Center(
110+
child: Text(
111+
'HEX',
112+
style: TextStyle(
113+
color: Color(0xFF0F1111),
114+
fontSize: 12,
115+
fontWeight: FontWeight.w400,
116+
),
117+
),
118+
),
119+
),
120+
const SizedBox(width: 8),
121+
Expanded(
122+
child: SizedBox(
123+
height: 40,
124+
child: TextField(
125+
decoration: InputDecoration(
126+
enabledBorder: border,
127+
focusedBorder: border,
128+
border: border,
129+
counterText: '',
130+
),
131+
controller: textController,
132+
style: const TextStyle(
133+
color: Color(0xFF0F1111),
134+
fontSize: 12,
135+
fontWeight: FontWeight.w400,
136+
),
137+
autofocus: true,
138+
maxLength: 9,
139+
),
140+
),
141+
),
142+
],
143+
),
144+
),
145+
],
146+
),
147+
);
148+
},
149+
),
43150
),
44-
const SizedBox(height: 16),
45-
ReactiveBlockColorPicker<Color>(
46-
formControlName: 'inputDisabled',
151+
_Section(
152+
title: const Text('BlockColorPicker'),
153+
child: ReactiveBlockColorPicker<Color>(
154+
formControlName: 'input',
155+
),
47156
),
48-
const SizedBox(height: 16),
49-
ReactiveMultipleBlockColorPicker<List<Color>>(
50-
formControlName: 'inputList',
157+
_Section(
158+
title: const Text('BlockColorPicker disabled'),
159+
child: ReactiveBlockColorPicker<Color>(
160+
formControlName: 'inputDisabled',
161+
),
51162
),
52-
const SizedBox(height: 16),
53-
ReactiveMaterialColorPicker<Color>(
54-
formControlName: 'material',
163+
_Section(
164+
title: const Text('MultipleBlockColorPicker'),
165+
child: ReactiveMultipleBlockColorPicker<List<Color>>(
166+
formControlName: 'inputList',
167+
),
55168
),
56-
const SizedBox(height: 16),
57-
ReactiveColorPicker<Color>(
58-
formControlName: 'colorPicker',
59-
enableAlpha: false,
169+
_Section(
170+
title: const Text('MaterialColorPicker'),
171+
child: ReactiveMaterialColorPicker<Color>(
172+
formControlName: 'material',
173+
),
60174
),
61-
const SizedBox(height: 16),
62-
ReactiveSliderColorPicker<Color>(
63-
formControlName: 'sliderColorPicker',
175+
_Section(
176+
title: const Text('SliderColorPicker'),
177+
child: ReactiveSliderColorPicker<Color>(
178+
formControlName: 'sliderColorPicker',
179+
),
64180
),
65-
const SizedBox(height: 16),
66181
ElevatedButton(
67182
child: const Text('Sign Up'),
68183
onPressed: () {
@@ -84,3 +199,28 @@ class MyApp extends StatelessWidget {
84199
);
85200
}
86201
}
202+
203+
class _Section extends StatelessWidget {
204+
final Widget title;
205+
final Widget child;
206+
207+
const _Section({
208+
required this.title,
209+
required this.child,
210+
});
211+
212+
@override
213+
Widget build(BuildContext context) {
214+
return Padding(
215+
padding: const EdgeInsets.only(bottom: 16),
216+
child: Column(
217+
crossAxisAlignment: CrossAxisAlignment.start,
218+
children: [
219+
title,
220+
const SizedBox(height: 8),
221+
child,
222+
],
223+
),
224+
);
225+
}
226+
}

packages/reactive_color_picker/lib/src/reactive_color_picker.dart

Lines changed: 71 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,15 @@ import 'package:reactive_forms/reactive_forms.dart';
55

66
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
77

8+
typedef ColorPickerBuilder<T> = Widget Function(
9+
void Function() pickColor,
10+
Color? color,
11+
);
12+
13+
typedef ColorPickerDialogBuilder = Widget Function(
14+
ColorPicker colorPicker,
15+
);
16+
817
/// A builder that builds a widget responsible to decide when to show
918
/// the picker dialog.
1019
///
@@ -66,45 +75,47 @@ class ReactiveColorPicker<T> extends ReactiveFormField<T, Color> {
6675
TextEditingController? hexInputController,
6776
List<Color>? colorHistory,
6877
ValueChanged<List<Color>>? onHistoryChanged,
78+
ColorPickerBuilder<Color>? colorPickerBuilder,
79+
ColorPickerDialogBuilder? colorPickerDialogBuilder,
6980
}) : super(
7081
builder: (field) {
71-
void _showDialog(
72-
BuildContext context, {
73-
required Color pickerColor,
74-
required ValueChanged<Color> onColorChanged,
75-
}) {
82+
void pickColor() {
83+
final _colorPicker = ColorPicker(
84+
pickerColor: field.value ?? Colors.transparent,
85+
onColorChanged: field.didChange,
86+
paletteType: paletteType,
87+
enableAlpha: enableAlpha,
88+
pickerHsvColor: pickerHsvColor,
89+
labelTypes: labelTypes,
90+
displayThumbColor: displayThumbColor,
91+
portraitOnly: portraitOnly,
92+
colorPickerWidth: colorPickerWidth,
93+
pickerAreaHeightPercent: pickerAreaHeightPercent,
94+
pickerAreaBorderRadius: pickerAreaBorderRadius,
95+
hexInputBar: hexInputBar,
96+
hexInputController: hexInputController,
97+
colorHistory: colorHistory,
98+
onHistoryChanged: onHistoryChanged,
99+
);
100+
76101
showDialog<Color>(
77-
context: context,
102+
context: field.context,
78103
builder: (BuildContext context) {
79-
return AlertDialog(
80-
titlePadding: const EdgeInsets.all(0.0),
81-
contentPadding: const EdgeInsets.all(0.0),
82-
content: SingleChildScrollView(
83-
child: ColorPicker(
84-
pickerColor: pickerColor,
85-
onColorChanged: onColorChanged,
86-
paletteType: paletteType,
87-
enableAlpha: enableAlpha,
88-
pickerHsvColor: pickerHsvColor,
89-
labelTypes: labelTypes,
90-
displayThumbColor: displayThumbColor,
91-
portraitOnly: portraitOnly,
92-
colorPickerWidth: colorPickerWidth,
93-
pickerAreaHeightPercent: pickerAreaHeightPercent,
94-
pickerAreaBorderRadius: pickerAreaBorderRadius,
95-
hexInputBar: hexInputBar,
96-
hexInputController: hexInputController,
97-
colorHistory: colorHistory,
98-
onHistoryChanged: onHistoryChanged,
99-
),
100-
),
101-
);
104+
return colorPickerDialogBuilder?.call(
105+
_colorPicker,
106+
) ??
107+
AlertDialog(
108+
titlePadding: const EdgeInsets.all(0.0),
109+
contentPadding: const EdgeInsets.all(0.0),
110+
content: SingleChildScrollView(
111+
child: _colorPicker,
112+
),
113+
);
102114
},
103115
);
104116
}
105117

106-
final isEmptyValue =
107-
field.value == null || field.value.toString().isEmpty;
118+
final value = field.value;
108119

109120
final InputDecoration effectiveDecoration = (decoration ??
110121
const InputDecoration())
@@ -114,51 +125,40 @@ class ReactiveColorPicker<T> extends ReactiveFormField<T, Color> {
114125
? contrastIconColorDark
115126
: contrastIconColorLight;
116127

117-
return IgnorePointer(
118-
ignoring: !field.control.enabled,
119-
child: Opacity(
120-
opacity: field.control.enabled ? 1 : disabledOpacity,
121-
child: Listener(
122-
onPointerDown: (_) => field.control.markAsTouched(),
123-
child: InputDecorator(
124-
decoration: effectiveDecoration.copyWith(
125-
errorText: field.errorText,
126-
enabled: field.control.enabled,
127-
fillColor: field.value,
128-
filled: field.value != null,
129-
suffixIcon: Row(
130-
mainAxisAlignment: MainAxisAlignment.end,
131-
children: [
132-
IconButton(
133-
color: iconColor,
134-
icon: const Icon(Icons.edit),
135-
onPressed: () {
136-
_showDialog(
137-
field.context,
138-
pickerColor: field.value ?? (enableAlpha ? Colors.transparent : Colors.white),
139-
onColorChanged: field.didChange,
140-
);
141-
},
142-
splashRadius: 0.01,
143-
),
144-
if (field.value != null)
128+
return InputDecorator(
129+
decoration: effectiveDecoration.copyWith(
130+
errorText: field.errorText,
131+
enabled: field.control.enabled,
132+
),
133+
child: IgnorePointer(
134+
ignoring: !field.control.enabled,
135+
child: Opacity(
136+
opacity: field.control.enabled ? 1 : 0.5,
137+
child: colorPickerBuilder?.call(
138+
() => pickColor(),
139+
value,
140+
) ??
141+
ListTile(
142+
tileColor: value,
143+
trailing: Wrap(
144+
children: [
145145
IconButton(
146146
color: iconColor,
147-
icon: const Icon(Icons.clear),
148-
onPressed: () {
149-
field.didChange(null);
150-
},
147+
icon: const Icon(Icons.edit),
148+
onPressed: () => pickColor(),
151149
splashRadius: 0.01,
152150
),
153-
],
151+
if (field.value != null)
152+
IconButton(
153+
color: iconColor,
154+
icon: const Icon(Icons.clear),
155+
onPressed: () => field.didChange(null),
156+
splashRadius: 0.01,
157+
),
158+
],
159+
),
160+
onTap: pickColor,
154161
),
155-
),
156-
isEmpty:
157-
isEmptyValue && effectiveDecoration.hintText == null,
158-
child: Container(
159-
color: field.value,
160-
),
161-
),
162162
),
163163
),
164164
);

0 commit comments

Comments
 (0)