We will use a simple example to illustrate how to quickly add a shortcut event.
In this example, text that starts and ends with an underscore ( _ ) character will be rendered in italics for emphasis. So typing _xxx_ will automatically be converted into xxx.
Let's start with a blank document:
import 'package:novident_editor/novident_editor.dart';
import 'package:flutter/material.dart';
class UnderScoreToItalic extends StatelessWidget {
const UnderScoreToItalic({super.key});
@override
Widget build(BuildContext context) {
return NovidentEditor.custom(
editorState: EditorState.blank(withInitialText: true),
blockComponentBuilders: standardBlockComponentBuilderMap,
characterShortcutEvents: const [],
);
}
}At this point, nothing magic will happen after typing _xxx_.
To implement our shortcut event we will create a CharacterShortcutEvent instance to handle an underscore input.
We need to define key and character in a CharacterShortcutEvent object to customize hotkeys. We recommend using the description of your event as a key. For example, if the underscore _ is defined to make text italic, the key can be 'Underscore to italic'.
import 'package:novident_editor/novident_editor.dart';
import 'package:flutter/material.dart';
// ...
CharacterShortcutEvent underscoreToItalicEvent = CharacterShortcutEvent(
key: 'Underscore to italic',
character: '_',
handler: (editorState) async => handleFormatByWrappingWithSingleCharacter(
editorState: editorState,
character: '_',
formatStyle: FormatStyleByWrappingWithSingleChar.italic,
),
);Now our 'underscore handler' function is done and the only task left is to inject it into the NovidentEditor.
import 'package:novident_editor/novident_editor.dart';
import 'package:flutter/material.dart';
class UnderScoreToItalic extends StatelessWidget {
const UnderScoreToItalic({super.key});
@override
Widget build(BuildContext context) {
return NovidentEditor.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,
),
);Check out the complete code file of this example.
We will use a simple example to illustrate how to quickly customize a theme.
Let's start with a blank document:
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.topCenter,
child: NovidentEditor(
editorState: EditorState.blank(),
),
),
);
}At this point, the editor looks like ...

Next, we will customize the EditorStyle and the block style with BlockComponentConfiguration.
Warning
Most of these elements could be removed in future releases.
At this point, we recommend use NovidentStyleDefinition, to customize block design, and character configs.
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?[NovidentRichTextKeys.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,
);
},
),
};
}Now our 'customize style' function is done and the only task left is to inject it into the NovidentEditor.
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
alignment: Alignment.topCenter,
child: NovidentEditor(
editorState: EditorState.blank(),
editorStyle: customizeEditorStyle(),
blockComponentBuilders: customBuilder(),
),
),
);
}The text direction is LTR by default, but you can change it by wrapping NovidentEditor with Directionality.
In code snippet below we set the default direction to RTL.
@override
Widget build(BuildContext context) {
return Scaffold(
body: Directionality(
textDirection: TextDirection.rtl,
child: NovidentEditor(
editorState: EditorState.blank(),
),
),
);
}

