Appflowy: Feature of the week

Table of contents

Built for people who value privacy
AppFlowy's Rich Text Editor for Flutter is a groundbreaking innovation that promises not only a flawless experience but a truly mind-blowing journey for developers. In a world where precision and personalization are paramount, AppFlowy has redefined the boundaries of what's possible in software development.
Let's quickly walk through what the text editor is about
Discover the power of AppFlowy's Rich Text Editor with the ability to:
Create advanced and user-friendly editors.
Customize various features specific to your needs, including:
Block components (e.g., form input controls, numbered lists, and rich text widgets)
Keyboard shortcuts
Themes
Selection menu
Toolbar menu
Today we are introducing the Customizing Editor Features. A user experience customization feature consisting of
Customizing a shortcut event
Customizing theme
Introducing you your inner Superhero
Customizing a Shortcut Event: In the world of text editing, efficiency is key. With the ability to customize shortcut events, users can streamline their workflow, ensuring that their most frequently used actions are just a keypress away. This feature empowers users to set up their keyboard shortcuts, making the editing process faster and more intuitive.
Tailored Themes: The visual appeal and readability of your text editor can significantly impact your work experience. AppFlowy's Rich Text Editor takes personalization to the next level with the option to customize themes. Whether you prefer a light or dark mode the tailored themes feature allows you to create an editing environment that suits your style and enhances your productivity.
Short-cut event
Light theme
Dark theme
How Customizing short-cut event works
First, make sure you have the Appflowy package installed within the pubspec yaml of your project
flutter pub add appflowy_editor
flutter pub get
Just like how you make use of short-cut events to automate activities on some of the daily software you use, making life easier. Appflowy makes it much more interesting. From setting a special character as a specific shortcut function to making use of them anywhere within the text editor.
Let's make use of the underscore character (_) adding it to the start and end of the text changing the text style to 'italics'
- First, we create a blank document, this allows us to write text on our editor
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
class UnderScoreToItalic extends StatelessWidget {
const UnderScoreToItalic({super.key});
@override
Widget build(BuildContext context) {
return AppFlowyEditor.custom(
editorState: EditorState.blank(withInitialText: true),
blockComponentBuilders: standardBlockComponentBuilderMap,
characterShortcutEvents: const [],
);
}
}
We move on to set a specific character that will represent our short-cut event, changing our text to italics when added at the start and end of the text.
We are making use of an underscore (_)
import 'package:appflowy_editor/appflowy_editor.dart';
import 'package:flutter/material.dart';
class UnderScoreToItalic extends StatelessWidget {
const UnderScoreToItalic({super.key});
@override
Widget build(BuildContext context) {
return AppFlowyEditor.custom(
editorState: EditorState.blank(withInitialText: true),
blockComponentBuilders: standardBlockComponentBuilderMap,
characterShortcutEvents: [
underScoreToItalicEvent,
],
);
}
}
CharacterShortcutEvent underScoreToItalicEvent = CharacterShortcutEvent(
key: 'Underscore to italic',
character: '_',
handler: (editorState) async => handleFormatByWrappingWithSingleCharacter(
editorState: editorState,
character: '_',
formatStyle: FormatStyleByWrappingWithSingleChar.italic,
),
);
There you have it!
You can decide to go ahead with any character you wish to. There are no ends to the text editor.
How do we customize our Editor's theme
Pretty simple! First, we will customize out style by customizing the EditorStyle
and the block style with BlockComponentConfiguration
within your code
EditorStyle customizeEditorStyle() {
return EditorStyle(
padding: PlatformExtension.isDesktopOrWeb
? const EdgeInsets.only(left: 100, right: 100, top: 20)
: const EdgeInsets.symmetric(horizontal: 20),
cursorColor: Colors.green,
selectionColor: Colors.green,
textStyleConfiguration: TextStyleConfiguration(
text: const TextStyle(
fontSize: 18.0,
color: Colors.white54,
),
bold: const TextStyle(
fontWeight: FontWeight.w900,
),
href: TextStyle(
color: Colors.amber,
decoration: TextDecoration.combine(
[
TextDecoration.overline,
TextDecoration.underline,
],
),
),
code: const TextStyle(
fontSize: 14.0,
fontStyle: FontStyle.italic,
color: Colors.blue,
backgroundColor: Colors.black12,
),
),
textSpanDecorator: (context, node, index, text, textSpan) {
final attributes = text.attributes;
final href = attributes?[AppFlowyRichTextKeys.href];
if (href != null) {
return TextSpan(
text: text.text,
style: textSpan.style,
recognizer: TapGestureRecognizer()
..onTap = () {
debugPrint('onTap: $href');
},
);
}
return textSpan;
},
);
}
Map<String, BlockComponentBuilder> customBuilder() {
final configuration = BlockComponentConfiguration(
padding: (node) {
if (HeadingBlockKeys.type == node.type) {
return const EdgeInsets.symmetric(vertical: 30);
}
return const EdgeInsets.symmetric(vertical: 10);
},
textStyle: (node) {
if (HeadingBlockKeys.type == node.type) {
return const TextStyle(color: Colors.yellow);
}
return const TextStyle();
},
);
// customize heading block style
return {
...standardBlockComponentBuilderMap,
// heading block
HeadingBlockKeys.type: HeadingBlockComponentBuilder(
configuration: configuration,
),
// todo-list block
TodoListBlockKeys.type: TodoListBlockComponentBuilder(
configuration: configuration,
iconBuilder: (context, node) {
final checked = node.attributes[TodoListBlockKeys.checked] as bool;
return Icon(
checked ? Icons.check_box : Icons.check_box_outline_blank,
size: 20,
color: Colors.white,
);
},
),
// bulleted list block
BulletedListBlockKeys.type: BulletedListBlockComponentBuilder(
configuration: configuration,
iconBuilder: (context, node) {
return const Icon(
Icons.circle,
size: 20,
color: Colors.green,
);
},
),
// quote block
QuoteBlockKeys.type: QuoteBlockComponentBuilder(
configuration: configuration,
iconBuilder: (context, node) {
return const EditorSvg(
width: 20,
height: 20,
padding: EdgeInsets.only(right: 5.0),
name: 'quote',
color: Colors.pink,
);
},
),
};
}
This will not make any difference on the editor till we inject it into the AppFlowyEditor
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.topCenter,
child: AppFlowyEditor(
editorState: EditorState.blank(),
editorStyle: customizeEditorStyle(),
blockComponentBuilders: customBuilder(),
),
),
);
}
Get ready for a much more flexible and amazing way of customizing the rich text editor, tailoring it to deliver projects as you like it!
Subscribe to my newsletter
Read articles from Oluwaseyi Ogunjinmi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Oluwaseyi Ogunjinmi
Oluwaseyi Ogunjinmi
A skilled software engineer with expertise in Golang, Kubernetes, and backend systems , autonomous machine development. Focused on building scalable solutions and enhancing deployment efficiency. A solid interest in biology and ecosystems. A skilled technical writer.