Procházet zdrojové kódy

feat: flowy_rich_text supports placeholder and customizes placeholder style

Lucas.Xu před 2 roky
rodič
revize
f6fbe55477

+ 1 - 0
frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/bulleted_list_text.dart

@@ -64,6 +64,7 @@ class _BulletedListTextNodeWidgetState extends State<BulletedListTextNodeWidget>
         ),
         FlowyRichText(
           key: _richTextKey,
+          placeholderText: 'List',
           textNode: widget.textNode,
           editorState: widget.editorState,
         ),

+ 1 - 0
frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/checkbox_text.dart

@@ -84,6 +84,7 @@ class _CheckboxNodeWidgetState extends State<CheckboxNodeWidget>
         ),
         FlowyRichText(
           key: _richTextKey,
+          placeholderText: 'To-do',
           textNode: widget.textNode,
           editorState: widget.editorState,
         )

+ 40 - 5
frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/flowy_rich_text.dart

@@ -35,15 +35,19 @@ class FlowyRichText extends StatefulWidget {
     this.cursorHeight,
     this.cursorWidth = 2.0,
     this.textSpanDecorator,
+    this.placeholderText = ' ',
+    this.placeholderTextSpanDecorator,
     required this.textNode,
     required this.editorState,
   }) : super(key: key);
 
-  final double? cursorHeight;
-  final double cursorWidth;
   final TextNode textNode;
   final EditorState editorState;
+  final double? cursorHeight;
+  final double cursorWidth;
   final FlowyTextSpanDecorator? textSpanDecorator;
+  final String placeholderText;
+  final FlowyTextSpanDecorator? placeholderTextSpanDecorator;
 
   @override
   State<FlowyRichText> createState() => _FlowyRichTextState();
@@ -51,10 +55,14 @@ class FlowyRichText extends StatefulWidget {
 
 class _FlowyRichTextState extends State<FlowyRichText> with Selectable {
   final _textKey = GlobalKey();
+  final _placeholderTextKey = GlobalKey();
 
   RenderParagraph get _renderParagraph =>
       _textKey.currentContext?.findRenderObject() as RenderParagraph;
 
+  RenderParagraph get _placeholderRenderParagraph =>
+      _placeholderTextKey.currentContext?.findRenderObject() as RenderParagraph;
+
   @override
   Widget build(BuildContext context) {
     return _buildRichText(context);
@@ -74,6 +82,7 @@ class _FlowyRichTextState extends State<FlowyRichText> with Selectable {
         _renderParagraph.getOffsetForCaret(textPosition, Rect.zero);
     final cursorHeight = widget.cursorHeight ??
         _renderParagraph.getFullHeightForCaret(textPosition) ??
+        _placeholderRenderParagraph.getFullHeightForCaret(textPosition) ??
         18.0; // default height
     return Rect.fromLTWH(
       cursorOffset.dx - (widget.cursorWidth / 2),
@@ -129,9 +138,35 @@ class _FlowyRichTextState extends State<FlowyRichText> with Selectable {
   }
 
   Widget _buildRichText(BuildContext context) {
-    return Align(
-      alignment: Alignment.centerLeft,
-      child: _buildSingleRichText(context),
+    return Stack(
+      children: [
+        _buildPlaceholderText(context),
+        Align(
+          alignment: Alignment.centerLeft,
+          child: _buildSingleRichText(context),
+        )
+      ],
+    );
+  }
+
+  Widget _buildPlaceholderText(BuildContext context) {
+    final textSpan = TextSpan(
+      children: [
+        TextSpan(
+          text: widget.placeholderText,
+          style: TextStyle(
+            color: widget.textNode.toRawString().isNotEmpty
+                ? Colors.transparent
+                : Colors.grey,
+          ),
+        ),
+      ],
+    );
+    return RichText(
+      key: _placeholderTextKey,
+      text: widget.placeholderTextSpanDecorator != null
+          ? widget.placeholderTextSpanDecorator!(textSpan)
+          : textSpan,
     );
   }
 

+ 19 - 0
frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/heading_text.dart

@@ -65,6 +65,8 @@ class _HeadingTextNodeWidgetState extends State<HeadingTextNodeWidget>
           ),
           child: FlowyRichText(
             key: _richTextKey,
+            placeholderText: 'Heading',
+            placeholderTextSpanDecorator: _placeholderTextSpanDecorator,
             textSpanDecorator: _textSpanDecorator,
             textNode: widget.textNode,
             editorState: widget.editorState,
@@ -90,4 +92,21 @@ class _HeadingTextNodeWidgetState extends State<HeadingTextNodeWidget>
           .toList(),
     );
   }
+
+  TextSpan _placeholderTextSpanDecorator(TextSpan textSpan) {
+    return TextSpan(
+      children: textSpan.children
+          ?.whereType<TextSpan>()
+          .map(
+            (span) => TextSpan(
+              text: span.text,
+              style: span.style?.copyWith(
+                fontSize: widget.textNode.attributes.fontSize,
+              ),
+              recognizer: span.recognizer,
+            ),
+          )
+          .toList(),
+    );
+  }
 }

+ 1 - 0
frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/number_list_text.dart

@@ -65,6 +65,7 @@ class _NumberListTextNodeWidgetState extends State<NumberListTextNodeWidget>
         ),
         FlowyRichText(
           key: _richTextKey,
+          placeholderText: 'List',
           textNode: widget.textNode,
           editorState: widget.editorState,
         ),

+ 1 - 0
frontend/app_flowy/packages/flowy_editor/lib/render/rich_text/quoted_text.dart

@@ -64,6 +64,7 @@ class _QuotedTextNodeWidgetState extends State<QuotedTextNodeWidget>
         ),
         FlowyRichText(
           key: _richTextKey,
+          placeholderText: 'Quote',
           textNode: widget.textNode,
           editorState: widget.editorState,
         ),