From 32d9d9a4f985cc093a75caa29d19ed030d20b880 Mon Sep 17 00:00:00 2001 From: Ellet <73608287+freshtechtips@users.noreply.github.com> Date: Mon, 6 Nov 2023 02:57:34 +0300 Subject: [PATCH] Update README.md --- README.md | 285 ++----------------------------------- doc/custom_embed_blocks.md | 124 ++++++++++++++++ doc/custom_toolbar.md | 143 +++++++++++++++++++ 3 files changed, 277 insertions(+), 275 deletions(-) create mode 100644 doc/custom_embed_blocks.md create mode 100644 doc/custom_toolbar.md diff --git a/README.md b/README.md index 63e43be0..65305d67 100644 --- a/README.md +++ b/README.md @@ -294,270 +294,13 @@ Define `width`, `height`, `margin`, `alignment` as follows: Sometimes you want to add some custom content inside your text, custom widgets inside of them. An example is adding notes to the text, or anything custom that you want to add in your text editor. -The only thing that you need is to add a `CustomBlockEmbed` and provider a builder for it to the `embedBuilders` parameter, to transform the data inside of the Custom Block into a widget! - -Here is an example: - -Starting with the `CustomBlockEmbed`, here we extend it and add the methods that are useful for the 'Note' widget, that will be the `Document`, used by the `flutter_quill` to render the rich text. - -```dart -class NotesBlockEmbed extends CustomBlockEmbed { - const NotesBlockEmbed(String value) : super(noteType, value); - - static const String noteType = 'notes'; - - static NotesBlockEmbed fromDocument(Document document) => - NotesBlockEmbed(jsonEncode(document.toDelta().toJson())); - - Document get document => Document.fromJson(jsonDecode(data)); -} -``` - -After that, we need to map this "notes" type into a widget. In that case, I used a `ListTile` with a text to show the plain text resume of the note, and the `onTap` function to edit the note. -Don't forget to add this method to the `QuillEditor` after that! - -```dart -class NotesEmbedBuilder extends EmbedBuilder { - NotesEmbedBuilder({required this.addEditNote}); - - Future Function(BuildContext context, {Document? document}) addEditNote; - - @override - String get key => 'notes'; - - @override - Widget build( - BuildContext context, - QuillController controller, - Embed node, - bool readOnly, - bool inline, - TextStyle textStyle, - ) { - final notes = NotesBlockEmbed(node.value.data).document; - - return Material( - color: Colors.transparent, - child: ListTile( - title: Text( - notes.toPlainText().replaceAll('\n', ' '), - maxLines: 3, - overflow: TextOverflow.ellipsis, - ), - leading: const Icon(Icons.notes), - onTap: () => addEditNote(context, document: notes), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(10), - side: const BorderSide(color: Colors.grey), - ), - ), - ); - } -} -``` - -And finally, we write the function to add/edit this note. The `showDialog` function shows the QuillEditor to edit the note, after the user ends the edition, we check if the document has something, and if it has, we add or edit the `NotesBlockEmbed` inside of a `BlockEmbed.custom` (this is a little detail that will not work if you don't pass the `CustomBlockEmbed` inside of a `BlockEmbed.custom`). - -```dart -Future _addEditNote(BuildContext context, {Document? document}) async { - final isEditing = document != null; - final quillEditorController = QuillController( - document: document ?? Document(), - selection: const TextSelection.collapsed(offset: 0), - ); - - await showDialog( - context: context, - builder: (context) => AlertDialog( - titlePadding: const EdgeInsets.only(left: 16, top: 8), - title: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text('${isEditing ? 'Edit' : 'Add'} note'), - IconButton( - onPressed: () => Navigator.of(context).pop(), - icon: const Icon(Icons.close), - ) - ], - ), - content: QuillEditor.basic( - controller: quillEditorController, - readOnly: false, - ), - ), - ); - - if (quillEditorController.document.isEmpty()) return; - - final block = BlockEmbed.custom( - NotesBlockEmbed.fromDocument(quillEditorController.document), - ); - final controller = _controller!; - final index = controller.selection.baseOffset; - final length = controller.selection.extentOffset - index; - - if (isEditing) { - final offset = getEmbedNode(controller, controller.selection.start).offset; - controller.replaceText( - offset, 1, block, TextSelection.collapsed(offset: offset)); - } else { - controller.replaceText(index, length, block, null); - } -} -``` - -And voila, we have a custom widget inside of the rich text editor! - -

- 1 -

- -> 1. For more info and a video example, see the [PR of this feature](https://github.com/singerdmx/flutter-quill/pull/877) -> 2. For more details, check out [this YouTube video](https://youtu.be/pI5p5j7cfHc) +Open this [page](./doc/custom_embed_blocks.dart) for more info ### Custom Toolbar If you want to use custom toolbar but still want the support of this libray -You can use the `QuillBaseToolbar` which is the base for the `QuillToolbar` - -> If you are using the toolbar buttons like `QuillToolbarHistoryButton`, `QuillToolbarToggleStyleButton` in the somewhere like the the custom toolbar then you must provide them with `QuillToolbarProvider` inherited widget, you don't have to do this if you are using the `QuillToolbar` since it will be done for you - -Example: - -```dart -QuillProvider( - configurations: QuillConfigurations( - controller: _controller, - sharedConfigurations: const QuillSharedConfigurations(), - ), - child: Column( - children: [ - QuillToolbarProvider( - toolbarConfigurations: const QuillToolbarConfigurations(), - child: QuillBaseToolbar( - configurations: QuillBaseToolbarConfigurations( - toolbarSize: 15 * 2, - multiRowsDisplay: false, - childrenBuilder: (context) { - final controller = context.requireQuillController; - return [ - QuillToolbarHistoryButton( - controller: controller, - options: const QuillToolbarHistoryButtonOptions( - isUndo: true), - ), - QuillToolbarHistoryButton( - controller: controller, - options: const QuillToolbarHistoryButtonOptions( - isUndo: false), - ), - QuillToolbarToggleStyleButton( - attribute: Attribute.bold, - controller: controller, - options: const QuillToolbarToggleStyleButtonOptions( - iconData: Icons.format_bold, - iconSize: 20, - ), - ), - QuillToolbarToggleStyleButton( - attribute: Attribute.italic, - controller: controller, - options: const QuillToolbarToggleStyleButtonOptions( - iconData: Icons.format_italic, - iconSize: 20, - ), - ), - QuillToolbarToggleStyleButton( - attribute: Attribute.underline, - controller: controller, - options: const QuillToolbarToggleStyleButtonOptions( - iconData: Icons.format_underline, - iconSize: 20, - ), - ), - QuillToolbarClearFormatButton( - controller: controller, - options: const QuillToolbarClearFormatButtonOptions( - iconData: Icons.format_clear, - iconSize: 20, - ), - ), - VerticalDivider( - indent: 12, - endIndent: 12, - color: Colors.grey.shade400, - ), - QuillToolbarSelectHeaderStyleButtons( - controller: controller, - options: - const QuillToolbarSelectHeaderStyleButtonsOptions( - iconSize: 20, - ), - ), - QuillToolbarToggleStyleButton( - attribute: Attribute.ol, - controller: controller, - options: const QuillToolbarToggleStyleButtonOptions( - iconData: Icons.format_list_numbered, - iconSize: 20, - ), - ), - QuillToolbarToggleStyleButton( - attribute: Attribute.ul, - controller: controller, - options: const QuillToolbarToggleStyleButtonOptions( - iconData: Icons.format_list_bulleted, - iconSize: 20, - ), - ), - QuillToolbarToggleStyleButton( - attribute: Attribute.blockQuote, - controller: controller, - options: const QuillToolbarToggleStyleButtonOptions( - iconData: Icons.format_quote, - iconSize: 20, - ), - ), - VerticalDivider( - indent: 12, - endIndent: 12, - color: Colors.grey.shade400, - ), - QuillToolbarIndentButton( - controller: controller, - isIncrease: true, - options: const QuillToolbarIndentButtonOptions( - iconData: Icons.format_indent_increase, - iconSize: 20, - )), - QuillToolbarIndentButton( - controller: controller, - isIncrease: false, - options: const QuillToolbarIndentButtonOptions( - iconData: Icons.format_indent_decrease, - iconSize: 20, - ), - ), - ]; - }, - ), - ), - ), - Expanded( - child: QuillEditor.basic( - configurations: const QuillEditorConfigurations( - readOnly: false, - placeholder: 'Write your notes', - padding: EdgeInsets.all(16), - ), - ), - ) - ], - ), -) -``` -if you want more customized toolbar feel free to create your own and use the `controller` to interact with the editor. checkout the `QuillToolbar` and the buttons inside it to see an example of how that will works +Open this [page](./doc/custom_toolbar.md) for more info ### Translation @@ -590,22 +333,14 @@ The package offers translations for the quill toolbar and editor, it will follow Currently, translations are available for these 31 locales: -* `Locale('en')`, `Locale('ar')` -* `Locale('bn')`, `Locale('bs')` -* `Locale('cs')`, `Locale('de')` -* `Locale('da')`, `Locale('fr')` -* `Locale('he')`, `Locale('zh', 'cn')` -* `Locale('zh', 'hk')`, `Locale('ko')` -* `Locale('ru')`, `Locale('es')` -* `Locale('tk')`, `Locale('tr')` -* `Locale('uk')`, `Locale('ur')` -* `Locale('pt')`, `Locale('pl')` -* `Locale('vi')`, `Locale('id')` -* `Locale('it')`, `Locale('ms')` -* `Locale('nl')`, `Locale('no')` -* `Locale('fa')`, `Locale('hi')` -* `Locale('sr')`, `Locale('sw')` -* `Locale('ja')` +* `Locale('en')`, `Locale('ar')`, `Locale('bn')`, `Locale('bs')` +* `Locale('cs')`, `Locale('de')`, `Locale('da')`, `Locale('fr')` +* `Locale('he')`, `Locale('zh', 'cn')`, `Locale('zh', 'hk')`, `Locale('ko')` +* `Locale('ru')`, `Locale('es')`, `Locale('tk')`, `Locale('tr')` +* `Locale('uk')`, `Locale('ur')`, `Locale('pt')`, `Locale('pl')` +* `Locale('vi')`, `Locale('id')`, `Locale('it')`, `Locale('ms')` +* `Locale('nl')`, `Locale('no')`, `Locale('fa')`, `Locale('hi')` +* `Locale('sr')`, `Locale('sw')`, `Locale('ja')` #### Contributing to translations diff --git a/doc/custom_embed_blocks.md b/doc/custom_embed_blocks.md new file mode 100644 index 00000000..82a1e44c --- /dev/null +++ b/doc/custom_embed_blocks.md @@ -0,0 +1,124 @@ +# Custom Embed Blocks + +Sometimes you want to add some custom content inside your text, custom widgets inside of them. An example is adding notes to the text, or anything custom that you want to add in your text editor. + +The only thing that you need is to add a `CustomBlockEmbed` and provider a builder for it to the `embedBuilders` parameter, to transform the data inside of the Custom Block into a widget! + +Here is an example: + +Starting with the `CustomBlockEmbed`, here we extend it and add the methods that are useful for the 'Note' widget, that will be the `Document`, used by the `flutter_quill` to render the rich text. + +```dart +class NotesBlockEmbed extends CustomBlockEmbed { + const NotesBlockEmbed(String value) : super(noteType, value); + + static const String noteType = 'notes'; + + static NotesBlockEmbed fromDocument(Document document) => + NotesBlockEmbed(jsonEncode(document.toDelta().toJson())); + + Document get document => Document.fromJson(jsonDecode(data)); +} +``` + +After that, we need to map this "notes" type into a widget. In that case, I used a `ListTile` with a text to show the plain text resume of the note, and the `onTap` function to edit the note. +Don't forget to add this method to the `QuillEditor` after that! + +```dart +class NotesEmbedBuilder extends EmbedBuilder { + NotesEmbedBuilder({required this.addEditNote}); + + Future Function(BuildContext context, {Document? document}) addEditNote; + + @override + String get key => 'notes'; + + @override + Widget build( + BuildContext context, + QuillController controller, + Embed node, + bool readOnly, + bool inline, + TextStyle textStyle, + ) { + final notes = NotesBlockEmbed(node.value.data).document; + + return Material( + color: Colors.transparent, + child: ListTile( + title: Text( + notes.toPlainText().replaceAll('\n', ' '), + maxLines: 3, + overflow: TextOverflow.ellipsis, + ), + leading: const Icon(Icons.notes), + onTap: () => addEditNote(context, document: notes), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10), + side: const BorderSide(color: Colors.grey), + ), + ), + ); + } +} +``` + +And finally, we write the function to add/edit this note. The `showDialog` function shows the QuillEditor to edit the note, after the user ends the edition, we check if the document has something, and if it has, we add or edit the `NotesBlockEmbed` inside of a `BlockEmbed.custom` (this is a little detail that will not work if you don't pass the `CustomBlockEmbed` inside of a `BlockEmbed.custom`). + +```dart +Future _addEditNote(BuildContext context, {Document? document}) async { + final isEditing = document != null; + final quillEditorController = QuillController( + document: document ?? Document(), + selection: const TextSelection.collapsed(offset: 0), + ); + + await showDialog( + context: context, + builder: (context) => AlertDialog( + titlePadding: const EdgeInsets.only(left: 16, top: 8), + title: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text('${isEditing ? 'Edit' : 'Add'} note'), + IconButton( + onPressed: () => Navigator.of(context).pop(), + icon: const Icon(Icons.close), + ) + ], + ), + content: QuillEditor.basic( + controller: quillEditorController, + readOnly: false, + ), + ), + ); + + if (quillEditorController.document.isEmpty()) return; + + final block = BlockEmbed.custom( + NotesBlockEmbed.fromDocument(quillEditorController.document), + ); + final controller = _controller!; + final index = controller.selection.baseOffset; + final length = controller.selection.extentOffset - index; + + if (isEditing) { + final offset = getEmbedNode(controller, controller.selection.start).offset; + controller.replaceText( + offset, 1, block, TextSelection.collapsed(offset: offset)); + } else { + controller.replaceText(index, length, block, null); + } +} +``` + +And voila, we have a custom widget inside of the rich text editor! + +

+ 1 +

+ +> 1. For more info and a video example, see the [PR of this feature](https://github.com/singerdmx/flutter-quill/pull/877) +> 2. For more details, check out [this YouTube video](https://youtu.be/pI5p5j7cfHc) \ No newline at end of file diff --git a/doc/custom_toolbar.md b/doc/custom_toolbar.md new file mode 100644 index 00000000..06c8bb72 --- /dev/null +++ b/doc/custom_toolbar.md @@ -0,0 +1,143 @@ +# Custom Toolbar + +If you want to use custom toolbar but still want the support of this libray +You can use the `QuillBaseToolbar` which is the base for the `QuillToolbar` + +> If you are using the toolbar buttons like `QuillToolbarHistoryButton`, `QuillToolbarToggleStyleButton` in the somewhere like the the custom toolbar then you must provide them with `QuillToolbarProvider` inherited widget, you don't have to do this if you are using the `QuillToolbar` since it will be done for you + +Example: + +```dart +QuillProvider( + configurations: QuillConfigurations( + controller: _controller, + sharedConfigurations: const QuillSharedConfigurations(), + ), + child: Column( + children: [ + QuillToolbarProvider( + toolbarConfigurations: const QuillToolbarConfigurations(), + child: QuillBaseToolbar( + configurations: QuillBaseToolbarConfigurations( + toolbarSize: 15 * 2, + multiRowsDisplay: false, + childrenBuilder: (context) { + final controller = context.requireQuillController; + return [ + QuillToolbarHistoryButton( + controller: controller, + options: const QuillToolbarHistoryButtonOptions( + isUndo: true), + ), + QuillToolbarHistoryButton( + controller: controller, + options: const QuillToolbarHistoryButtonOptions( + isUndo: false), + ), + QuillToolbarToggleStyleButton( + attribute: Attribute.bold, + controller: controller, + options: const QuillToolbarToggleStyleButtonOptions( + iconData: Icons.format_bold, + iconSize: 20, + ), + ), + QuillToolbarToggleStyleButton( + attribute: Attribute.italic, + controller: controller, + options: const QuillToolbarToggleStyleButtonOptions( + iconData: Icons.format_italic, + iconSize: 20, + ), + ), + QuillToolbarToggleStyleButton( + attribute: Attribute.underline, + controller: controller, + options: const QuillToolbarToggleStyleButtonOptions( + iconData: Icons.format_underline, + iconSize: 20, + ), + ), + QuillToolbarClearFormatButton( + controller: controller, + options: const QuillToolbarClearFormatButtonOptions( + iconData: Icons.format_clear, + iconSize: 20, + ), + ), + VerticalDivider( + indent: 12, + endIndent: 12, + color: Colors.grey.shade400, + ), + QuillToolbarSelectHeaderStyleButtons( + controller: controller, + options: + const QuillToolbarSelectHeaderStyleButtonsOptions( + iconSize: 20, + ), + ), + QuillToolbarToggleStyleButton( + attribute: Attribute.ol, + controller: controller, + options: const QuillToolbarToggleStyleButtonOptions( + iconData: Icons.format_list_numbered, + iconSize: 20, + ), + ), + QuillToolbarToggleStyleButton( + attribute: Attribute.ul, + controller: controller, + options: const QuillToolbarToggleStyleButtonOptions( + iconData: Icons.format_list_bulleted, + iconSize: 20, + ), + ), + QuillToolbarToggleStyleButton( + attribute: Attribute.blockQuote, + controller: controller, + options: const QuillToolbarToggleStyleButtonOptions( + iconData: Icons.format_quote, + iconSize: 20, + ), + ), + VerticalDivider( + indent: 12, + endIndent: 12, + color: Colors.grey.shade400, + ), + QuillToolbarIndentButton( + controller: controller, + isIncrease: true, + options: const QuillToolbarIndentButtonOptions( + iconData: Icons.format_indent_increase, + iconSize: 20, + )), + QuillToolbarIndentButton( + controller: controller, + isIncrease: false, + options: const QuillToolbarIndentButtonOptions( + iconData: Icons.format_indent_decrease, + iconSize: 20, + ), + ), + ]; + }, + ), + ), + ), + Expanded( + child: QuillEditor.basic( + configurations: const QuillEditorConfigurations( + readOnly: false, + placeholder: 'Write your notes', + padding: EdgeInsets.all(16), + ), + ), + ) + ], + ), +) +``` + +if you want more customized toolbar feel free to create your own and use the `controller` to interact with the editor. checkout the `QuillToolbar` and the buttons inside it to see an example of how that will works \ No newline at end of file