瀏覽代碼

feat: refactor theme plugin, use themedata extension

Lucas.Xu 2 年之前
父節點
當前提交
dde50d32d6
共有 18 個文件被更改,包括 177 次插入475 次删除
  1. 26 71
      frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart
  2. 1 1
      frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart
  3. 4 3
      frontend/app_flowy/packages/appflowy_editor/lib/src/editor_state.dart
  4. 0 50
      frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/built_in_text_widget.dart
  5. 3 2
      frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/bulleted_list_text.dart
  6. 3 2
      frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/checkbox_text.dart
  7. 5 4
      frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/flowy_rich_text.dart
  8. 3 2
      frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/heading_text.dart
  9. 4 15
      frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/number_list_text.dart
  10. 3 2
      frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/quoted_text.dart
  11. 11 8
      frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/rich_text.dart
  12. 15 0
      frontend/app_flowy/packages/appflowy_editor/lib/src/render/style/built_in_plugin_styles.dart
  13. 48 266
      frontend/app_flowy/packages/appflowy_editor/lib/src/render/style/editor_style.dart
  14. 1 1
      frontend/app_flowy/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart
  15. 46 44
      frontend/app_flowy/packages/appflowy_editor/lib/src/service/editor_service.dart
  16. 1 1
      frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/format_style_handler.dart
  17. 0 1
      frontend/app_flowy/packages/appflowy_editor/test/infra/test_editor.dart
  18. 3 2
      frontend/app_flowy/packages/appflowy_editor/test/render/image/image_node_builder_test.dart

+ 26 - 71
frontend/app_flowy/packages/appflowy_editor/example/lib/main.dart

@@ -10,7 +10,6 @@ import 'package:flutter/services.dart';
 
 import 'package:file_picker/file_picker.dart';
 import 'package:flutter_localizations/flutter_localizations.dart';
-import 'package:google_fonts/google_fonts.dart';
 import 'package:path_provider/path_provider.dart';
 import 'package:universal_html/html.dart' as html;
 
@@ -57,7 +56,6 @@ class _MyHomePageState extends State<MyHomePage> {
   int _pageIndex = 0;
   EditorState? _editorState;
   bool darkMode = false;
-  EditorStyle _editorStyle = EditorStyle.defaultStyle();
   Future<String>? _jsonString;
 
   @override
@@ -132,7 +130,8 @@ class _MyHomePageState extends State<MyHomePage> {
                   CheckboxPluginStyle.dark,
                   NumberListPluginStyle.dark,
                   QuotedTextPluginStyle.dark,
-                  BulletedListPluginStyle.dark
+                  BulletedListPluginStyle.dark,
+                  EditorStyle.dark,
                 ])
               : ThemeData.light().copyWith(
                   extensions: [
@@ -140,34 +139,32 @@ class _MyHomePageState extends State<MyHomePage> {
                     CheckboxPluginStyle.light,
                     NumberListPluginStyle.light,
                     QuotedTextPluginStyle.light,
-                    BulletedListPluginStyle.light
+                    BulletedListPluginStyle.light,
+                    EditorStyle.light,
                   ],
                 );
-          return Theme(
-            data: themeData,
-            child: Container(
-              color: darkMode ? Colors.black : Colors.white,
-              width: MediaQuery.of(context).size.width,
-              child: AppFlowyEditor(
-                editorState: _editorState!,
-                editorStyle: _editorStyle,
-                editable: true,
-                customBuilders: {
-                  'text/code_block': CodeBlockNodeWidgetBuilder(),
-                  'tex': TeXBlockNodeWidgetBuidler(),
-                  'horizontal_rule': HorizontalRuleWidgetBuilder(),
-                },
-                shortcutEvents: [
-                  enterInCodeBlock,
-                  ignoreKeysInCodeBlock,
-                  insertHorizontalRule,
-                ],
-                selectionMenuItems: [
-                  codeBlockMenuItem,
-                  teXBlockMenuItem,
-                  horizontalRuleMenuItem,
-                ],
-              ),
+          return Container(
+            color: darkMode ? Colors.black : Colors.white,
+            width: MediaQuery.of(context).size.width,
+            child: AppFlowyEditor(
+              editorState: _editorState!,
+              themeData: themeData,
+              editable: true,
+              customBuilders: {
+                'text/code_block': CodeBlockNodeWidgetBuilder(),
+                'tex': TeXBlockNodeWidgetBuidler(),
+                'horizontal_rule': HorizontalRuleWidgetBuilder(),
+              },
+              shortcutEvents: [
+                enterInCodeBlock,
+                ignoreKeysInCodeBlock,
+                insertHorizontalRule,
+              ],
+              selectionMenuItems: [
+                codeBlockMenuItem,
+                teXBlockMenuItem,
+                horizontalRuleMenuItem,
+              ],
             ),
           );
         } else {
@@ -207,8 +204,6 @@ class _MyHomePageState extends State<MyHomePage> {
           icon: const Icon(Icons.color_lens),
           onPressed: () {
             setState(() {
-              _editorStyle =
-                  darkMode ? EditorStyle.defaultStyle() : _customizedStyle();
               darkMode = !darkMode;
             });
           },
@@ -277,44 +272,4 @@ class _MyHomePageState extends State<MyHomePage> {
       });
     }
   }
-
-  EditorStyle _customizedStyle() {
-    final editorStyle = EditorStyle.defaultStyle();
-    return editorStyle.copyWith(
-      cursorColor: Colors.white,
-      selectionColor: Colors.blue.withOpacity(0.3),
-      textStyle: editorStyle.textStyle.copyWith(
-        defaultTextStyle: GoogleFonts.poppins().copyWith(
-          color: Colors.white,
-          fontSize: 14.0,
-        ),
-        defaultPlaceholderTextStyle: GoogleFonts.poppins().copyWith(
-          color: Colors.white.withOpacity(0.5),
-          fontSize: 14.0,
-        ),
-        bold: const TextStyle(fontWeight: FontWeight.w900),
-        code: TextStyle(
-          fontStyle: FontStyle.italic,
-          color: Colors.red[300],
-          backgroundColor: Colors.grey.withOpacity(0.3),
-        ),
-        highlightColorHex: '0x6FFFEB3B',
-      ),
-      pluginStyles: {
-        'text/quote': builtInPluginStyle
-          ..update(
-            'textStyle',
-            (_) {
-              return (EditorState editorState, Node node) {
-                return TextStyle(
-                  color: Colors.blue[200],
-                  fontStyle: FontStyle.italic,
-                  fontSize: 12.0,
-                );
-              };
-            },
-          ),
-      },
-    );
-  }
 }

+ 1 - 1
frontend/app_flowy/packages/appflowy_editor/example/lib/plugin/code_block_node_widget.dart

@@ -167,7 +167,7 @@ class __CodeBlockNodeWidgeState extends State<_CodeBlockNodeWidge>
         textNode: widget.textNode,
         editorState: widget.editorState,
         textSpanDecorator: (textSpan) => TextSpan(
-          style: widget.editorState.editorStyle.textStyle.defaultTextStyle,
+          style: widget.editorState.editorStyle.textStyle,
           children: codeTextSpan,
         ),
       ),

+ 4 - 3
frontend/app_flowy/packages/appflowy_editor/lib/src/editor_state.dart

@@ -59,13 +59,14 @@ class EditorState {
   /// Stores the selection menu items.
   List<SelectionMenuItem> selectionMenuItems = [];
 
-  /// Stores the editor style.
-  EditorStyle editorStyle = EditorStyle.defaultStyle();
-
   /// Operation stream.
   Stream<Transaction> get transactionStream => _observer.stream;
   final StreamController<Transaction> _observer = StreamController.broadcast();
 
+  late ThemeData themeData;
+  EditorStyle get editorStyle =>
+      themeData.extension<EditorStyle>() ?? EditorStyle.light;
+
   final UndoManager undoManager = UndoManager();
   Selection? _cursorSelection;
 

+ 0 - 50
frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/built_in_text_widget.dart

@@ -10,56 +10,6 @@ abstract class BuiltInTextWidget extends StatefulWidget {
   TextNode get textNode;
 }
 
-mixin BuiltInStyleMixin<T extends BuiltInTextWidget> on State<T> {
-  EdgeInsets get padding {
-    final padding = widget.editorState.editorStyle.style(
-      widget.editorState,
-      widget.textNode,
-      'padding',
-    );
-    if (padding is EdgeInsets) {
-      return padding;
-    }
-    return const EdgeInsets.all(0);
-  }
-
-  TextStyle get textStyle {
-    final textStyle = widget.editorState.editorStyle.style(
-      widget.editorState,
-      widget.textNode,
-      'textStyle',
-    );
-    if (textStyle is TextStyle) {
-      return textStyle;
-    }
-    return const TextStyle();
-  }
-
-  Size? get iconSize {
-    final iconSize = widget.editorState.editorStyle.style(
-      widget.editorState,
-      widget.textNode,
-      'iconSize',
-    );
-    if (iconSize is Size) {
-      return iconSize;
-    }
-    return const Size.square(18.0);
-  }
-
-  EdgeInsets? get iconPadding {
-    final iconPadding = widget.editorState.editorStyle.style(
-      widget.editorState,
-      widget.textNode,
-      'iconPadding',
-    );
-    if (iconPadding is EdgeInsets) {
-      return iconPadding;
-    }
-    return const EdgeInsets.all(0);
-  }
-}
-
 mixin BuiltInTextWidgetMixin<T extends BuiltInTextWidget> on State<T>
     implements DefaultSelectable {
   @override

+ 3 - 2
frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/bulleted_list_text.dart

@@ -61,7 +61,8 @@ class _BulletedListTextNodeWidgetState extends State<BulletedListTextNodeWidget>
   }
 
   BulletedListPluginStyle get style =>
-      Theme.of(context).extension<BulletedListPluginStyle>()!;
+      Theme.of(context).extension<BulletedListPluginStyle>() ??
+      BulletedListPluginStyle.light;
 
   EdgeInsets get padding => style.padding(
         widget.editorState,
@@ -97,7 +98,7 @@ class _BulletedListTextNodeWidgetState extends State<BulletedListTextNodeWidget>
                   textSpan.updateTextStyle(textStyle),
               placeholderTextSpanDecorator: (textSpan) =>
                   textSpan.updateTextStyle(textStyle),
-              lineHeight: widget.editorState.editorStyle.textStyle.lineHeight,
+              lineHeight: widget.editorState.editorStyle.lineHeight,
               textNode: widget.textNode,
               editorState: widget.editorState,
             ),

+ 3 - 2
frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/checkbox_text.dart

@@ -54,7 +54,8 @@ class _CheckboxNodeWidgetState extends State<CheckboxNodeWidget>
   }
 
   CheckboxPluginStyle get style =>
-      Theme.of(context).extension<CheckboxPluginStyle>()!;
+      Theme.of(context).extension<CheckboxPluginStyle>() ??
+      CheckboxPluginStyle.light;
 
   EdgeInsets get padding => style.padding(
         widget.editorState,
@@ -94,7 +95,7 @@ class _CheckboxNodeWidgetState extends State<CheckboxNodeWidget>
             child: FlowyRichText(
               key: _richTextKey,
               placeholderText: 'To-do',
-              lineHeight: widget.editorState.editorStyle.textStyle.lineHeight,
+              lineHeight: widget.editorState.editorStyle.lineHeight,
               textNode: widget.textNode,
               textSpanDecorator: (textSpan) =>
                   textSpan.updateTextStyle(textStyle),

+ 5 - 4
frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/flowy_rich_text.dart

@@ -202,12 +202,13 @@ class _FlowyRichTextState extends State<FlowyRichText> with SelectableMixin {
   }
 
   TextSpan get _placeholderTextSpan {
-    final style = widget.editorState.editorStyle.textStyle;
+    final placeholderTextStyle =
+        widget.editorState.editorStyle.placeholderTextStyle;
     return TextSpan(
       children: [
         TextSpan(
           text: widget.placeholderText,
-          style: style.defaultPlaceholderTextStyle,
+          style: placeholderTextStyle,
         ),
       ],
     );
@@ -216,10 +217,10 @@ class _FlowyRichTextState extends State<FlowyRichText> with SelectableMixin {
   TextSpan get _textSpan {
     var offset = 0;
     List<TextSpan> textSpans = [];
-    final style = widget.editorState.editorStyle.textStyle;
+    final style = widget.editorState.editorStyle;
     final textInserts = widget.textNode.delta.whereType<TextInsert>();
     for (final textInsert in textInserts) {
-      var textStyle = style.defaultTextStyle;
+      var textStyle = style.textStyle!;
       GestureRecognizer? recognizer;
       final attributes = textInsert.attributes;
       if (attributes != null) {

+ 3 - 2
frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/heading_text.dart

@@ -60,7 +60,8 @@ class _HeadingTextNodeWidgetState extends State<HeadingTextNodeWidget>
   }
 
   HeadingPluginStyle get style =>
-      Theme.of(context).extension<HeadingPluginStyle>()!;
+      Theme.of(context).extension<HeadingPluginStyle>() ??
+      HeadingPluginStyle.light;
 
   EdgeInsets get padding => style.padding(
         widget.editorState,
@@ -82,7 +83,7 @@ class _HeadingTextNodeWidgetState extends State<HeadingTextNodeWidget>
         placeholderTextSpanDecorator: (textSpan) =>
             textSpan.updateTextStyle(textStyle),
         textSpanDecorator: (textSpan) => textSpan.updateTextStyle(textStyle),
-        lineHeight: widget.editorState.editorStyle.textStyle.lineHeight,
+        lineHeight: widget.editorState.editorStyle.lineHeight,
         textNode: widget.textNode,
         editorState: widget.editorState,
       ),

+ 4 - 15
frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/number_list_text.dart

@@ -4,7 +4,7 @@ import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart';
 import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart';
 import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart';
 import 'package:appflowy_editor/src/render/selection/selectable.dart';
-import 'package:appflowy_editor/src/render/style/plugin_style.dart';
+import 'package:appflowy_editor/src/render/style/built_in_plugin_styles.dart';
 import 'package:appflowy_editor/src/service/render_plugin_service.dart';
 import 'package:flutter/material.dart';
 import 'package:appflowy_editor/src/extensions/attributes_extension.dart';
@@ -59,20 +59,9 @@ class _NumberListTextNodeWidgetState extends State<NumberListTextNodeWidget>
     return super.baseOffset.translate(0, padding.top);
   }
 
-  Color get numberColor {
-    final numberColor = widget.editorState.editorStyle.style(
-      widget.editorState,
-      widget.textNode,
-      'numberColor',
-    );
-    if (numberColor is Color) {
-      return numberColor;
-    }
-    return Colors.black;
-  }
-
   NumberListPluginStyle get style =>
-      Theme.of(context).extension<NumberListPluginStyle>()!;
+      Theme.of(context).extension<NumberListPluginStyle>() ??
+      NumberListPluginStyle.light;
 
   EdgeInsets get padding => style.padding(
         widget.editorState,
@@ -106,7 +95,7 @@ class _NumberListTextNodeWidgetState extends State<NumberListTextNodeWidget>
               placeholderText: 'List',
               textNode: widget.textNode,
               editorState: widget.editorState,
-              lineHeight: widget.editorState.editorStyle.textStyle.lineHeight,
+              lineHeight: widget.editorState.editorStyle.lineHeight,
               placeholderTextSpanDecorator: (textSpan) =>
                   textSpan.updateTextStyle(textStyle),
               textSpanDecorator: (textSpan) =>

+ 3 - 2
frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/quoted_text.dart

@@ -60,7 +60,8 @@ class _QuotedTextNodeWidgetState extends State<QuotedTextNodeWidget>
   }
 
   QuotedTextPluginStyle get style =>
-      Theme.of(context).extension<QuotedTextPluginStyle>()!;
+      Theme.of(context).extension<QuotedTextPluginStyle>() ??
+      QuotedTextPluginStyle.light;
 
   EdgeInsets get padding => style.padding(
         widget.editorState,
@@ -98,7 +99,7 @@ class _QuotedTextNodeWidgetState extends State<QuotedTextNodeWidget>
                     textSpan.updateTextStyle(textStyle),
                 placeholderTextSpanDecorator: (textSpan) =>
                     textSpan.updateTextStyle(textStyle),
-                lineHeight: widget.editorState.editorStyle.textStyle.lineHeight,
+                lineHeight: widget.editorState.editorStyle.lineHeight,
                 editorState: widget.editorState,
               ),
             ),

+ 11 - 8
frontend/app_flowy/packages/appflowy_editor/lib/src/render/rich_text/rich_text.dart

@@ -4,6 +4,7 @@ import 'package:appflowy_editor/src/render/rich_text/built_in_text_widget.dart';
 import 'package:appflowy_editor/src/render/rich_text/default_selectable.dart';
 import 'package:appflowy_editor/src/render/rich_text/flowy_rich_text.dart';
 import 'package:appflowy_editor/src/render/selection/selectable.dart';
+import 'package:appflowy_editor/src/render/style/editor_style.dart';
 import 'package:appflowy_editor/src/service/render_plugin_service.dart';
 import 'package:flutter/material.dart';
 import 'package:appflowy_editor/src/extensions/text_style_extension.dart';
@@ -43,11 +44,7 @@ class RichTextNodeWidget extends BuiltInTextWidget {
 // customize
 
 class _RichTextNodeWidgetState extends State<RichTextNodeWidget>
-    with
-        SelectableMixin,
-        DefaultSelectable,
-        BuiltInStyleMixin,
-        BuiltInTextWidgetMixin {
+    with SelectableMixin, DefaultSelectable, BuiltInTextWidgetMixin {
   @override
   GlobalKey? get iconKey => null;
 
@@ -59,20 +56,26 @@ class _RichTextNodeWidgetState extends State<RichTextNodeWidget>
 
   @override
   Offset get baseOffset {
-    return padding.topLeft;
+    return textPadding.topLeft;
   }
 
+  EditorStyle get style => widget.editorState.editorStyle;
+
+  EdgeInsets get textPadding => style.textPadding!;
+
+  TextStyle get textStyle => style.textStyle!;
+
   @override
   Widget buildWithSingle(BuildContext context) {
     return Padding(
-      padding: padding,
+      padding: textPadding,
       child: FlowyRichText(
         key: _richTextKey,
         textNode: widget.textNode,
         textSpanDecorator: (textSpan) => textSpan.updateTextStyle(textStyle),
         placeholderTextSpanDecorator: (textSpan) =>
             textSpan.updateTextStyle(textStyle),
-        lineHeight: widget.editorState.editorStyle.textStyle.lineHeight,
+        lineHeight: widget.editorState.editorStyle.lineHeight,
         editorState: widget.editorState,
       ),
     );

+ 15 - 0
frontend/app_flowy/packages/appflowy_editor/lib/src/render/style/plugin_style.dart → frontend/app_flowy/packages/appflowy_editor/lib/src/render/style/built_in_plugin_styles.dart

@@ -2,6 +2,21 @@ import 'package:appflowy_editor/appflowy_editor.dart';
 import 'package:appflowy_editor/src/infra/flowy_svg.dart';
 import 'package:flutter/material.dart';
 
+Iterable<ThemeExtension<dynamic>> get lightPlguinStyleExtension => [
+      HeadingPluginStyle.light,
+      CheckboxPluginStyle.light,
+      NumberListPluginStyle.light,
+      QuotedTextPluginStyle.light,
+    ];
+
+Iterable<ThemeExtension<dynamic>> get darkPlguinStyleExtension => [
+      HeadingPluginStyle.dark,
+      CheckboxPluginStyle.dark,
+      NumberListPluginStyle.dark,
+      QuotedTextPluginStyle.dark,
+      BulletedListPluginStyle.dark,
+    ];
+
 typedef TextStyleCustomizer = TextStyle Function(
     EditorState editorState, TextNode textNode);
 typedef PaddingCustomizer = EdgeInsets Function(

+ 48 - 266
frontend/app_flowy/packages/appflowy_editor/lib/src/render/style/editor_style.dart

@@ -1,16 +1,21 @@
 import 'package:flutter/material.dart';
 
-import 'package:appflowy_editor/src/core/document/node.dart';
-import 'package:appflowy_editor/src/editor_state.dart';
-import 'package:appflowy_editor/src/extensions/attributes_extension.dart';
+Iterable<ThemeExtension<dynamic>> get lightEditorStyleExtension => [
+      EditorStyle.light,
+    ];
 
-class EditorStyleV2 extends ThemeExtension<EditorStyleV2> {
+Iterable<ThemeExtension<dynamic>> get dartEditorStyleExtension => [
+      EditorStyle.dark,
+    ];
+
+class EditorStyle extends ThemeExtension<EditorStyle> {
   // Editor styles
   final EdgeInsets? padding;
   final Color? cursorColor;
   final Color? selectionColor;
 
   // Text styles
+  final EdgeInsets? textPadding;
   final TextStyle? textStyle;
   final TextStyle? placeholderTextStyle;
   final double lineHeight;
@@ -24,10 +29,11 @@ class EditorStyleV2 extends ThemeExtension<EditorStyleV2> {
   final TextStyle? code;
   final String? highlightColorHex;
 
-  EditorStyleV2({
+  EditorStyle({
     required this.padding,
     required this.cursorColor,
     required this.selectionColor,
+    required this.textPadding,
     required this.textStyle,
     required this.placeholderTextStyle,
     required this.bold,
@@ -41,7 +47,7 @@ class EditorStyleV2 extends ThemeExtension<EditorStyleV2> {
   });
 
   @override
-  EditorStyleV2 copyWith({
+  EditorStyle copyWith({
     EdgeInsets? padding,
     Color? cursorColor,
     Color? selectionColor,
@@ -56,10 +62,11 @@ class EditorStyleV2 extends ThemeExtension<EditorStyleV2> {
     String? highlightColorHex,
     double? lineHeight,
   }) {
-    return EditorStyleV2(
+    return EditorStyle(
       padding: padding ?? this.padding,
       cursorColor: cursorColor ?? this.cursorColor,
       selectionColor: selectionColor ?? this.selectionColor,
+      textPadding: textPadding ?? textPadding,
       textStyle: textStyle ?? this.textStyle,
       placeholderTextStyle: placeholderTextStyle ?? this.placeholderTextStyle,
       bold: bold ?? this.bold,
@@ -74,14 +81,15 @@ class EditorStyleV2 extends ThemeExtension<EditorStyleV2> {
   }
 
   @override
-  ThemeExtension<EditorStyleV2> lerp(
-      ThemeExtension<EditorStyleV2>? other, double t) {
-    if (other == null || other is! EditorStyleV2) {
+  ThemeExtension<EditorStyle> lerp(
+      ThemeExtension<EditorStyle>? other, double t) {
+    if (other == null || other is! EditorStyle) {
       return this;
     }
-    return EditorStyleV2(
+    return EditorStyle(
       padding: EdgeInsets.lerp(padding, other.padding, t),
       cursorColor: Color.lerp(cursorColor, other.cursorColor, t),
+      textPadding: EdgeInsets.lerp(textPadding, other.textPadding, t),
       selectionColor: Color.lerp(selectionColor, other.selectionColor, t),
       textStyle: TextStyle.lerp(textStyle, other.textStyle, t),
       placeholderTextStyle:
@@ -96,262 +104,36 @@ class EditorStyleV2 extends ThemeExtension<EditorStyleV2> {
       lineHeight: lineHeight,
     );
   }
-}
-
-typedef PluginStyler = Object Function(EditorState editorState, Node node);
-typedef PluginStyle = Map<String, PluginStyler>;
-
-/// Editor style configuration
-class EditorStyle {
-  EditorStyle({
-    required this.padding,
-    required this.textStyle,
-    required this.cursorColor,
-    required this.selectionColor,
-    Map<String, PluginStyle> pluginStyles = const {},
-  }) {
-    _pluginStyles.addAll(pluginStyles);
-  }
-
-  EditorStyle.defaultStyle()
-      : padding = const EdgeInsets.fromLTRB(200.0, 0.0, 200.0, 0.0),
-        textStyle = BuiltInTextStyle.builtIn(),
-        cursorColor = const Color(0xFF00BCF0),
-        selectionColor = const Color.fromARGB(53, 111, 201, 231);
-
-  /// The margin of the document context from the editor.
-  final EdgeInsets padding;
-  final BuiltInTextStyle textStyle;
-  final Color cursorColor;
-  final Color selectionColor;
-
-  final Map<String, PluginStyle> _pluginStyles = Map.from(builtInTextStylers);
-
-  Object? style(EditorState editorState, Node node, String key) {
-    final styler = _pluginStyles[node.id]?[key];
-    if (styler != null) {
-      return styler(editorState, node);
-    }
-    return null;
-  }
-
-  @override
-  EditorStyle copyWith({
-    EdgeInsets? padding,
-    BuiltInTextStyle? textStyle,
-    Color? cursorColor,
-    Color? selectionColor,
-    Map<String, PluginStyle>? pluginStyles,
-  }) {
-    return EditorStyle(
-      padding: padding ?? this.padding,
-      textStyle: textStyle ?? this.textStyle,
-      cursorColor: cursorColor ?? this.cursorColor,
-      selectionColor: selectionColor ?? this.selectionColor,
-      pluginStyles: pluginStyles ?? {},
-    );
-  }
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) return true;
-
-    return other is EditorStyle &&
-        other.padding == padding &&
-        other.textStyle == textStyle &&
-        other.cursorColor == cursorColor &&
-        other.selectionColor == selectionColor;
-  }
-
-  @override
-  int get hashCode {
-    return padding.hashCode ^
-        textStyle.hashCode ^
-        cursorColor.hashCode ^
-        selectionColor.hashCode;
-  }
-}
-
-PluginStyle get builtInPluginStyle => Map.from({
-      'padding': (_, __) => const EdgeInsets.symmetric(vertical: 8.0),
-      'textStyle': (_, __) => const TextStyle(),
-      'iconSize': (_, __) => const Size.square(20.0),
-      'iconPadding': (_, __) => const EdgeInsets.only(right: 5.0),
-    });
 
-Map<String, PluginStyle> builtInTextStylers = {
-  'text': builtInPluginStyle,
-  'text/checkbox': builtInPluginStyle
-    ..update(
-      'textStyle',
-      (_) => (EditorState editorState, Node node) {
-        if (node is TextNode && node.attributes.check == true) {
-          return const TextStyle(
-            color: Colors.grey,
-            decoration: TextDecoration.lineThrough,
-          );
-        }
-        return const TextStyle();
-      },
+  static final light = EditorStyle(
+    padding: const EdgeInsets.fromLTRB(200.0, 0.0, 200.0, 0.0),
+    cursorColor: const Color(0xFF00BCF0),
+    selectionColor: const Color.fromARGB(53, 111, 201, 231),
+    textPadding: const EdgeInsets.symmetric(vertical: 8.0),
+    textStyle: const TextStyle(fontSize: 16.0, color: Colors.black),
+    placeholderTextStyle: const TextStyle(fontSize: 16.0, color: Colors.grey),
+    bold: const TextStyle(fontWeight: FontWeight.bold),
+    italic: const TextStyle(fontStyle: FontStyle.italic),
+    underline: const TextStyle(decoration: TextDecoration.underline),
+    strikethrough: const TextStyle(decoration: TextDecoration.lineThrough),
+    href: const TextStyle(
+      color: Colors.blue,
+      decoration: TextDecoration.underline,
     ),
-  'text/heading': builtInPluginStyle
-    ..update(
-      'textStyle',
-      (_) => (EditorState editorState, Node node) {
-        final headingToFontSize = {
-          'h1': 32.0,
-          'h2': 28.0,
-          'h3': 24.0,
-          'h4': 18.0,
-          'h5': 18.0,
-          'h6': 18.0,
-        };
-        final fontSize = headingToFontSize[node.attributes.heading] ?? 18.0;
-        return TextStyle(fontSize: fontSize, fontWeight: FontWeight.bold);
-      },
+    code: const TextStyle(
+      fontFamily: 'monospace',
+      color: Color(0xFF00BCF0),
+      backgroundColor: Color(0xFFE0F8FF),
     ),
-  'text/bulleted-list': builtInPluginStyle,
-  'text/number-list': builtInPluginStyle
-    ..addAll({
-      'numberColor': (EditorState editorState, Node node) {
-        return Colors.black;
-      },
-      'iconPadding': (EditorState editorState, Node node) {
-        return const EdgeInsets.only(left: 5.0, right: 5.0);
-      },
-    }),
-  'text/bulleted-list': builtInPluginStyle
-    ..addAll({
-      'bulletColor': (EditorState editorState, Node node) {
-        return Colors.black;
-      },
-    }),
-  'text/quote': builtInPluginStyle,
-  'image': builtInPluginStyle,
-};
-
-class BuiltInTextStyle {
-  const BuiltInTextStyle({
-    required this.defaultTextStyle,
-    required this.defaultPlaceholderTextStyle,
-    required this.bold,
-    required this.italic,
-    required this.underline,
-    required this.strikethrough,
-    required this.href,
-    required this.code,
-    this.highlightColorHex = '0x6000BCF0',
-    this.lineHeight = 1.5,
-  });
-
-  final TextStyle defaultTextStyle;
-  final TextStyle defaultPlaceholderTextStyle;
-  final TextStyle bold;
-  final TextStyle italic;
-  final TextStyle underline;
-  final TextStyle strikethrough;
-  final TextStyle href;
-  final TextStyle code;
-  final String highlightColorHex;
-  final double lineHeight;
-
-  BuiltInTextStyle.builtIn()
-      : defaultTextStyle = const TextStyle(fontSize: 16.0, color: Colors.black),
-        defaultPlaceholderTextStyle =
-            const TextStyle(fontSize: 16.0, color: Colors.grey),
-        bold = const TextStyle(fontWeight: FontWeight.bold),
-        italic = const TextStyle(fontStyle: FontStyle.italic),
-        underline = const TextStyle(decoration: TextDecoration.underline),
-        strikethrough = const TextStyle(decoration: TextDecoration.lineThrough),
-        href = const TextStyle(
-          color: Colors.blue,
-          decoration: TextDecoration.underline,
-        ),
-        code = const TextStyle(
-          fontFamily: 'monospace',
-          color: Color(0xFF00BCF0),
-          backgroundColor: Color(0xFFE0F8FF),
-        ),
-        highlightColorHex = '0x6000BCF0',
-        lineHeight = 1.5;
-
-  BuiltInTextStyle.builtInDarkMode()
-      : defaultTextStyle = const TextStyle(fontSize: 16.0, color: Colors.white),
-        defaultPlaceholderTextStyle = TextStyle(
-          fontSize: 16.0,
-          color: Colors.white.withOpacity(0.3),
-        ),
-        bold = const TextStyle(fontWeight: FontWeight.bold),
-        italic = const TextStyle(fontStyle: FontStyle.italic),
-        underline = const TextStyle(decoration: TextDecoration.underline),
-        strikethrough = const TextStyle(decoration: TextDecoration.lineThrough),
-        href = const TextStyle(
-          color: Colors.blue,
-          decoration: TextDecoration.underline,
-        ),
-        code = const TextStyle(
-          fontFamily: 'monospace',
-          color: Color(0xFF00BCF0),
-          backgroundColor: Color(0xFFE0F8FF),
-        ),
-        highlightColorHex = '0x6000BCF0',
-        lineHeight = 1.5;
-
-  BuiltInTextStyle copyWith({
-    TextStyle? defaultTextStyle,
-    TextStyle? defaultPlaceholderTextStyle,
-    TextStyle? bold,
-    TextStyle? italic,
-    TextStyle? underline,
-    TextStyle? strikethrough,
-    TextStyle? href,
-    TextStyle? code,
-    String? highlightColorHex,
-    double? lineHeight,
-  }) {
-    return BuiltInTextStyle(
-      defaultTextStyle: defaultTextStyle ?? this.defaultTextStyle,
-      defaultPlaceholderTextStyle:
-          defaultPlaceholderTextStyle ?? this.defaultPlaceholderTextStyle,
-      bold: bold ?? this.bold,
-      italic: italic ?? this.italic,
-      underline: underline ?? this.underline,
-      strikethrough: strikethrough ?? this.strikethrough,
-      href: href ?? this.href,
-      code: code ?? this.code,
-      highlightColorHex: highlightColorHex ?? this.highlightColorHex,
-      lineHeight: lineHeight ?? this.lineHeight,
-    );
-  }
-
-  @override
-  bool operator ==(Object other) {
-    if (identical(this, other)) return true;
-
-    return other is BuiltInTextStyle &&
-        other.defaultTextStyle == defaultTextStyle &&
-        other.defaultPlaceholderTextStyle == defaultPlaceholderTextStyle &&
-        other.bold == bold &&
-        other.italic == italic &&
-        other.underline == underline &&
-        other.strikethrough == strikethrough &&
-        other.href == href &&
-        other.code == code &&
-        other.highlightColorHex == highlightColorHex &&
-        other.lineHeight == lineHeight;
-  }
-
-  @override
-  int get hashCode {
-    return defaultTextStyle.hashCode ^
-        defaultPlaceholderTextStyle.hashCode ^
-        bold.hashCode ^
-        italic.hashCode ^
-        underline.hashCode ^
-        strikethrough.hashCode ^
-        href.hashCode ^
-        code.hashCode ^
-        highlightColorHex.hashCode ^
-        lineHeight.hashCode;
-  }
+    highlightColorHex: '0x6000BCF0',
+    lineHeight: 1.5,
+  );
+
+  static final dark = light.copyWith(
+    textStyle: const TextStyle(fontSize: 16.0, color: Colors.white),
+    placeholderTextStyle: TextStyle(
+      fontSize: 16.0,
+      color: Colors.white.withOpacity(0.3),
+    ),
+  );
 }

+ 1 - 1
frontend/app_flowy/packages/appflowy_editor/lib/src/render/toolbar/toolbar_item.dart

@@ -259,7 +259,7 @@ List<ToolbarItem> defaultToolbarItems = [
     ),
     handler: (editorState, context) => formatHighlight(
       editorState,
-      editorState.editorStyle.textStyle.highlightColorHex,
+      editorState.editorStyle.highlightColorHex!,
     ),
   ),
 ];

+ 46 - 44
frontend/app_flowy/packages/appflowy_editor/lib/src/service/editor_service.dart

@@ -1,12 +1,9 @@
+import 'package:appflowy_editor/appflowy_editor.dart';
 import 'package:appflowy_editor/src/flutter/overlay.dart';
 import 'package:appflowy_editor/src/render/image/image_node_builder.dart';
-import 'package:appflowy_editor/src/render/selection_menu/selection_menu_widget.dart';
-import 'package:appflowy_editor/src/render/style/editor_style.dart';
 import 'package:appflowy_editor/src/service/shortcut_event/built_in_shortcut_events.dart';
-import 'package:appflowy_editor/src/service/shortcut_event/shortcut_event.dart';
 import 'package:flutter/material.dart' hide Overlay, OverlayEntry;
 
-import 'package:appflowy_editor/src/editor_state.dart';
 import 'package:appflowy_editor/src/render/editor/editor_entry.dart';
 import 'package:appflowy_editor/src/render/rich_text/bulleted_list_text.dart';
 import 'package:appflowy_editor/src/render/rich_text/checkbox_text.dart';
@@ -14,12 +11,6 @@ import 'package:appflowy_editor/src/render/rich_text/heading_text.dart';
 import 'package:appflowy_editor/src/render/rich_text/number_list_text.dart';
 import 'package:appflowy_editor/src/render/rich_text/quoted_text.dart';
 import 'package:appflowy_editor/src/render/rich_text/rich_text.dart';
-import 'package:appflowy_editor/src/service/input_service.dart';
-import 'package:appflowy_editor/src/service/keyboard_service.dart';
-import 'package:appflowy_editor/src/service/render_plugin_service.dart';
-import 'package:appflowy_editor/src/service/scroll_service.dart';
-import 'package:appflowy_editor/src/service/selection_service.dart';
-import 'package:appflowy_editor/src/service/toolbar_service.dart';
 
 NodeWidgetBuilders defaultBuilders = {
   'editor': EditorEntryWidgetBuilder(),
@@ -33,15 +24,21 @@ NodeWidgetBuilders defaultBuilders = {
 };
 
 class AppFlowyEditor extends StatefulWidget {
-  const AppFlowyEditor({
+  AppFlowyEditor({
     Key? key,
     required this.editorState,
     this.customBuilders = const {},
     this.shortcutEvents = const [],
     this.selectionMenuItems = const [],
     this.editable = true,
-    required this.editorStyle,
-  }) : super(key: key);
+    ThemeData? themeData,
+  }) : super(key: key) {
+    this.themeData = themeData ??
+        ThemeData.light().copyWith(extensions: [
+          ...lightEditorStyleExtension,
+          ...lightPlguinStyleExtension,
+        ]);
+  }
 
   final EditorState editorState;
 
@@ -53,7 +50,7 @@ class AppFlowyEditor extends StatefulWidget {
 
   final List<SelectionMenuItem> selectionMenuItems;
 
-  final EditorStyle editorStyle;
+  late final ThemeData themeData;
 
   final bool editable;
 
@@ -65,13 +62,15 @@ class _AppFlowyEditorState extends State<AppFlowyEditor> {
   Widget? services;
 
   EditorState get editorState => widget.editorState;
+  EditorStyle get editorStyle =>
+      editorState.themeData.extension<EditorStyle>() ?? EditorStyle.light;
 
   @override
   void initState() {
     super.initState();
 
     editorState.selectionMenuItems = widget.selectionMenuItems;
-    editorState.editorStyle = widget.editorStyle;
+    editorState.themeData = widget.themeData;
     editorState.service.renderPluginService = _createRenderPlugin();
     editorState.editable = widget.editable;
   }
@@ -85,7 +84,7 @@ class _AppFlowyEditorState extends State<AppFlowyEditor> {
       editorState.service.renderPluginService = _createRenderPlugin();
     }
 
-    editorState.editorStyle = widget.editorStyle;
+    editorState.themeData = widget.themeData;
     editorState.editable = widget.editable;
     services = null;
   }
@@ -102,38 +101,41 @@ class _AppFlowyEditorState extends State<AppFlowyEditor> {
     );
   }
 
-  AppFlowyScroll _buildServices(BuildContext context) {
-    return AppFlowyScroll(
-      key: editorState.service.scrollServiceKey,
-      child: Padding(
-        padding: widget.editorStyle.padding,
-        child: AppFlowySelection(
-          key: editorState.service.selectionServiceKey,
-          cursorColor: widget.editorStyle.cursorColor,
-          selectionColor: widget.editorStyle.selectionColor,
-          editorState: editorState,
-          editable: widget.editable,
-          child: AppFlowyInput(
-            key: editorState.service.inputServiceKey,
+  Widget _buildServices(BuildContext context) {
+    return Theme(
+      data: widget.themeData,
+      child: AppFlowyScroll(
+        key: editorState.service.scrollServiceKey,
+        child: Padding(
+          padding: editorStyle.padding!,
+          child: AppFlowySelection(
+            key: editorState.service.selectionServiceKey,
+            cursorColor: editorStyle.cursorColor!,
+            selectionColor: editorStyle.selectionColor!,
             editorState: editorState,
             editable: widget.editable,
-            child: AppFlowyKeyboard(
-              key: editorState.service.keyboardServiceKey,
-              editable: widget.editable,
-              shortcutEvents: [
-                ...widget.shortcutEvents,
-                ...builtInShortcutEvents,
-              ],
+            child: AppFlowyInput(
+              key: editorState.service.inputServiceKey,
               editorState: editorState,
-              child: FlowyToolbar(
-                key: editorState.service.toolbarServiceKey,
+              editable: widget.editable,
+              child: AppFlowyKeyboard(
+                key: editorState.service.keyboardServiceKey,
+                editable: widget.editable,
+                shortcutEvents: [
+                  ...widget.shortcutEvents,
+                  ...builtInShortcutEvents,
+                ],
                 editorState: editorState,
-                child:
-                    editorState.service.renderPluginService.buildPluginWidget(
-                  NodeWidgetContext(
-                    context: context,
-                    node: editorState.document.root,
-                    editorState: editorState,
+                child: FlowyToolbar(
+                  key: editorState.service.toolbarServiceKey,
+                  editorState: editorState,
+                  child:
+                      editorState.service.renderPluginService.buildPluginWidget(
+                    NodeWidgetContext(
+                      context: context,
+                      node: editorState.document.root,
+                      editorState: editorState,
+                    ),
                   ),
                 ),
               ),

+ 1 - 1
frontend/app_flowy/packages/appflowy_editor/lib/src/service/internal_key_event_handlers/format_style_handler.dart

@@ -57,7 +57,7 @@ ShortcutEventHandler formatHighlightEventHandler = (editorState, event) {
   }
   formatHighlight(
     editorState,
-    editorState.editorStyle.textStyle.highlightColorHex,
+    editorState.editorStyle.highlightColorHex!,
   );
   return KeyEventResult.handled;
 };

+ 0 - 1
frontend/app_flowy/packages/appflowy_editor/test/infra/test_editor.dart

@@ -39,7 +39,6 @@ class EditorWidgetTester {
       home: Scaffold(
         body: AppFlowyEditor(
           editorState: _editorState,
-          editorStyle: EditorStyle.defaultStyle(),
         ),
       ),
     );

+ 3 - 2
frontend/app_flowy/packages/appflowy_editor/test/render/image/image_node_builder_test.dart

@@ -49,10 +49,11 @@ void main() async {
         final editorRect = tester.getRect(editorFinder);
 
         final leftImageRect = tester.getRect(imageFinder.at(0));
-        expect(leftImageRect.left, editor.editorState.editorStyle.padding.left);
+        expect(
+            leftImageRect.left, editor.editorState.editorStyle.padding!.left);
         final rightImageRect = tester.getRect(imageFinder.at(2));
         expect(rightImageRect.right,
-            editorRect.right - editor.editorState.editorStyle.padding.right);
+            editorRect.right - editor.editorState.editorStyle.padding!.right);
         final centerImageRect = tester.getRect(imageFinder.at(1));
         expect(centerImageRect.left,
             (leftImageRect.left + rightImageRect.left) / 2.0);