Skip to content

Commit

Permalink
feat: Implement color picker dialog for user color selection (#5041)
Browse files Browse the repository at this point in the history
- Added `flex_color_picker` dependency.
- Implemented a new color picker dialog with the following features:
  - Display Material predefined colors.
  - Include primary, accent, and shade colors, as well as a color wheel for selection.
  - Add a graphical opacity selector.
  - Enhanced the previous dialog to include an icon in a text field for opening the palette.
- Added `suffixIcon` parameter to the `_ColorSettingTextField` class, making it reactive to tap gestures.
- Utilized `ColorExtension` on the `Color` class to avoid namespace conflicts when converting colors to hexadecimal strings.
- Added translation strings to english

This commit addresses issue #5041: [FR] Let user pick from a list of predefined colors for selection and cursor colors.
  • Loading branch information
gustamor committed Jul 2, 2024
1 parent 050478f commit a3892c4
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import 'package:flex_color_picker/flex_color_picker.dart';
import 'package:flowy_infra/size.dart';
import 'package:flowy_infra/theme_extension.dart';
import 'package:flutter/material.dart';

import 'package:appflowy/generated/locale_keys.g.dart';
Expand Down Expand Up @@ -103,11 +106,20 @@ class DocumentColorSettingDialogState
}
}

void updateColor(Color color) {
setState(() {
hexController.text = ColorExtension(color).toHexString().extractHex();
opacityController.text =
ColorExtension(color).toHexString().extractOpacity();
});
updateSelectedColor();
}

@override
void initState() {
super.initState();
selectedColorOnDialog = widget.currentColor;
currentColorHexString = widget.currentColor.toHexString();
currentColorHexString = ColorExtension(widget.currentColor).toHexString();
hexController = TextEditingController(
text: currentColorHexString.extractHex(),
);
Expand Down Expand Up @@ -148,6 +160,14 @@ class DocumentColorSettingDialogState
onChanged: (_) => updateSelectedColor(),
onFieldSubmitted: (_) => updateSelectedColor(),
validator: (v) => validateHexValue(v, opacityController.text),
suffixIcon: GestureDetector(
onTap: () => _showColorPickerDialog(
context: context,
currentColor: widget.currentColor,
updateColor: updateColor,
),
child: const Icon(Icons.color_lens_rounded),
),
),
const VSpace(8),
_ColorSettingTextField(
Expand All @@ -172,6 +192,7 @@ class _ColorSettingTextField extends StatelessWidget {
required this.labelText,
required this.hintText,
required this.onFieldSubmitted,
this.suffixIcon,
this.onChanged,
this.validator,
});
Expand All @@ -180,6 +201,7 @@ class _ColorSettingTextField extends StatelessWidget {
final String labelText;
final String hintText;
final void Function(String) onFieldSubmitted;
final Widget? suffixIcon;
final void Function(String)? onChanged;
final String? Function(String?)? validator;

Expand All @@ -191,6 +213,7 @@ class _ColorSettingTextField extends StatelessWidget {
decoration: InputDecoration(
labelText: labelText,
hintText: hintText,
suffixIcon: suffixIcon,
border: OutlineInputBorder(
borderSide: BorderSide(color: style.colorScheme.outline),
),
Expand Down Expand Up @@ -241,3 +264,107 @@ String? validateOpacityValue(String? value) {
}
return null;
}

void _showColorPickerDialog({
required BuildContext context,
String? title,
required Color currentColor,
required void Function(Color) updateColor,
}) {
const kColorCircleWidth = 46.0;
const kColorCircleHeight = 46.0;
const kColorCircleRadius = 23.0;
const kColorOpacityThumbRadius = 23.0;
const kDialogButtonPaddingHorizontal = 24.0;
const kDialogButtonPaddingVertical = 12.0;
const kColorsColumnSpacing = 3.0;
final style = Theme.of(context);
Color selectedColor = currentColor;

void updated(Color color) {
updateColor(color);
Navigator.of(context).pop();
}

showDialog(
context: context,
barrierColor: const Color.fromARGB(128, 0, 0, 0),
builder: (context) {
return AlertDialog(
icon: const Icon(Icons.palette),
title: Text(
title ??
LocaleKeys.settings_appearance_documentSettings_pickColor.tr(),
style: style.textTheme.titleLarge,
),
content: SingleChildScrollView(
child: ColorPicker(
width: kColorCircleWidth,
height: kColorCircleHeight,
borderRadius: kColorCircleRadius,
enableOpacity: true,
opacityThumbRadius: kColorOpacityThumbRadius,
columnSpacing: kColorsColumnSpacing,
enableTooltips: false,
pickersEnabled: const {
ColorPickerType.both: false,
ColorPickerType.primary: true,
ColorPickerType.accent: true,
ColorPickerType.wheel: true,
},
subheading: Text(
LocaleKeys.settings_appearance_documentSettings_colorShade.tr(),
style: style.textTheme.labelLarge,
),
opacitySubheading: Text(
LocaleKeys.settings_appearance_documentSettings_opacity.tr(),
style: style.textTheme.labelLarge,
),
onColorChanged: (color) {
selectedColor = color;
},
),
),
actionsPadding: const EdgeInsets.all(8),
actions: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
SizedBox(
height: 24,
child: FlowyTextButton(
LocaleKeys.button_cancel.tr(),
padding: const EdgeInsets.symmetric(
horizontal: kDialogButtonPaddingHorizontal,
vertical: kDialogButtonPaddingVertical,
),
fontColor: AFThemeExtension.of(context).textColor,
fillColor: Colors.transparent,
hoverColor: Colors.transparent,
radius: Corners.s12Border,
onPressed: () => updated(currentColor),
),
),
const HSpace(8),
SizedBox(
height: 48,
child: FlowyTextButton(
LocaleKeys.button_done.tr(),
padding: const EdgeInsets.symmetric(
horizontal: kDialogButtonPaddingHorizontal,
vertical: kDialogButtonPaddingVertical,
),
radius: Corners.s12Border,
fontHoverColor: Colors.white,
fillColor: Theme.of(context).colorScheme.primary,
hoverColor: const Color(0xFF005483),
onPressed: () => updated(selectedColor),
),
),
],
),
],
);
},
);
}
16 changes: 16 additions & 0 deletions frontend/appflowy_flutter/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.0"
flex_color_picker:
dependency: "direct main"
description:
name: flex_color_picker
sha256: "809af4ec82ede3b140ed0219b97d548de99e47aa4b99b14a10f705a2dbbcba5e"
url: "https://pub.dev"
source: hosted
version: "3.5.1"
flex_seed_scheme:
dependency: transitive
description:
name: flex_seed_scheme
sha256: "6c595e545b0678e1fe17e8eec3d1fbca7237482da194fadc20ad8607dc7a7f3d"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
flowy_infra:
dependency: "direct main"
description:
Expand Down
1 change: 1 addition & 0 deletions frontend/appflowy_flutter/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ dependencies:
styled_widget: ^0.4.1
expandable: ^5.0.1
flutter_colorpicker: ^1.0.3
flex_color_picker: ^3.5.1
highlight: ^0.7.0
package_info_plus: ^6.0.0
url_launcher: ^6.1.11
Expand Down
3 changes: 3 additions & 0 deletions frontend/resources/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,9 @@
"documentSettings": {
"cursorColor": "Document cursor color",
"selectionColor": "Document selection color",
"pickColor": "Select a color",
"colorShade": "Color shade",
"opacity": "Opacity",
"hexEmptyError": "Hex color cannot be empty",
"hexLengthError": "Hex value must be 6 digits long",
"hexInvalidError": "Invalid hex value",
Expand Down

0 comments on commit a3892c4

Please sign in to comment.