feat: customize appflowy editor selection menu style

This commit is contained in:
Lucas.Xu 2022-10-26 10:16:14 +08:00
parent 8656cfeb1e
commit 23a65bfa2a
10 changed files with 123 additions and 31 deletions

View File

@ -39,6 +39,8 @@ class MyApp extends StatelessWidget {
primarySwatch: Colors.blue, primarySwatch: Colors.blue,
// extensions: [HeadingPluginStyle.light], // extensions: [HeadingPluginStyle.light],
), ),
darkTheme: ThemeData.dark(),
themeMode: ThemeMode.dark,
home: const MyHomePage(title: 'AppFlowyEditor Example'), home: const MyHomePage(title: 'AppFlowyEditor Example'),
); );
} }

View File

@ -46,7 +46,7 @@ ShortcutEventHandler _ignorekHandler = (editorState, event) {
SelectionMenuItem codeBlockMenuItem = SelectionMenuItem( SelectionMenuItem codeBlockMenuItem = SelectionMenuItem(
name: () => 'Code Block', name: () => 'Code Block',
icon: const Icon( icon: (_, __) => const Icon(
Icons.abc, Icons.abc,
color: Colors.black, color: Colors.black,
size: 18.0, size: 18.0,

View File

@ -38,7 +38,7 @@ ShortcutEventHandler _insertHorzaontalRule = (editorState, event) {
SelectionMenuItem horizontalRuleMenuItem = SelectionMenuItem( SelectionMenuItem horizontalRuleMenuItem = SelectionMenuItem(
name: () => 'Horizontal rule', name: () => 'Horizontal rule',
icon: const Icon( icon: (_, __) => const Icon(
Icons.horizontal_rule, Icons.horizontal_rule,
color: Colors.black, color: Colors.black,
size: 18.0, size: 18.0,

View File

@ -6,7 +6,7 @@ import 'package:flutter_math_fork/flutter_math.dart';
SelectionMenuItem teXBlockMenuItem = SelectionMenuItem( SelectionMenuItem teXBlockMenuItem = SelectionMenuItem(
name: () => 'Tex', name: () => 'Tex',
icon: const Icon( icon: (_, __) => const Icon(
Icons.text_fields_rounded, Icons.text_fields_rounded,
color: Colors.black, color: Colors.black,
size: 18.0, size: 18.0,

View File

@ -3,7 +3,7 @@ import 'package:appflowy_editor/src/render/selection_menu/selection_menu_service
import 'package:appflowy_editor/src/render/selection_menu/selection_menu_widget.dart'; import 'package:appflowy_editor/src/render/selection_menu/selection_menu_widget.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class SelectionMenuItemWidget extends StatelessWidget { class SelectionMenuItemWidget extends StatefulWidget {
const SelectionMenuItemWidget({ const SelectionMenuItemWidget({
Key? key, Key? key,
required this.editorState, required this.editorState,
@ -11,7 +11,6 @@ class SelectionMenuItemWidget extends StatelessWidget {
required this.item, required this.item,
required this.isSelected, required this.isSelected,
this.width = 140.0, this.width = 140.0,
this.selectedColor = const Color(0xFFE0F8FF),
}) : super(key: key); }) : super(key: key);
final EditorState editorState; final EditorState editorState;
@ -19,33 +18,52 @@ class SelectionMenuItemWidget extends StatelessWidget {
final SelectionMenuItem item; final SelectionMenuItem item;
final double width; final double width;
final bool isSelected; final bool isSelected;
final Color selectedColor;
@override
State<SelectionMenuItemWidget> createState() =>
_SelectionMenuItemWidgetState();
}
class _SelectionMenuItemWidgetState extends State<SelectionMenuItemWidget> {
var _onHover = false;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final editorStyle = widget.editorState.editorStyle;
return Container( return Container(
padding: const EdgeInsets.fromLTRB(8.0, 5.0, 8.0, 5.0), padding: const EdgeInsets.fromLTRB(8.0, 5.0, 8.0, 5.0),
child: SizedBox( child: SizedBox(
width: width, width: widget.width,
child: TextButton.icon( child: TextButton.icon(
icon: item.icon, icon: widget.item
.icon(widget.editorState, widget.isSelected || _onHover),
style: ButtonStyle( style: ButtonStyle(
alignment: Alignment.centerLeft, alignment: Alignment.centerLeft,
overlayColor: MaterialStateProperty.all(selectedColor), overlayColor: MaterialStateProperty.all(
backgroundColor: isSelected editorStyle.selectionMenuItemSelectedColor),
? MaterialStateProperty.all(selectedColor) backgroundColor: widget.isSelected
? MaterialStateProperty.all(
editorStyle.selectionMenuItemSelectedColor)
: MaterialStateProperty.all(Colors.transparent), : MaterialStateProperty.all(Colors.transparent),
), ),
label: Text( label: Text(
item.name(), widget.item.name(),
textAlign: TextAlign.left, textAlign: TextAlign.left,
style: const TextStyle( style: TextStyle(
color: Colors.black, color: widget.isSelected || _onHover
fontSize: 14.0, ? editorStyle.selectionMenuItemSelectedTextColor
: editorStyle.selectionMenuItemTextColor,
fontSize: 12.0,
), ),
), ),
onPressed: () { onPressed: () {
item.handler(editorState, menuService, context); widget.item
.handler(widget.editorState, widget.menuService, context);
},
onHover: (value) {
setState(() {
_onHover = value;
});
}, },
), ),
), ),

View File

@ -131,7 +131,8 @@ List<SelectionMenuItem> get defaultSelectionMenuItems =>
final List<SelectionMenuItem> _defaultSelectionMenuItems = [ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
SelectionMenuItem( SelectionMenuItem(
name: () => AppFlowyEditorLocalizations.current.text, name: () => AppFlowyEditorLocalizations.current.text,
icon: _selectionMenuIcon('text'), icon: (editorState, onSelected) =>
_selectionMenuIcon('text', editorState, onSelected),
keywords: ['text'], keywords: ['text'],
handler: (editorState, _, __) { handler: (editorState, _, __) {
insertTextNodeAfterSelection(editorState, {}); insertTextNodeAfterSelection(editorState, {});
@ -139,7 +140,8 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
), ),
SelectionMenuItem( SelectionMenuItem(
name: () => AppFlowyEditorLocalizations.current.heading1, name: () => AppFlowyEditorLocalizations.current.heading1,
icon: _selectionMenuIcon('h1'), icon: (editorState, onSelected) =>
_selectionMenuIcon('h1', editorState, onSelected),
keywords: ['heading 1, h1'], keywords: ['heading 1, h1'],
handler: (editorState, _, __) { handler: (editorState, _, __) {
insertHeadingAfterSelection(editorState, BuiltInAttributeKey.h1); insertHeadingAfterSelection(editorState, BuiltInAttributeKey.h1);
@ -147,7 +149,8 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
), ),
SelectionMenuItem( SelectionMenuItem(
name: () => AppFlowyEditorLocalizations.current.heading2, name: () => AppFlowyEditorLocalizations.current.heading2,
icon: _selectionMenuIcon('h2'), icon: (editorState, onSelected) =>
_selectionMenuIcon('h2', editorState, onSelected),
keywords: ['heading 2, h2'], keywords: ['heading 2, h2'],
handler: (editorState, _, __) { handler: (editorState, _, __) {
insertHeadingAfterSelection(editorState, BuiltInAttributeKey.h2); insertHeadingAfterSelection(editorState, BuiltInAttributeKey.h2);
@ -155,7 +158,8 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
), ),
SelectionMenuItem( SelectionMenuItem(
name: () => AppFlowyEditorLocalizations.current.heading3, name: () => AppFlowyEditorLocalizations.current.heading3,
icon: _selectionMenuIcon('h3'), icon: (editorState, onSelected) =>
_selectionMenuIcon('h3', editorState, onSelected),
keywords: ['heading 3, h3'], keywords: ['heading 3, h3'],
handler: (editorState, _, __) { handler: (editorState, _, __) {
insertHeadingAfterSelection(editorState, BuiltInAttributeKey.h3); insertHeadingAfterSelection(editorState, BuiltInAttributeKey.h3);
@ -163,13 +167,15 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
), ),
SelectionMenuItem( SelectionMenuItem(
name: () => AppFlowyEditorLocalizations.current.image, name: () => AppFlowyEditorLocalizations.current.image,
icon: _selectionMenuIcon('image'), icon: (editorState, onSelected) =>
_selectionMenuIcon('image', editorState, onSelected),
keywords: ['image'], keywords: ['image'],
handler: showImageUploadMenu, handler: showImageUploadMenu,
), ),
SelectionMenuItem( SelectionMenuItem(
name: () => AppFlowyEditorLocalizations.current.bulletedList, name: () => AppFlowyEditorLocalizations.current.bulletedList,
icon: _selectionMenuIcon('bulleted_list'), icon: (editorState, onSelected) =>
_selectionMenuIcon('bulleted_list', editorState, onSelected),
keywords: ['bulleted list', 'list', 'unordered list'], keywords: ['bulleted list', 'list', 'unordered list'],
handler: (editorState, _, __) { handler: (editorState, _, __) {
insertBulletedListAfterSelection(editorState); insertBulletedListAfterSelection(editorState);
@ -177,7 +183,8 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
), ),
SelectionMenuItem( SelectionMenuItem(
name: () => AppFlowyEditorLocalizations.current.numberedList, name: () => AppFlowyEditorLocalizations.current.numberedList,
icon: _selectionMenuIcon('number'), icon: (editorState, onSelected) =>
_selectionMenuIcon('number', editorState, onSelected),
keywords: ['numbered list', 'list', 'ordered list'], keywords: ['numbered list', 'list', 'ordered list'],
handler: (editorState, _, __) { handler: (editorState, _, __) {
insertNumberedListAfterSelection(editorState); insertNumberedListAfterSelection(editorState);
@ -185,7 +192,8 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
), ),
SelectionMenuItem( SelectionMenuItem(
name: () => AppFlowyEditorLocalizations.current.checkbox, name: () => AppFlowyEditorLocalizations.current.checkbox,
icon: _selectionMenuIcon('checkbox'), icon: (editorState, onSelected) =>
_selectionMenuIcon('checkbox', editorState, onSelected),
keywords: ['todo list', 'list', 'checkbox list'], keywords: ['todo list', 'list', 'checkbox list'],
handler: (editorState, _, __) { handler: (editorState, _, __) {
insertCheckboxAfterSelection(editorState); insertCheckboxAfterSelection(editorState);
@ -193,7 +201,8 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
), ),
SelectionMenuItem( SelectionMenuItem(
name: () => AppFlowyEditorLocalizations.current.quote, name: () => AppFlowyEditorLocalizations.current.quote,
icon: _selectionMenuIcon('quote'), icon: (editorState, onSelected) =>
_selectionMenuIcon('quote', editorState, onSelected),
keywords: ['quote', 'refer'], keywords: ['quote', 'refer'],
handler: (editorState, _, __) { handler: (editorState, _, __) {
insertQuoteAfterSelection(editorState); insertQuoteAfterSelection(editorState);
@ -201,10 +210,13 @@ final List<SelectionMenuItem> _defaultSelectionMenuItems = [
), ),
]; ];
Widget _selectionMenuIcon(String name) { Widget _selectionMenuIcon(
String name, EditorState editorState, bool onSelected) {
return FlowySvg( return FlowySvg(
name: 'selection_menu/$name', name: 'selection_menu/$name',
color: Colors.black, color: onSelected
? editorState.editorStyle.selectionMenuItemSelectedIconColor
: editorState.editorStyle.selectionMenuItemIconColor,
width: 18.0, width: 18.0,
height: 18.0, height: 18.0,
); );

View File

@ -29,7 +29,7 @@ class SelectionMenuItem {
} }
final String Function() name; final String Function() name;
final Widget icon; final Widget Function(EditorState editorState, bool onSelected) icon;
/// Customizes keywords for item. /// Customizes keywords for item.
/// ///
@ -142,7 +142,7 @@ class _SelectionMenuWidgetState extends State<SelectionMenuWidget> {
onKey: _onKey, onKey: _onKey,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.white, color: widget.editorState.editorStyle.selectionMenuBackgroundColor,
boxShadow: [ boxShadow: [
BoxShadow( BoxShadow(
blurRadius: 5, blurRadius: 5,

View File

@ -14,6 +14,14 @@ class EditorStyle extends ThemeExtension<EditorStyle> {
final Color? cursorColor; final Color? cursorColor;
final Color? selectionColor; final Color? selectionColor;
// Selection menu styles
final Color? selectionMenuBackgroundColor;
final Color? selectionMenuItemTextColor;
final Color? selectionMenuItemIconColor;
final Color? selectionMenuItemSelectedTextColor;
final Color? selectionMenuItemSelectedIconColor;
final Color? selectionMenuItemSelectedColor;
// Text styles // Text styles
final EdgeInsets? textPadding; final EdgeInsets? textPadding;
final TextStyle? textStyle; final TextStyle? textStyle;
@ -33,6 +41,12 @@ class EditorStyle extends ThemeExtension<EditorStyle> {
required this.padding, required this.padding,
required this.cursorColor, required this.cursorColor,
required this.selectionColor, required this.selectionColor,
required this.selectionMenuBackgroundColor,
required this.selectionMenuItemTextColor,
required this.selectionMenuItemIconColor,
required this.selectionMenuItemSelectedTextColor,
required this.selectionMenuItemSelectedIconColor,
required this.selectionMenuItemSelectedColor,
required this.textPadding, required this.textPadding,
required this.textStyle, required this.textStyle,
required this.placeholderTextStyle, required this.placeholderTextStyle,
@ -51,6 +65,12 @@ class EditorStyle extends ThemeExtension<EditorStyle> {
EdgeInsets? padding, EdgeInsets? padding,
Color? cursorColor, Color? cursorColor,
Color? selectionColor, Color? selectionColor,
Color? selectionMenuBackgroundColor,
Color? selectionMenuItemTextColor,
Color? selectionMenuItemIconColor,
Color? selectionMenuItemSelectedTextColor,
Color? selectionMenuItemSelectedIconColor,
Color? selectionMenuItemSelectedColor,
TextStyle? textStyle, TextStyle? textStyle,
TextStyle? placeholderTextStyle, TextStyle? placeholderTextStyle,
TextStyle? bold, TextStyle? bold,
@ -66,6 +86,18 @@ class EditorStyle extends ThemeExtension<EditorStyle> {
padding: padding ?? this.padding, padding: padding ?? this.padding,
cursorColor: cursorColor ?? this.cursorColor, cursorColor: cursorColor ?? this.cursorColor,
selectionColor: selectionColor ?? this.selectionColor, selectionColor: selectionColor ?? this.selectionColor,
selectionMenuBackgroundColor:
selectionMenuBackgroundColor ?? this.selectionMenuBackgroundColor,
selectionMenuItemTextColor:
selectionMenuItemTextColor ?? this.selectionMenuItemTextColor,
selectionMenuItemIconColor:
selectionMenuItemIconColor ?? this.selectionMenuItemIconColor,
selectionMenuItemSelectedTextColor: selectionMenuItemSelectedTextColor ??
selectionMenuItemSelectedTextColor,
selectionMenuItemSelectedIconColor: selectionMenuItemSelectedIconColor ??
selectionMenuItemSelectedIconColor,
selectionMenuItemSelectedColor:
selectionMenuItemSelectedColor ?? this.selectionMenuItemSelectedColor,
textPadding: textPadding ?? textPadding, textPadding: textPadding ?? textPadding,
textStyle: textStyle ?? this.textStyle, textStyle: textStyle ?? this.textStyle,
placeholderTextStyle: placeholderTextStyle ?? this.placeholderTextStyle, placeholderTextStyle: placeholderTextStyle ?? this.placeholderTextStyle,
@ -91,6 +123,22 @@ class EditorStyle extends ThemeExtension<EditorStyle> {
cursorColor: Color.lerp(cursorColor, other.cursorColor, t), cursorColor: Color.lerp(cursorColor, other.cursorColor, t),
textPadding: EdgeInsets.lerp(textPadding, other.textPadding, t), textPadding: EdgeInsets.lerp(textPadding, other.textPadding, t),
selectionColor: Color.lerp(selectionColor, other.selectionColor, t), selectionColor: Color.lerp(selectionColor, other.selectionColor, t),
selectionMenuBackgroundColor: Color.lerp(
selectionMenuBackgroundColor, other.selectionMenuBackgroundColor, t),
selectionMenuItemTextColor: Color.lerp(
selectionMenuItemTextColor, other.selectionMenuItemTextColor, t),
selectionMenuItemIconColor: Color.lerp(
selectionMenuItemIconColor, other.selectionMenuItemIconColor, t),
selectionMenuItemSelectedTextColor: Color.lerp(
selectionMenuItemSelectedTextColor,
other.selectionMenuItemSelectedTextColor,
t),
selectionMenuItemSelectedIconColor: Color.lerp(
selectionMenuItemSelectedIconColor,
other.selectionMenuItemSelectedIconColor,
t),
selectionMenuItemSelectedColor: Color.lerp(selectionMenuItemSelectedColor,
other.selectionMenuItemSelectedColor, t),
textStyle: TextStyle.lerp(textStyle, other.textStyle, t), textStyle: TextStyle.lerp(textStyle, other.textStyle, t),
placeholderTextStyle: placeholderTextStyle:
TextStyle.lerp(placeholderTextStyle, other.placeholderTextStyle, t), TextStyle.lerp(placeholderTextStyle, other.placeholderTextStyle, t),
@ -109,6 +157,12 @@ class EditorStyle extends ThemeExtension<EditorStyle> {
padding: const EdgeInsets.fromLTRB(200.0, 0.0, 200.0, 0.0), padding: const EdgeInsets.fromLTRB(200.0, 0.0, 200.0, 0.0),
cursorColor: const Color(0xFF00BCF0), cursorColor: const Color(0xFF00BCF0),
selectionColor: const Color.fromARGB(53, 111, 201, 231), selectionColor: const Color.fromARGB(53, 111, 201, 231),
selectionMenuBackgroundColor: const Color(0xFFFFFFFF),
selectionMenuItemTextColor: const Color(0xFF333333),
selectionMenuItemIconColor: const Color(0xFF333333),
selectionMenuItemSelectedTextColor: const Color(0xFF333333),
selectionMenuItemSelectedIconColor: const Color(0xFF333333),
selectionMenuItemSelectedColor: const Color(0xFFE0F8FF),
textPadding: const EdgeInsets.symmetric(vertical: 8.0), textPadding: const EdgeInsets.symmetric(vertical: 8.0),
textStyle: const TextStyle(fontSize: 16.0, color: Colors.black), textStyle: const TextStyle(fontSize: 16.0, color: Colors.black),
placeholderTextStyle: const TextStyle(fontSize: 16.0, color: Colors.grey), placeholderTextStyle: const TextStyle(fontSize: 16.0, color: Colors.grey),
@ -135,5 +189,11 @@ class EditorStyle extends ThemeExtension<EditorStyle> {
fontSize: 16.0, fontSize: 16.0,
color: Colors.white.withOpacity(0.3), color: Colors.white.withOpacity(0.3),
), ),
selectionMenuBackgroundColor: const Color(0xFF282E3A),
selectionMenuItemTextColor: const Color(0xFFBBC3CD),
selectionMenuItemIconColor: const Color(0xFFBBC3CD),
selectionMenuItemSelectedTextColor: const Color(0xFF131720),
selectionMenuItemSelectedIconColor: const Color(0xFF131720),
selectionMenuItemSelectedColor: const Color(0xFF00BCF0),
); );
} }

View File

@ -137,7 +137,7 @@ Future<EditorWidgetTester> _prepare(WidgetTester tester) async {
); );
for (final item in defaultSelectionMenuItems) { for (final item in defaultSelectionMenuItems) {
expect(find.byWidget(item.icon), findsOneWidget); expect(find.text(item.name()), findsOneWidget);
} }
return Future.value(editor); return Future.value(editor);

View File

@ -30,7 +30,7 @@ void main() async {
); );
for (final item in defaultSelectionMenuItems) { for (final item in defaultSelectionMenuItems) {
expect(find.byWidget(item.icon), findsOneWidget); expect(find.text(item.name()), findsOneWidget);
} }
await editor.updateSelection(Selection.single(path: [1], startOffset: 0)); await editor.updateSelection(Selection.single(path: [1], startOffset: 0));